代理
后期会使用会在启动类中@MapperScan(“路径”)注解进行代替
数据库事务说明
案例分析:
- userMapper.insert(User对象)
- deptMapper.insert(dept对象)
说明: 由于业务需求 要求方法要么同时入库,要么同时回滚.所以必须通过事务进行控制.
Spring实现事务控制(demo)
代码结构如下
*事物的控制应该写在service层;
Spring中规定:
* 如果传入的是接口的类型 则自动查找/注入 该接口的实现类, 条件必须:该接口只有一个实现类
public class TestUser {
/**
* Controller-Service-Mapper(Dao)
* Spring中规定:
* 如果传入的是接口的类型 则自动查找/注入 该接口的实现类
* 该接口只有一个实现类
* 注入接口的原理:
* if(getBean(isinterface)){
* Class targetClass = interface.getImpl();
* 根据类型,动态获取对象
* return 对象
* }
*/
@Test
public void testTx(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
// 向上造型
//通过类型进行注入
UserService userService = context.getBean(UserService.class);
//通过名字进行注入加入的是一个实现类,切首字母小写;
//UserService userService = (UserService) context.getBean("userServiceImpl");
User user = new User();
user.setId(101);
user.setName("SpringAOP测试入门案例");
userService.addUser(user);
}
将数据库的事物控制写在service层但是如果事物都写在service那么就会出现代码冗余,所以使用代理模式;
代理模式
代理:用户表面上使用的是真实的对象实际上使用的是代理对象,在项目中的服务器中
作用:在不该变原有的方法的前提下对方法进行拓展
组成部分
1.要求代理者实现与被代理者相同的接口
2.在代理方法中实现功能的扩展
3.用户调用代理对象完成功能(用户认为代理就是目标对象)
具体的代码的实现
pom.xml的代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_demo_7_tx</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--Spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入SpringBean-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入表达式jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入日志依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
配置类的代码
pojo的代码
package com.jt.pojo;
public class User {
private Integer id;
private String name;
/*业务中实体对象不会交给Spring容器管理 所以需要添加方法*/
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
usermapper的接口
package com.jt.mapper;
import com.jt.pojo.User;
public interface UserMapper {
void addUser(User user);
void deleteUser(User user);
}
usermapper的接口实现类
package com.jt.mapper;
import com.jt.pojo.User;
import org.springframework.stereotype.Repository;
@Repository
public class UserMapperImpl implements UserMapper{
//??事务控制应该在那一层完成!!!! dao/mapper service
@Override
public void addUser(User user) {
System.out.println("用户入库:"+user);
}
@Override
public void deleteUser(User user) {
System.out.println("删除用户:"+user);
}
}
service接口
package com.jt.service;
import com.jt.pojo.User;
public interface UserService {
void addUser(User user);
void deleteUser(User user);
}
service接口实现类
package com.jt.service;
import com.jt.mapper.UserMapper;
import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("target")
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
//事务控制应该放到Service层中进行控制
@Override
public void addUser(User user) {
userMapper.addUser(user);
}
@Override
public void deleteUser(User user) {
userMapper.deleteUser(user);
}
/*//事务控制应该放到Service层中进行控制
@Override
public void addUser(User user) {
try {
System.out.println("Spring事务开始");
userMapper.addUser(user);
System.out.println("事务结束");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
}*/
}
静态代理(只能实现一个接口)
通过代理实现事物的控制
角色划分:
1.目标对象target UserServiceImpl类
2.目标方法 method addUser()方法
3.代理: 实现事务控制.
4.代理对象与目标对象实现相同的接口.
静态代理弊端
1).静态代理只针对于某个接口 不能实现所有接口的代理 实用性较差

2).静态代理中所有的方法,都需要手动的添加事务开始/事务提交代码 代码冗余 不够简洁.

