Spring源码之AOP原理

2020-08-17  本文已影响0人  写代码的杰西

AOP的基本概念

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

例子

@Component
public class BizClass {
    public void doSth(){
        System.out.println(0/0);
        System.out.println("executing a biz code");
    }
}



@Aspect
@Component
public class LogAspect {
    /**
     * 定义切入点,切入点为com.example.demo.aop.AopController中的所有函数
     *通过@Pointcut注解声明频繁使用的切点表达式
     */
    @Pointcut("execution(public * com.jesse.aop.BizClass.*(..)))")
    public void LogAspect(){

    }

    /**
     * @description  在连接点执行之前执行的通知
     */
    @Before("LogAspect()")
    public void doBefore(){
        System.out.println("连接点执行之前");
    }

    /**
     * @description  在连接点执行之后执行的通知(返回通知和异常通知的异常)
     */
    @After("LogAspect()")
    public void doAfter(){
        System.out.println(" 在连接点执行之后执行的通知(返回通知和异常通知的异常)");
    }

    /**
     * @description  在连接点执行之后执行的通知(返回通知)
     */
    @AfterReturning("LogAspect()")
    public void doAfterReturning(){
        System.out.println("在连接点执行之后执行的通知(返回通知)");
    }

    /**
     * @description  在连接点执行之后执行的通知(异常通知)
     */
    @AfterThrowing("LogAspect()")
    public void doAfterThrowing(){
        System.out.println(" 在连接点执行之后执行的通知(异常通知)");
    }
}



@RunWith(SpringRunner.class)
@SpringBootTest(classes={StudyProApplication.class})
public class SpringTest {
    @Autowired
    BizClass bizClass;

    @Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
    }
    @Test
    public void testAop(){
        bizClass.doSth();
    }
}

结果

连接点执行之前
executing a biz code
在连接点执行之后执行的通知(返回通知)
在连接点执行之后执行的通知(返回通知和异常通知的异常)

源码分析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@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}.
     */
  //使用cglib还是java动态代理的方式 默认是动态代理
    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;
 

而能够让这个类参与到bean初始化功能,并为bean添加代理功能的还是因为它实现了BeanPostProcessor这个接口.这个接口的postProcessAfterInitialization方法会在bean初始化结束后(赋值完成)被调用。
这里先看一下最顶部的抽象类:AbstractAutoProxyCreator,这个抽象类主要抽象了实现代理的逻辑:


@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }
 
    // 主要看这个方法,在bean初始化之后对生产出的bean进行包装
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
 
    // wrapIfNecessary
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
 
        // Create proxy if we have advice.
        // 意思就是如果该类有advice则创建proxy,
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            
            // 2.返回该proxy代替原来的bean
            return proxy;
        }
 
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

总结
1)将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中
2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy

JDK Proxy 的优势:
最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。代码实现简单。
基于类似 cglib 框架的优势:有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。只操作我们关心的类,而不必为其他相关类增加工作量。高性能。

上一篇下一篇

猜你喜欢

热点阅读