Spring AOP学习总结(一)

Spring AOP学习总结(一)

Aop是什么

与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。

Aop的应用场景

  • 日志记录
  • 权限验证
  • 效率检查
  • 事务管理
  • exception

springAop的底层技术

项目JDK动态代理CGLIB代理
编译时期的织入还是运行时期的织入?运行时期织入运行时期织入
初始化时期织入还是获取对象时期织入?初始化时期织入初始化时期织入

springAop和AspectJ的关系

Aop是一种概念

springAop、AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的

spring AOP提供两种编程风格
@AspectJ support         ------------>利用aspectj的注解
Schema-based AOP support ----------->xml aop:config 命名空间

证明:spring,通过源 码分析了,我们可以知道spring底层使用的是JDK或者CGLIB来完成的代理,并且在官网上spring给出了aspectj的文档,和springAOP是不同的

spring Aop的概念

  1. aspect: 一定要给spring去管理 抽象 aspectj->类
  2. pointcut: 切点表示连接点的集合 —>
    (理解:切点表达式决定 JoinPoint 的数量)
  3. Joinpoint: 连接点 目标对象中的方法 —> 记录
    (理解:JoinPoint是要关注和增强的方法,也就是我们要作用的点)
  4. Weaving: 把代理逻辑加入到目标对象上的过程叫做织入
  5. target: 目标对象 原始对象
  6. aop Proxy: 代理对象 包含了原始对象的代码和增加后的代码的那个对象
  7. advice: 通知 (位置 + logic)
    advice通知类型
    • Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
    • After 连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
    • After throwing 执行抛出异常的时候
    • After (finally) 无论连接点是正常退出还是异常退出,都会执行
    • Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。

Proceedingjoinpoint 和JoinPoint的区别

Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。

JoinPoint的方法

  • java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
  • Signature getSignature() :获取连接点的方法签名对象;
  • java.lang.Object getTarget() :获取连接点所在的目标对象;
  • java.lang.Object getThis() :获取代理对象本身;
    proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

Introductions
perthis
使用方式如下:
@Aspect(“perthis(this(com.chenss.dao.IndexDaoImpl))”)
要求:

  1. AspectJ对象的注入类型为prototype
  2. 目标对象也必须是prototype的

原因为:只有目标对象是原型模式的,每次getBean得到的对象才是不一样的,由此针对每个对象就会产生新的切面对象,才能产生不同的切面结果。