代理相当于一个接口有两个实现类,且这两个实现类还要进行来回调用(此时通过名字进行依赖注入)但是代理对象和真实的实现类不再一个包里;
代理类的名字为其名为userService
将真实的实现类起名字为target
代理类的代码
package com.jt.proxy;
import com.jt.pojo.User;
import com.jt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService")
public class StaticProxy implements UserService {
//要求引入目标对象
@Autowired //ByType byName
//@Qualifier("target")
private UserService target;
//目的: 对原有方法进行扩展
@Override
public void addUser(User user) {
try {
System.out.println("事务开始");
//调用真实的目标方法
target.addUser(user);
System.out.println("事务结束");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
}
@Override
public void deleteUser(User user) {
try {
System.out.println("事务开始");
//调用真实的目标方法
target.deleteUser(user);
System.out.println("事务结束");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
}
/* public void updateUser(User user) {
try {
System.out.println("事务开始");
//调用真实的目标方法
target.updateUser(user);
System.out.println("事务结束");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
}*/
}
测试类的代码
package com.jt.test;
import com.jt.config.SpringConfig;
import com.jt.pojo.User;
import com.jt.proxy.JDKProxyFactory;
import com.jt.service.UserService;
import com.jt.service.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestUser {
@Test
public void testStaticProxy(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = (UserService) context.getBean("userService");
User user = new User();
user.setId(10001);
user.setName("测试代理机制");
//执行用户调用
userService.addUser(user);
}
}
动态代理
1.JDK代理:
要求: 要求目标对象必须实现接口
代理要求: 代理对象也必须实现目标对象的接口
目标对象/代理关系: 目标对象与代理对象(proxy)兄弟关系.
2.CGlib代理
要求: 不管目标对象是否有接口,都可以为其创建代理对象
代理要求: 要求代理对象必须继承目标对象
目标对象/代理关系: 目标对象与代理对象是父子关系
如果new 一个借口就要重写里面的方法
创建一个代理类
java api 的一个方法Proxy.newProxyInstance()
测试jdk动态代理
创建一个代理类
package com.jt.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//能否利用一个工厂动态为目标对象创建代理
public class JDKProxyFactory {
//要求用户传递目标对象
//关于匿名内部类用法说明jdk版本较低的时候需要加final: 匿名内部类引用外部参数 要求参数必须final修饰
//object说明所有的对象都可以进行使用该代理
public static Object getProxy(final Object target){
//1.调用java API实现动态代理
/**
* 参数分析: 3个参数
* 1.ClassLoader loader, 类加载器(获取目标对象的Class)
* 2.类<?>[] interfaces, JDK代理要求 必须有接口
* java中可以多实现
* 3.InvocationHandler h 对目标方法进行扩展
*/
//1.获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//2.获取接口数组
Class[] interfaces = target.getClass().getInterfaces();
//3.通过动态代理创建对象
//
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
//invoke方法: 代理对象调用方法时,invoke开始执行,扩展方法的编辑位置
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy: 代理对象本身
//method: 用户调用的方法对象
//args: 用户调用方法的参数
// result 标识目标方法执行的返回值
Object result = null;
try {
//添加事务的控制
System.out.println("事务开始");
//执行目标方法
// target真实的目标对象,method方法对象,args方法参数,invoke动态的执行目标方法;
result = method.invoke(target,args);
System.out.println("事务提交");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
return result;
}
});
return proxy;
}
}
创建测试类
package com.jt.test;
import com.jt.config.SpringConfig;
import com.jt.pojo.User;
import com.jt.proxy.JDKProxyFactory;
import com.jt.service.UserService;
import com.jt.service.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestUser {
/**
* 测试JDK动态代理
*/
@Test
public void testJDKProxy(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//1.获取用户目标对象
UserService target = (UserService) context.getBean("target");
//拿到的是目标对象实际上是代理对象;
//2.获取代理对象
UserService userService = (UserService) JDKProxyFactory.getProxy(target);
//3.打印代理对象的类型
System.out.println(userService.getClass());
//4.用户完成调用
User user = new User();
user.setId(1001);
user.setName("JDK动态代理完成");
//新增用户方法
userService.addUser(user);
//动态代理对方法进行优化
//删除用户的方法
userService.deleteUser(user);
}
}
JDK动态代理流程

