Spring AOP 总结

AOP 面向切面编程

将一些共有的操作(日志、统计、安全控制、异常处理等)从业务逻辑代码中分离出来,实现复用,且不影响业务逻辑代码

AOP相关概念

  • Aspect 切面:包含多个Advice(通知)和Pointcut(切入点)声明
  • Join Point 连接点:Advice(通知,增强)可以运行的时机(函数的执行,异常的捕获)(Spring AOP中表示函数的执行) – 可以被切入的地方
  • Pointcut 切入点:用于匹配连接点的谓词
    • Spring默认使用AspectJ切入点表达式
  • Advice 通知(增强): 要在Join Point(连接点)上执行的操作(函数执行前,函数返回后…)。每个Advice关联一个Pointcut表达式,将在被Pointcut匹配的Join Point上运行
    • Spring AOP的Advice类型–在何时执行:
    • 前置通知 Before Advice: 函数执行前
    • 后置通知 After returning advice: 函数正常返回后执行
    • 最终通知 After(Finally) Advice:函数退出后(正常返回或抛出异常)
    • 环绕通知 **Around Advice **: 可在函数执行前后执行操作 – 在Advice中手动调用被切入的函数(切入点)
    • 异常通知 After throwing Advice:异常抛出后
  • Introduction :用于附加 字段行为到一个类型上。
    • Spring AOP可将新的接口和其实现附加到被增强的对象上
  • Target Object:被一个或多个Aspect增强的对象
  • Weaving:将Aspect与Target Object联系起来增强对象的过
    • 可在编译时、加载时、运行时完成

Spring中使用AOP

Spring同时集成Spring AOP 和 AspectJ

  • Spring AOP
    • Spring提供的AOP框架实现
    • 通过动态代理实现AOP
    • 其目标是实现与Spring IoC容器的结合 ,增强对象由容器管理
    • 只能增强函数的执行
    • 借用了AspectJ的一小部分,可以使用@AspectJ注解来定义aspect等
  • AspectJ独立的AOP框架,完备的AOP框架
  • 使用Spring AOP可以通过AspectJ的注解(@AspectJ Support),或配置文件(XML)

Spring AOP

实现机制
  • Spring AOP通过动态代理实现AOP

  • Spring AOP默认使用JDK的动态代理 和 CGLIB的动态代理

    • 若被增强的类实现了接口,使用JDK的动态代理。生成一个实现该接口的代理类来实现代理。
    • 若被增强的类没有实现接口,则使用CGLIB。生成一个被代理的类的子类来实现代理。