案例实现

  1. 声明一个Aspect

    申明一个@Aspect注释类,并且定义成一个bean交给Spring管理。

     @Component("appAop")
     @Aspect
     @EnableAspectJAutoProxy
     public class AppAop {
     
     	//切入点表达式
     	@Pointcut("execution(* cn.itcast.service.*.*(..))")
     	//切入点签名
     	public void pointCut() {
     		}
    
     	//声明advice通知
     	@Before("pointCut()")
     	public void advice() {
     		System.out.println("aop ------ before");
     		}	
     }
    

    注:如果不是springboot项目,需要添加@EnableAspectJAutoProxy注解表示自动开启代理

  2. 声明一个接口

     public interface CityService {
     	void query1();
     }
    
  3. 声明一个实现类

     @Component
     public class CityService implements L{
     	
     	@Override
     	public void query1() {
     	System.out.println("---query1----");
     	}
     }
    
  4. 编写测试类

     @RunWith(SpringRunner.class)
     @ContextConfiguration(locations = "classpath:applicationContext.xml")
     public class TestAop {
    
      @Autowired 
      private CityService cityService;
     
     @Test
     public void testAop() {
     	cityService.query1();
     }
    

各种连接点joinPoint的意义

  1. execution
    用于匹配方法执行 join points连接点,最小粒度方法,在aop中主要使用。

     execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
     			
     这里问号表示当前项可以有也可以没有,其中各项的语义如下
     	modifiers-pattern:方法的可见性,如public,protected;
     	ret-type-pattern:方法的返回值类型,如int,void等;
     	declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
     	name-pattern:方法名类型,如buisinessService();
     	param-pattern:方法的参数类型,如java.lang.String;
     	throws-pattern:方法抛出的异常类型,如java.lang.Exception;
    
     **example:**
    
     	//匹配com.chenss.dao包下的任意接口和类的任意方法
     	@Pointcut("execution(* com.chenss.dao.*.*(..))")
     	//匹配com.chenss.dao包下的任意接口和类的public方法
     	@Pointcut("execution(public * com.chenss.dao.*.*(..))")
     	//匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
     	@Pointcut("execution(public * com.chenss.dao.*.*())")
     	//匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
     	@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")
     	//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
     	@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")
     	//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
     	@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")
     	//匹配任意的public方法
     	@Pointcut("execution(public * *(..))")
     	//匹配任意的以te开头的方法
     	@Pointcut("execution(* te*(..))")
     	//匹配com.chenss.dao.IndexDao接口中任意的方法
     	@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")
     	//匹配com.chenss.dao包及其子包中任意的方法
     	@Pointcut("execution(* com.chenss.dao..*.*(..))")
    

    官网参考:
    https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

  2. within
    表达式的最小粒度为类,within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等

     //匹配com.chenss.dao包中的任意方法
     @Pointcut("within(com.chenss.dao.*)")
     //匹配com.chenss.dao包及其子包中的任意方法
     @Pointcut("within(com.chenss.dao..*)")
    
    案例:
     @Pointcut("within(cn.itcast.service.CityService)")			
     public void pointCut1() {
     	System.out.println("aop ------ before");
     }
    
  3. args
    args匹配指定参数类型和指定参数数量的方法,与包名和类名无关
    args同execution不同的地方在于:

     args匹配的是==运行时==传递给方法的参数类型
     *execution(* *(java.io.Serializable))匹配的是方法在==声明时==指定的方法参数类型。
    
    案例:

service

 @Override
public void query2(String s) {
	System.out.println("----query2----");		
}

AppAop

@Pointcut("args(java.lang.String)")		
public void pointCut2() {
}	
@After("pointCut2()")
public void advice2() {
	System.out.println("aop ------ args");
}
打印结果:
----query2----
aop ------ args
  1. @annotation作用方法级别

    案例:

    使用:声明一个自定义注解

     import java.lang.annotation.Retention;
     import java.lang.annotation.RetentionPolicy;
     	
     @Retention(RetentionPolicy.RUNTIME)
     public @interface Zhaojie {
     	
     }
    

    使用注解:

     @Pointcut("@annotation(cn.itcast.inter.Zhaojie)")		
     public void pointCut3() {
     }
     @After("pointCut3()")
     public void advice3() {
     	System.out.println("aop ------ annotation");
     }
    
    案例:
     @Pointcut("args(java.lang.String)")		
     public void pointCut2() {
     }
     //作用于service包下的所有类的所有方法但是方法不能有一个参数
     @Pointcut("execution(* cn.itcast.service.*.*(..)) && !pointCut2() ")		
     public void pointCut4() {
     	
     }
    
  2. this
    JDK代理时,指向接口和代理类proxy,
    cglib代理时 指向接口和子类(不使用proxy)

    案例
     @Pointcut("this(cn.itcast.service.CityService")			
     public void pointCut5() {
     	
     }
     
     AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
     
     CityService  cityService = (CityService )ac.getBean(CityService.class);
    

    注意:如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理

  • JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。eg: cityService extends Proxy implements CityService

  • CGLIB继承被代理的类来实现。eg: cityService extends CityService implements CityService

    所以使用target会保证目标不变,关联对象不会受到这个设置的影响。
    但是使用this对象时,会根据该选项的设置,判断是否能找到对象。

     //目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类
     @Pointcut("target(com.chenss.dao.IndexDaoImpl)")
     
     //当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个
     @Pointcut("this(com.chenss.dao.IndexDaoImpl)")
     
     //具有@Chenss的目标对象中的任意方法
     @Pointcut("@target(com.chenss.anno.Chenss)")
     
     //等同于@targ
     @Pointcut("@within(com.chenss.anno.Chenss)")
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值