动态代理优势
(代理的内容代码就是公共的代码,即无论写几个service代码代理的代码只需要写一个即可)
将公共的部分写到动态代理中,之后其他的业务类调用即可
编辑DeptService/DeptServiceImpl
1).编辑DeptService

2).编辑DeptServiceImpl
编辑测试类
public class TestDept {
@Test
public void testTx(){
//1.获取目标对象
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
DeptService target = (DeptService) context.getBean("deptService");
//2.获取代理对象
DeptService deptService = (DeptService) JDKProxyFactory.getProxy(target);
//通过代理对象 调用方法 扩展了方法!!!!!
deptService.addDept(); //invoke
}
}
动态代理实现方案(二)
业务需求
要求对Service层的方法记录其执行的时间!!! 通过执行时间的长短 进行针对性的优化!!!
要求: service中 有 addUser方法/deleteUser方法.
要求代码结构扩展性好,耦合性低.
做完1000块!!!
编辑UserService/UserServiceImpl
1).编辑UserService
public interface UserService {
void addUser(User user);
void deleteUser(User user);
}
.编辑UserServiceImpl
@Service("target")
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("新增用户");
}
@Override
public void deleteUser() {
System.out.println("删除用户");
}
}
代理工厂
package com.jt.proxy;
import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyFactory {
//编辑静态方法获取代理对象
public static Object getProxy(final Object target){
//3个参数 1.类加载器 2.对象的接口
ClassLoader classLoader = target.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
Object proxy = Proxy.newProxyInstance(classLoader, interfaces,
new InvocationHandler() {
//代理对象执行目标方法时执行
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//让用户执行目标方法
Long startTime = System.currentTimeMillis(); //开始时间
//执行目标方法 获取返回值 可能为null
Object result = method.invoke(target);
Long endTime = System.currentTimeMillis(); //结束时间
//根据项目经理要求 给程序预留bug 后期维护时删除 不友好
Thread.sleep(2000);
System.out.println("程序执行:"+(endTime-startTime)+"毫秒");
//将返回值传递给调用者
return result;
}
}) ;
return proxy;
}
}
编辑测试案例
public class TestSpring {
@Test
public void test01(){
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
//1.获取目标对象
UserService target = (UserService) context.getBean("target");
//2.获取代理对象
UserService proxy = (UserService) JDKProxyFactory.getProxy(target);
System.out.println(proxy.getClass());
//3.调用业务方法
proxy.addUser();
}
}
AOP
底层是通过动态代理进行实现的即底层是动态代理;
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总结: AOP(面向切面编程) 主要利用动态代理的模式 降低程序的耦合度,扩展业务功能方法.
什么样的方法需要放入到aop中
答:耦合性较高且又必须需要修改的方法放入到切面中来;
作用
在方法执行前后加功能,即对功能的方法都拓展。
组成
1.2 关于AOP名词介绍
1).连接点(Joinpoint): 用户可以被扩展的方法,但是现在还没有被扩展;

