【代理模式AOP】2. @Aspect的代码实战(比较Cglib和动态JDK)

14 篇文章 0 订阅
6 篇文章 0 订阅

前言:
1)横切关注点,从每个方法中抽取出来的同一类非核心业务
2)切面(Aspect),对横切关注点进行封装的类,每个关注点体现为一个通知方法;通常使用 @Aspect 注解来定义切面。
3)通知(Advice),切面必须要完成的各个具体工作,比如我们的日志切面需要记录接口调用前后的时长,就需要在调用接口前后记录时间,再取差值。通知的方式有五种:
@Before:通知方法会在目标方法调用之前执行
@After:通知方法会在目标方法调用后执行
@AfterReturning:通知方法会在目标方法返回后执行
@AfterThrowing:通知方法会在目标方法抛出异常后执行
@Around:把整个目标方法包裹起来,在被调用前和调用之后分别执行通知方法
4)连接点(JoinPoint),通知应用的时机,比如接口方法被调用时就是日志切面的连接点。
5)切点(Pointcut),通知功能被应用的范围,比如本篇日志切面的应用范围是所有 controller 的接口。通常使用@Pointcut 注解来定义切点表达式。

1 execution表达式

使用execution表达式,直接匹配需要被代理的目标方法
execution表达式写法:
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? name-pattern(parameter-types-pattern) throws-pattern?)

  1. 匹配所有公共方法
    execution(public * *(..))
  2. 匹配名为 save 的所有方法,无论返回类型和参数类型
    execution(* save*(..))
  3. 匹配特定类中返回类型为 void 的方法
    execution(void com.example.MyClass.*(..))
  4. 匹配返回类型为 String 的任何方法,不论方法名和参数
    execution(String *(..))
  5. 匹配特定包中所有 update 方法
    execution(* com.example.service..update*(..))

(1) 代理类

@Aspect
@Component
public class MyBefore {
    @Before("execution(* com.example.demo.Service.testBefore(..))")
    public void before(){
        System.out.println("before...");
    }
}

(2) 被代理的类

@Component
public class Service {
    public void testBefore(){
        System.out.println("this is in the method");
    }
}

(3) 测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @Autowired
    private Service service;
    
    @Test
    public void testBefore(){
        service.testBefore();
    }
}

2 注解方式

声明注解, 在需要被代理的目标方法上增加注解

(1) 代理类

@Aspect
@Component
public class MyAround {
    @Pointcut(value = "@annotation(com.example.demo.ProxyAnnotation)")
    public void pointCut(){};

    @Around(value = "@annotation(around)")
    public Object around(ProceedingJoinPoint joinPoint, ProxyAnnotation around) throws Throwable {
        System.out.println(around.methodName());
        System.out.println("before...");
        int result = (int) joinPoint.proceed();
        System.out.println("result = " + result);
        System.out.println("after...");
        return result;
    }
}
  • @Around(value = "@annotation(around)")这里的"around"对应方法参数中的参数名around, 用来传递被代理的方法信息, 还可以在自定义的注解中自定义其他的属性
  • pointCut()上面标注的注解, 说明查找哪个注解被使用来增强. 还可以在增强的方法中显式使用
    @Aspect
    @Component
    public class MyAfterReturning {
        @Pointcut(value = "@annotation(com.alipay.demo.ProxyAnnotation2)")
        public void pointCut(){};
    
        @AfterReturning("pointCut()")
        public void afterReturning(){
            System.out.println("afterReturning...");
        }
    }
    
    中的@AfterReturning("pointCut()")

注意: 因为这里的切面类方法返回类型需要符合(包含)实际被代理的方法类型, 因此最好设置为Object

(2) 自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProxyAnnotation {
    String methodName();
}

(3) 被代理的类

@Component
public class Service {
    @ProxyAnnotation(methodName = "testAround")
    public int testAround(){
        System.out.println("this is in the method");
        return 1;
    }
}

(4) 测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @Autowired
    private Service service;

    @Test
    public void testAround(){
        service.testAround();
    }
}

3 具体代理生效的方式

在Spring中, 注解@Aspect具体起效可能是动态JDK(JDK Proxy)和Cglib, 具体是运行哪个, 是看被代理的目标类是否实现了接口决定的:
接口org.springframework.aop.framework.AopProxy的两个实现类:org.springframework.aop.framework.JdkDynamicAopProxyorg.springframework.aop.framework.CglibAopProxy

  • 动态JDK: 实现了接口的对象。当对一个实例调用一个方法时,Spring会生成一个代理,这个代理实现了该接口。
  • Cglib: 没有实现接口的对象。适用于没有实现接口的类或是类的所有方法都被 final 或是 private 修饰的情况。CGLIB会在运行时生成目标类的子类来实现代理。

由于Spring AOP现在已经接入了AspectJ, 如果想要以AspectJ的方式完成代理, 则需要在/META-INF/AOP.xml中设置:

<aspectj>
    <weaver>
        <include within="your.package.name.*"/>
    </weaver>
    <aspects>
        <aspect name="your.package.name.LoggingAspect"/>
    </aspects>
</aspectj>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值