Spring-AOP实现原理(1)-解析切面

1. 简要写一个spring-AOP的例子

主启动类

@EnableAspectJAutoProxy
@ComponentScan("com.jw.springframework.g_aop")
public class AopMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopMain.class);
        Caculate caculate = context.getBean(Caculate.class);
        System.out.println(caculate.add(1, 2));
       /* ProgramCaculate programCaculate = context.getBean(ProgramCaculate.class);
        System.out.println(programCaculate.logicalAnd(16, 15));*/
    }

}

切面类以及切面类内部设置pointcut切点和两个advice通知, before和after

@Component
@Aspect
public class CaculateAspect {

    @Pointcut("execution(* com.jw.springframework.g_aop.Caculate.*(..))")
    public void pointCut() {
    }

    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法[" + methodName + "]的前置通知, 入参: " + Arrays.asList(joinPoint.getArgs()));
    }

    @After(value = "pointCut()")
    public void methodAfter(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法[" + methodName + "]的后置通知, 入参: " + Arrays.asList(joinPoint.getArgs()));
    }

    // 引入
    /*@DeclareParents(value = "com.jw.springframework.g_aop.Caculate",  // 需要动态增强的类
            defaultImpl = ProgramCaculateImpl.class)  // 引入接口的默认实现
    public ProgramCaculate programCaculate;  // 引入的接口*/

}

需要被增强的类, 以及切点方法jointPoint

@Component
public class Caculate {
    public int add(int a, int b){
        System.out.println("执行add方法, 本切点具有前置和后置通知");
        return a + b;
    }
}

运行结果:

执行目标方法[add]的前置通知, 入参: [1, 2]
执行add方法, 本切点具有前置和后置通知
执行目标方法[add]的后置通知, 入参: [1, 2]
3

2. AOP的实现原理

要使用AOP首先需要在某个能被扫描的类上加上@EnableAspectJAutoProxy注解, 保证开启了AOP功能

然后写一个切面类, 使用@Aspect注解标注, 再在本切面类上写一个@PointCut("切点表达式")注解的方法, 保证明确切点目标

再在切面类中写通知, 使用@Around, @Before, @After等等之类的注解表示这是一个什么样的通知

最终都会被spring解析, 对要增强的切点方法所在的对象进行动态代理, 把所有的通知, 每个通知解析成为一个Advisor对象, 在最后转换成interceptor, 最终通过责任链的方式进行依次调用, 实现对方法的增强

3. 源码

首先看看@EnableAspectJAutoProxy注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/*xxx: 这个是允许Aspectj自动代理的注解*/
/*xxx: 在springboot中,默认是会配置这个注解,并且默认用的是 cglib的代理*/
/*xxx: 与之想对的是,spring默认用的是 jdk接口代理*/

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

其中proxyTargetClass属性代表可以选择强制使用cglib代理, 后续会说明如何作用的

exposeProxy属性表示是否暴露当前代理对象到ThreadLocal中去, 我们知道, 如果使用jdk动态代理, 在增强方法中内嵌再调用一个增强方法, 默认是不会执行内嵌方法的增强的, 这是因为jdk动态代理是通过反射调用, 所以spring做了一种解决方案, 在AopContext中有一个ThreadLocal变量, 如下代码所示

public final class AopContext {

	/**
	 * ThreadLocal holder for AOP proxy associated with this thread.
	 * Will contain {@code null} unless the "exposePro

你可能感兴趣的:(spring,java,aop)