2).切入点PointCut: 用户实际扩展的方法这里的adduser就由原来 的连接点变成了切入点,找到指定包里的类,类里的方法,增加功能;
3).通知Advice: 扩展方法的具体实现,即就是要实现什么样的功能;
4).切面Aspect: 将通知应用到切入点的过程,其实就是一个类,由通知和切点组成;
1.3 通知类型(必会)
before: 在目标方法执行之前执行
afterReturning: 在目标方法执行之后返回时执行
afterThrowing: 在目标方法执行之后,抛出异常时执行
after: 无论程序是否执行成功,都要最后执行的通知(相当于关闭该方法 的资源)
around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式),功能最为强大 只有环绕通知可以控制目标方法的执行
通知类型的注解要加下面的包
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.ProceedingJoinPoint;
关于通知方法总结:
1.环绕通知是处理业务的首选. 可以修改程序的执行轨迹
2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录
切入点表达式
概念:当程序满足切入点表达式,才能进入切面,执行通知方法.
1.bean(“bean的ID”) 根据beanId进行拦截 只能匹配一个
2.within(“包名.类名”) 可以使用通配符( * 或 ? ) 能匹配多个.
粒度: 上述的切入点表达式 粒度是类级别的. 粗粒度. 最细粒度的是方法的参数
3.execution(返回值类型 包名.类名.方法名(参数列表…));
粒度: 控制的是方法参数级别. 所以粒度较细. 最常用的.
4.@annotation(包名.注解名) 只拦截注解.
粒度: 注解是一种标记 根据规则标识某个方法/属性/类 细粒度
通知Advice:就是类里的一个方法,分为前置通知(方法执行前要加的),后置通知(方法执行后要加的),环绕通知(方法执行前后都要加的),返回后通知,异常通知
AOP入门案例
导入jar包
需求
统计所有service层的方法的执行时间
<!--引入AOP包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<!--切面包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
pom.xml文件的全部代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_demo_9_aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--Spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入SpringBean-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入表达式jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入日志依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--引入AOP包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<!--切面包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<!--当下所有的jar包都需要自动手动的依赖 并且需要确定依赖的关系,对初学者不友好-->
</dependencies>
</project>
配置切面类
切面 = 切入点表达式 + 通知方法
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {
//1.定义before通知
@Before("bean(deptServiceImpl)")
public void before(){
System.out.println("我是before通知");
}
}
编辑配置类
@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象
//默认启用JDK动态代理,
//目标对象没有实现接口时,则采用CGLIB
//强制使用cglib proxyTargetClass=true
//JDK代理创建速度快.运行时稍慢
//CGLIB创建时速度较慢,运行时更快
public class SpringConfig {
}
编辑测试代码

关于表达式写法
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {
/**
* 切入点表达式练习
* within:
* 1.within(com.jt.*.DeptServiceImpl) 一级包下的类
* 2.within(com.jt..*.DeptServiceImpl) ..代表多级包下的类
* 3.within(com.jt..*) 包下的所有的类
*
* execution(返回值类型 包名.类名.方法名(参数列表))
* 1.execution(* com.jt..*.DeptServiceImpl.add*())
* 注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
* 的add开头的方法 ,并且没有参数.
*
* 2.execution(* com.jt..*.*(..))
* 注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.
*
* 3.execution(int com.jt..*.*(int))
* 4.execution(Integer com.jt..*.*(Integer))
* 强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
*
* @annotation(包名.注解名)
* @Before("@annotation(com.jt.anno.Cache)")
* 只拦截特定注解的内容.
*/
//1.定义before通知
//@Before("bean(deptServiceImpl)")
//@Before("within(com.jt..*)")
//@Before("execution(* com.jt..*.DeptServiceImpl.add*())")
@Before("@annotation(com.jt.anno.Cache)")
public void before(){
System.out.println("我是before通知");
}
}
关于通知方法测试
通知方法

