关于AOP的知识点集合
概念:AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
面向切面编程:将扩展的方法在切面中定义封装,不需要修改源码,对方法进行扩展,而且切面可能有很多
作用:可对业务逻辑的各个部分进行隔离,降低业务逻辑各部分之间的耦合度,提高程序可重用性,提高开发效率。
总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.
AOP中的专业术语
1.连接点:用户可以被扩展的方法
2.切入点:用户实际扩展的方法(方法满足切入点表达式时,就会执行通知方法)
3.通知:扩展方法具体实现
4.切面:将通知应用到切入点的过程
切入点表达式:
bean(“对象的id”)每次拦截,只拦截一个
@Pointcut("bean(userServiceImpl)")只匹配ID为userServiceImpl
within("包名.类名")
@Pointcut("within(com.jt.demo2.userservice.*)") 匹配xx.xxx.userservice下的所有路径 说明:上述操作,粒度是粗粒度的。按类匹配
execution("返回值类型 包名.类名.方法名(参数列表)")方法参数级别
@Pointcut("execution(* com.jt.demo2.userservice(包名)..*.*(..) )") * * 拦截返回值类型任意 XX.xx.userservice包下所有子孙包的所有类的任意方法
java中有一个可变参数类型 .. * @Pointcut("execution(* com.jt.demo2.userservice..*.add*(..) * 拦截返回值类型任意 xx.xx.service包下所有子孙类的所有类.以add开头的方法
@annotation(注解的路径)
@Pointcut("@annotation(com.jt.demo2.anno.CGB2110)") * 拦截XX包下的CGB2110的注解
定义通知方法:
* 1.前置通知 在目标方法执行之前执行.
@Before("pointcut1()") public void before(JoinPoint joinPoint){ System.out.println("本方法是前置通知"); }
* 2.后置通知 在目标方法执行之后执行.
@AfterReturning("pointcut2()") public void afterReturning(){ System.out.println("本方法是后置通知");
* 3.异常通知 在目标方法执行之后抛出异常时执行.
@AfterThrowing("pointcut3()") public void afterThrowing(){ System.out.println("本方法是异常通知");
* 4.最终通知 都要执行的通知
@After("pointcut4()") public void After(){ System.out.println("本方法是最终通知"); }
以上为记录程序运行状态
* 5.环绕通知 在目标方法执行前后都要执行的通知---控制目标方法是否执行
@Around("") public void Around(){ System.out.println("本方法是环绕通知"); }
SpringAOP当中执行顺序:
AOP书写流程:
准备工作:
第一步:创建 com.jt.aop1
第二步:在aop1包下创建创建三个层,分别是aop,config,service
第三步:aop下创建SpringAop,这是一个切面类
config下创建SpringConfig
service下创建 接口UserService 实现类 UserserviceImpl
创建一个测试类,和三个包同级
代码分层展示
SpringAop
@Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop {
SpringConfig
package com.jt.aop1.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("com.jt.aop1") //让Spring中的AOP生效 @EnableAspectJAutoProxy public class SpringConfig { }
UserService
package com.jt.aop1.service; public interface UserService { void addUser(); void deleteUser(); }
UserserviceImpl
package com.jt.aop1.service; import com.jt.aop1.note.ZHUJIE1; import com.jt.aop1.note.ZHUJIE2; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; @Service//标明是业务层 public class UserServiceImpl implements UserService { @Override public void addUser(){ System.out.println("业务层中的addUser新增用户"); } @Order public void deleteUser(){ System.out.println("业务层中的delete删除用户"); } }
测试方法:
package com.jt.aop1; import com.jt.aop1.config.SpringConfig; import com.jt.aop1.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Spring_AOP { /** * 流程: * 1.先走前两行,扫描里边的配置类,扫描配置类前两个注解,AOP生效 * 2.SpringAop生效后,扫描注解@Aspect 会认为当前类为切面类 * 3.会在SpringAop当中进行一系类切面的操作 * */ public static void main(String[] args) { ApplicationContext context= new AnnotationConfigApplicationContext(SpringConfig.class); // 理论上来说是根据接口获取实现类的对象,但与切入点表达式匹配,获得的是代理对象 // 因为当前类在获取的时候,与aop中的表达式匹配了,所以创建了一个代理对象 // 代理对象创建方式:JDK动态代理,CGLib动态代理 UserService userService = context.getBean(UserService.class); // 实现类对象的方法没有被扩展 // 代理对象方法会被拓展 Aop有效 System.out.println(userService.getClass()); // 调用业务层的方法 userService.addUser(); userService.deleteUser(); userService.updateUser(); userService.saveUser(); } }
开始运行流程:
从测试类开始:
流程: * 1.先走测试类前两行,扫描里边的配置类,扫描配置类前两个注解,AOP生效 * 2.SpringAop生效后,扫描注解@Aspect 会认为当前类为切面类 * 3.会在SpringAop当中进行一系类切面的操作
切面=切入点表达式+通知方法 * * 1.定义一个切面类 * 2.创建一个方法 --表达式+通知方法 * (需要把这个公共的方法设置一个注解,用方法名代替, * 里面的路径设置为bean切入点表达式,标签里调用业务层的类中的方法) * 测试类开始扫描,走完测试类的流程后,开始执行3 * 3.让SpringAop生效,在配置类添加注解 @EnableAspectJAutoProxy * 4.开始扫描@Pointcut,判断bean标签中运行的是否当前对象userServiceImpl * 5.如果满足,会执行切面表达式,执行下面和切入点表达式绑定的通知方法,又因为是前置,先与目标方法执行,然后根据接口,获取实现类对象,
加载流程归纳总结:
springAOP的具体加载步骤:配置文件的路子
1、当spring容器启动的时候,加载了spring的配置文件
2、为配置文件中所有的bean创建对象
3、spring容器在创建对象的时候它会解析aop:config的配置
解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,则为该bean创建正常的对象
其实就是你通过表达式告诉Spring哪些bean需要它帮你生成代理对象而不是生成原有的正常对象
4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果没有代理对象,则返回目标对象
注意:如果目标类实现了接口,spring容器会采用jdk的动态代理产生代理对象,产生的代理类和目标类实现了相同的接口;
如果目标类没实现接口,spring容器会采用cglib的方式产生代理对象,产生的代理类是目标类的子类
第四步:代码功能实现
建立一个模块
package com.jt.aop1.aop; @Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop { }
1.在切面类代码当中实现bean的切入点表达式+前置通知方法
package com.jt.aop1.aop; @Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop { @Pointcut("bean(userServiceImpl)") public void pointcut1() { } @Before("pointcut1()") public void before() { System.out.println("本方法是前置通知"); }
实现展示:
2.在切面类代码当中实现within的切入点表达式+前置通知方法
package com.jt.aop1.aop; @Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop { // 2.创建一个within切入点表达式的方法 @Pointcut("within(com.jt.aop1.service.*)") public void pointcut2(){ } @AfterReturning("pointcut2()") public void afterReturning(){ System.out.println("本方法是后置通知"); } }
实现展示:
3.在切面类代码当中实现execution的切入点表达式+异常通知方法
package com.jt.aop1.aop; @Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop { // 3.创建一个execution切入点表达式的方法 @Pointcut("execution(* com.jt.aop1.service..*.*(..))") public void pointcut3(){ } @AfterThrowing(value = "pointcut3()",throwing = "exception") public void afterThrowing(Exception exception){ System.out.println("本方法是异常通知"); } }
4.在切面类代码当中实现@annotation的切入点表达式+最终通知方法
package com.jt.aop1.aop; @Component//把当前类交给spring容器管理 @Aspect//表示当前是切面类 public class SpringAop { //4.创建一个@annotation切入点表达式的方法 @Pointcut("@annotation(com.jt.aop1.note.BAOCUN)") public void pointcut4(){ } @After("pointcut4()") public void After(){ System.out.println("本方法是最终通知"); } }
业务层代码:
package com.jt.aop1.service; import com.jt.aop1.note.*; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; @Service//标明是业务层 public class UserServiceImpl implements UserService { @Override @TIANJIA public void addUser(){ System.out.println("业务层中的addUser新增用户"); } @Order @SHANCHU public void deleteUser(){ System.out.println("业务层中的delete删除用户"); } @Override @XIUGAI public void updateUser() { System.out.println("修改用户信息"); } @Override @BAOCUN public void saveUser() { System.out.println("保存用户信息"); } }
注意:注解是起到一个标记作用,所以不是采用的动态代理,而是直接通过注解定位到业务层中的类当中
5.在切面类代码当中实现@annotation的切入点表达式+环绕通知方法
@Order()//实现排序的注解