通过@AspectJ支持的方式使用Spring AOP
  • Spring AOP借用AspectJ定义的一系列注解,来完成切入点的解析和匹配 —> @AspectJ支持

  • 使用注解启用@AspectJ支持,Spring Boot中默认开启

    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig {
    }
    
  • 声明切面**@Aspect**, 并注册到容器@Component

  • 声明切入点**@Pointcut(“切入点表达式”)**

    @Aspect
    @Component
    public class TestAspect {
        @Pointcut("execution(public * *(..))") 		// 切入点表达式:匹配所有共有方法
    	private void anyPublicOperation() {} 		// 切入点签名
    }
    
  • 切入点表达式为 AspectJ的表达式格式,用于匹配连接点。Spring AOP支持的AspectJ 切入点标识符(pointcut designer, PDC)

    • execution(…):匹配函数的执行。格式
      • execution(public * *(…)) 所有共有方法
      • execution(* set*(…)) 所有set开头的方法
      • execution(* com.xyz.service.AccountService.*(…)) 所有被AccountService接口定义的方法
      • execution(* com.xyz.service..(…)) service包下所有方法
      • execution(* com.xyz.service….(…)) service包及其子包下所有方法
    • within(…)
      • within(com.xyz.service.*) service包下
      • within(com.xyz.service…*) service及其子包下
    • this(…) 指定类型的代理(Aspect为被增强的类创建的代理对象,与被代理的类具有相同的类型)
    • target(…) 指定类型的Target Object
    • args(…) 拥有指定参数类型的方法
    • @target(…) 拥有指定注解的对象
    • @within(…) 拥有指定注解的类
    • @annotation(…) 拥有指定注解的方法
    • @args(…) 参数用于指定注解的方法
    • Spring AOP拓展的标识符 bean(…) :匹配bean的id或名称
  • 切入点表达式可使用&& || !进行连接

  • 常用切入点表达式

    package com.xyz.myapp;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class CommonPointcuts {
    
        /**
         * A join point is in the web layer if the method is defined
         * in a type in the com.xyz.myapp.web package or any sub-package
         * under that.
         */
        @Pointcut("within(com.xyz.myapp.web..*)")
        public void inWebLayer() {}
    
        /**
         * A join point is in the service layer if the method is defined
         * in a type in the com.xyz.myapp.service package or any sub-package
         * under that.
         */
        @Pointcut("within(com.xyz.myapp.service..*)")
        public void inServiceLayer() {}
    
        /**
         * A join point is in the data access layer if the method is defined
         * in a type in the com.xyz.myapp.dao package or any sub-package
         * under that.
         */
        @Pointcut("within(com.xyz.myapp.dao..*)")
        public void inDataAccessLayer() {}
    
        /**
         * A business service is the execution of any method defined on a service
         * interface. This definition assumes that interfaces are placed in the
         * "service" package, and that implementation types are in sub-packages.
         *
         * If you group service interfaces by functional area (for example,
         * in packages com.xyz.myapp.abc.service and com.xyz.myapp.def.service) then
         * the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
         * could be used instead.
         *
         * Alternatively, you can write the expression using the 'bean'
         * PCD, like so "bean(*Service)". (This assumes that you have
         * named your Spring service beans in a consistent fashion.)
         */
        @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
        public void businessService() {}
    
        /**
         * A data access operation is the execution of any method defined on a
         * dao interface. This definition assumes that interfaces are placed in the
         * "dao" package, and that implementation types are in sub-packages.
         */
        @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
        public void dataAccessOperation() {}
    }
    
  • 声明增强(Advice)

    @Aspect
    @Component
    public class TestAspect {
        //---------------------------------------------------------------------------------
        @Pointcut("execution(public * *(..))")
    	private void anyPublicOperation() {}
        
        //声明Advice----------------------------------------------------------------------------------------
        @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
        public void doAccessCheck() {
            // ...
        }
        
        //使用签名指定切入点------------------------------------------------------------------
        @AfterReturning(pointcut = "anyPublicOperation()", returning = "returnVal")
        public void doAccessCheck(JoinPoint joinPoint, Object returnVal) {
            //通过连接点的函数签名
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Class<?> returnType = methodSignature.getReturnType();
            //.....
            
        }
        
        //---------------------------------------------------------------------------------
        @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
        public void doRecoveryActions() {
            // ...
        }
        
        //---------------------------------------------------------------------------------
        @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
        public void doReleaseLock() {
            // ...
        }
        
        //---------------------------------------------------------------------------------
        @Around("com.xyz.myapp.CommonPointcuts.businessService()")
        public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
            //开始
            
            Object retVal = pjp.proceed();	//执行函数
            
            //完成
            return retVal;
        }
        
        //捕获参数2---------------------------------------------------------------------------
        @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
        public void validateAccount(Account account) {
            // ...
        }
    	
        //捕获参数2---------------------------------------------------------------------------
        @Pointcut("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
        private void accountDataAccessOperation(Account account) {}
    
        @Before("accountDataAccessOperation(account)")
        public void validateAccount(Account account) {
            // ...
        }
    }
    
    • 第一个参数可是为JoinPoint,以获取其切入的连接点的信息(Around Advice的应为ProceedingJoinPoint (JoinPoint子类))
    • 可通过JoinPoint获取连接点的签名。对于函数,签名为MethodSignature类型
  • Advice执行的优先级

    • 进入连接点时执行的Advice(Before Advice),高优先级的先执行
    • 离开连接点时执行的Advice(After Advice…),高优先级的后执行
    • 使用**@Order实现org.springframework.core.Ordered**设置Aspect类的优先级
    • 同Aspect下Advice的优先级: @Around > @Before > @After > @AfterReturning > @AfterThrowing
Introductions
  • 使用Aspect声明一个被增强的对象实现了一个接口,并提为这些对象提供接口的实现

  • @DeclareParents使被匹配的类型,实现新的接口,并提供默认实现

    @Aspect
    public class UsageTracking {
    	//使得service下的类 是接口UsageTracked的实现,并提供了默认实现
        //(DefaultUsageTracked implements UsageTracked)
        @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
        public static UsageTracked mixin;
    
        @Before("com.xyz.myapp.CommonPointcuts.businessService() && this(usageTracked)")
        public void recordUsage(UsageTracked usageTracked) {
            usageTracked.incrementUseCount();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绫零依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值