配置类
package com.jt.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy(proxyTargetClass=false) //只有加了此代码aop切面才会有效
//启动AOP的注解 创建代理对象
//默认启用JDK动态代理,
//目标对象没有实现接口时,则采用CGLIB
//强制使用cglib代理 proxyTargetClass=true
//JDK代理创建速度快.运行时稍慢
//CGLIB创建时速度较慢,运行时更快
public class SpringConfig {
}
service的代码
package com.jt.service;
public interface DeptService {
void addDept();
void updateDept();
//AOP中的测试方法
String after(Integer id);
//测试异常通知
void afterThrow();
//测试环绕通知执行
void doAround();
//测试执行的顺序
void doOrder();
}
service 接口的实现类的代码
package com.jt.service;
import com.jt.anno.Cache;
import org.springframework.stereotype.Service;
@Service
public class DeptServiceImpl implements DeptService{
@Override
public void addDept() {
System.out.println("添加部门信息");
}
@Override
@Cache //被注解标识
public void updateDept() {
System.out.println("更新部门");
}
@Override
@Cache //标识该方法需要执行切面
public String after(Integer id) {
return "Spring通知的测试";
}
//让该方法执行时 抛出异常
@Override
@Cache
public void afterThrow() {
System.out.println("用户执行目标方法");
//手动抛出算数异常
int a = 1/0;
}
@Override
@Cache //标识执行AOP中的方法
public void doAround() {
System.out.println("实现用户数据的入库操作");
}
@Override
@Cache
public void doOrder() {
System.out.println("测试程序执行的顺序");
}
}
创建切面1
package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
//@Aspect
public class SpringAOP {
//面向切面编程 = 切入点表达式(IF判断) + 通知方法
/**
* 切入点表达式练习
* within:
* 1.within(com.jt.*.DeptServiceImpl) 一级包下的类
* 2.within(com.jt..*.DeptServiceImpl) ..代表多级包下的类
* 3.within(com.jt..*) 包下的所有的类
*
* execution(返回值类型 包名.类名.方法名(参数列表))
* 1.execution(* com.jt..*.DeptServiceImpl.add*())
* 注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
* 的add开头的方法 ,并且没有参数.
*
* 2.execution(* com.jt..*.*(..))
* 注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.
*
* 3.execution(int com.jt..*.*(int))
* 4.execution(Integer com.jt..*.*(Integer))
* 是所有包下的所有包下 的所有类的方法只有int的参数
* 强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
*
* @annotation(包名.注解名)
* @Before("@annotation(com.jt.anno.Cache)")
* 只拦截特定注解的内容.
*/
//1.定义before通知
//@Before("bean(deptServiceImpl)")
//@Before("within(com.jt..*)")
//@Before("execution(* com.jt..*.DeptServiceImpl.add*())")
//@Before("@annotation(com.jt.anno.Cache)")
//1.定义切入点表达式 if判断
@Pointcut("@annotation(com.jt.anno.Cache)")
public void pointcut(){
}
/*Spring为了AOP动态获取目标对象及方法中的数据,则通过joinPoint对象
* 进行数据的传递.
* getSignature : 方法签名 获取方法的参数
* */
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("获取目标对象的类型:"+joinPoint.getTarget().getClass());
System.out.println("获取目标对象类名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("获取目标对象方法名:"+joinPoint.getSignature().getName());
System.out.println("获取方法参数:"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("我是before通知");
}
/**
* 记录方法的方法返回值!!!
* pointcut: 关联的切入点表达式
* returning: 将方法的返回值,通过形参result进行传递
* @AfterReturning(pointcut = "pointcut()",returning = "result")
* 注意事项:
* 如果参数中需要添加joinPoint 对象时,参数必须位于第一位.
* Spring在进行参数赋值时,采用index[0] 下标的方式赋值
* 报错提示: ::0xxxx
*/
@AfterReturning(pointcut = "pointcut()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
System.out.println(Arrays.toString(joinPoint.getArgs()));
System.out.println("用户的返回值结果:"+result);
System.out.println("我是afterReturning通知");
}
/*
* throwing = "e" 动态接收程序运行时的报错信息,
* 利用异常通知进行记录
* */
@AfterThrowing(pointcut = "pointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("获取异常信息:"+e.getMessage());
System.out.println("获取异常的类型:"+e.getClass());
System.out.println("我是afterThrowing");
}
@After("pointcut()")
public void after(){
System.out.println("我是after通知");
}
/**
* 关于环绕通知的说明
* 作用: 可以控制目标方法是否执行.
* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
* 注意事项:
* ProceedingJoinPoint 只能适用环绕通知
* @return
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object result = null;
try {
System.out.println("环绕通知开始");
//1.执行下一个通知 2.执行目标方法 3.接收返回值
Long start = System.currentTimeMillis();
result = joinPoint.proceed();
Long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束");
return result;
}
}
关于的切入点表达式@annotation(包名.注解名) 只拦截注解的说明
此注解:在代码没有任何功能,只不过被 @annotation(com.jt.anno.Cache)进行拦截进行标示执行切面表达式的功能;
package com.jt.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解 包括元注解
//注解的作用: 配合AOP进行注解类型 案例的训练 标识
//控制注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
//注解的作用对象 方法有效 类有效 TYPE
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface Cache {
}


关于@Before作用
说明: 前置通知,在目标方法执行之前执行
用途: 如果需要记录程序在方法执行前的状态,则使用前置通知.
需求: 1.获取目标对象的类型
2.获取目标方法的名称
3.获取目标方法的参数
/*Spring为了AOP动态获取目标对象及方法中的数据,则通过joinPoint对象
* 进行数据的传递.
* getSignature : 方法签名 获取方法的参数
* */
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("获取目标对象的类型:"+joinPoint.getTarget().getClass());
System.out.println("获取目标对象类名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("获取目标对象方法名:"+joinPoint.getSignature().getName());
System.out.println("获取方法参数:"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("我是before通知");
}
@AfterReturning
说明: afterReturning,在目标方法执行之之后执行
用途: 用来监控方法的返回值,进行日志的记录
1).在接口中添加测试方法
实现目标方法

编辑AOP方法测试

@AfterReturning(pointcut = "pointcut()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
System.out.println(Arrays.toString(joinPoint.getArgs()));
System.out.println("用户的返回值结果:"+result);
System.out.println("我是afterReturning通知");
}
@AfterThrowing
作用: 当目标方法执行时,抛出异常时 可以使用AfterThrowing 进行记录.
编辑业务接口

编辑业务实现类
编辑异常通知类型
AfterThrowing里的throwing记录的是日志信息;相当于pointcut里的returning

@Around
@Around作用
规则: 在目标方法执行前后都要执行
实际作用: 可以控制目标方法是否执行.
环绕通知学习
ProceedingJoinPoint仅使用于around通知;
proceed()方法底层封装了invoke()的方法,所以执行的功能都是一样的,proceed()是ProceedingJoinPoint里的方法;
/**
* 关于环绕通知的说明
* 作用: 可以控制目标方法是否执行.
* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
* 注意事项:
* ProceedingJoinPoint is only supported for around advice
* @return
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object result = null;
try {
System.out.println("环绕通知开始");
//1.执行下一个通知 2.执行目标方法 3.接收返回值
Long start = System.currentTimeMillis();
result = joinPoint.proceed();
Long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束");
return result;
}
关于Spring 中AOP流程介绍
1.8.1 AOP中的名词解释
1).连接点: 用户可以被扩展的方法 joinPoint
2).切入点: 用户实际扩展的方法(判断方法能否进入切面) pointcut 切入点表达式
3).通知: 扩展方法的具体实现 @before
4).切面: 将通知应用到切入点的过程 方法功能得到扩展全部配置(切面=切入点表达式+通知方法)

关于知识点讲解
pointcut
说明:判断方法能否进入切面 IF判断
注解说明: @Pointcut 内部编辑切入点表达式
判断依据:

joinPoint(连接点)
说明: 当用户执行方法时,如果方法满足pointcut表达式,则该方法就是连接点.
如果通知中需要获取方法相关参数 则Spring会将joinPoint对象进行封装.为通知参数进行赋值

通知方法的执行顺序
1.执行around开始
2.执行before
3.执行目标方法
4.执行afterReturning
5.执行afterThrowing
6.执行after
7,执行around通知结束
关于Order注解说明
说明: 研究如果是多个切面,如何控制切面的执行的顺序.
方法: 通过Order注解实现.
@order() 注解的作用就是当有多个切面时控制切面的 执行的顺序 , ()里 的值越小越先执行;
创建切面A
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(2)
public class Before1 {
@Before("@annotation(com.jt.anno.Cache)")
public void before(){
System.out.println("我是切面A执行");
}
}
创建切面B
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1) //控制程序的执行顺序 通过数字进行控制 值越小 越先执行
public class Before2 {
@Before("@annotation(com.jt.anno.Cache)")
public void before(){
System.out.println("我是切面B执行");
}
}


创建切面 2
切面=切入点表达式+通知方法
package cn.tedu.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//spring的aop功能,其实就是为了增强方法的功能,由切点和通知组成
//aop的使用场景:事务管理,缓存管理,权限管理,日志管理,性能测试
@Component
@Aspect//1. 标记是一个切面
public class AspectTest {
//2. 切点表达式:用来给 指定包,类,方法 加功能
//*代表一个值 ..表示多个值 第1个*表示方法的返回值 第2个*表示类 第2个*表示方法
//@Pointcut("execution(返回值 包名.类名.方法名(参数列表) )")
@Pointcut("execution(* cn.tedu.service..*.*(..) )")
public void point(){}
//3. 通知:本质就是一个方法,增强功能
@Around("point()")//标志这是环绕通知,是在方法执行前后都加了功能
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis() ;
Object o = joinPoint.proceed();//继续执行原来的业务方法
long end = System.currentTimeMillis() ;
System.out.println("aop计算的耗时是:"+(end-start));
return o ; //返回这个结果
}
}
测试

其实只有around是执行功能其他的通知都是在执行日志
1. 缓存控制(机制)
当第一次查询的时候从数据库中进行查询,第二次以后从缓存中进行查询;
业务说明
用户有一个缓存的集合 static Map<k:v> key=id号 value=User对象 根据id将User对象进行了缓存
要求:当用户第二次根据Id查询用户时,如果缓存中有数据,则直接返回!!!
业务分析
1).首选用AOP方式实现
2).通知方法: 使用环绕通知
3).切入点表达式: execution(…)
AOP切面实现
pojo类
package com.jt.pojo;
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
service.的接口的代码
package com.jt.service;
import com.jt.pojo.User;
public interface UserService {
void findUser(User user);
}
service.的接口的实现类的代码
package com.jt.service;
import com.jt.pojo.User;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImplB implements UserService{
@Override
public void findUser(User user) {
System.out.println("查询的结果是"+user);
}
}
配置类
package com.jt.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.jt")
public class SpringConfig {
}
测试类

切面的类
package com.jt.aop;
import com.jt.pojo.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
@Aspect
public class SpringAOP {
private static Map<Integer,User> map = new HashMap();
/**
* 需求: 用户第一次查询走目标方法
* 用户第二次查询走缓存 不执行目标方法
* 判断依据: 如何判断用户是否为第一次查询?
* 通过map集合进行判断 有数据 证明不是第一次查询
* 执行步骤:
* 1.获取用户查询的参数
* 2.判断map集合中是否有该数据.
* true: 从map集合中get之后返回
* false: 执行目标方法,之后将user对象保存到Map中
*/
//切入点表达式: 拦截service包中的所有方法
@Around("execution(* com.jt.service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
//1.获取目标对象的参数
Object[] args = joinPoint.getArgs();
//2.强制类型转化为对象
User user = (User) args[0];
//3.判断map集合中是否有该数据 user的Id是唯一标识
int id = user.getId();
if(map.containsKey(id)){
//map中有数据
System.out.println("AOP缓存执行");
//将获取的数据返回
return map.get(id);
}else{
//map中没有数据 执行目标方法
result = joinPoint.proceed();
//将user对象保存到Map中
map.put(id, user);
System.out.println("AOP执行目标方法");
}
return result;
}
}