• Spring系列.AOP原理简析


    Spring AOP使用简介

    Spring的两大核心功能是IOC和AOP。当我们使用Spring的AOP功能时是很方便的。只需要进行下面的配置即可。

    @Component
    @Aspect
    public class MyAspect {
    
    //PointCut匹配的方法必须是Spring中bean的方法
    //Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.
    //下面定义的这些切入点就可以通过&& ||组合
    
    private static Logger logger = LoggerFactory.getLogger(MyAspect.class);
    
    //*:代表方法的返回值可以是任何类型
    //整个表达式匹配controller包下面任何的的echo方法,方法入参乐意是任意
    @Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(..))")
    public void pointCut1(){}
    
    @Before("pointCut1()")
    public void befor(){
        logger.info("前置通知vvvv...");
        logger.info("我要做些事情...");
    }
    }
    

    然后再开启注解

    //自动选择合适的AOP代理
    //传统xml这样配置:<aop:aspectj-autoproxy/>
    
    //exposeProxy = true属性设置成true,意思是将动态生成的代理类expose到AopContext的ThreadLocal线程
    //可以通过AopContext.currentProxy();获取到生成的动态代理类。
    
    //proxyTargetClass属性设置动态代理使用JDK动态代理还是使用CGlib代理,设置成true是使用CGlib代理,false的话是使用JDK动态代理
    
    //注意:如果使用Spring Boot的话,下面的配置可以不需要。AopAutoConfiguration这个自动配置类中已经自动开启了AOP
    //默认使用CGLIB动态代理,Spring Boot配置的优先级高于下面的配置
    
    @Configuration
    @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = false)
    public class AopConfig {
    
    }
    

    通上面的配置,当我们调用controller包下面的任何类的echo方法时就会触发前置通知。其实这个说法不是很准确。因为我们调用的类已经不是我们自己写的类了。而是Spring框架通过动态代理生成的类。

    稍微了解一点Spring AOP的同学都会知道Spring的AOP是通过动态代理实现的。那Spring是怎么生成动态代理类,并将Advice织入代理类的呢?整个流程是怎样的呢?下面就分析下Spring生成动态代理类的过程。

    需要说明下的是,本博客旨在梳理整个AOP动态代理的过程,细节方面需要大家自己去看。

    @EnableAspectJAutoProxy干了些啥

    如果让你从头开始研究下AOP的原理,你是不是一头雾水,根本不知道从何入手。但其实看Spring的代码有个小技巧:如果你要研究一个功能,可以从开启这个功能的Enable注解开始看。Spring的很多功能都是通过Enable注解开启的,所以这些注解肯定和这些功能相关。

    那么这边我们可以从@EnableAspectJAutoProxy这个注解开始着手,看下这个注解做了些什么操作。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
        //设置为true的话就一直使用cglib动态代理
        //设置为false的话,对于接口使用jdk动态代理,对于类使用cglib代理
    	boolean proxyTargetClass() default false;
    	boolean exposeProxy() default false;
    }
    

    看到上面的@Impoer注解,我们很自然就会想到去看AspectJAutoProxyRegistrar这个类。

    //AspectJAutoProxyRegistrar源代码
    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
         * 主要作用也就是注册AnnotationAwareAspectJAutoProxyCreator
         */
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //注册AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                //给上面注册的BeanDefinition中添加两个属相proxyTargetClass和exposeProxy
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }
    

    我们可以看到上面的类中也没干什么特别的事情,就注册了一个BeanDefinition。如果我们点进去看下AnnotationAwareAspectJAutoProxyCreator这个类的源代码会发现这个类竟然实现了InstantiationAwareBeanPostProcessor这个接口。熟悉Spring尿性的朋友会敏锐的感觉到Spring可能是在postProcessBeforeInstantiation或者postProcessAfterInstantiation这些方法中对Bean进行动态代理的。

    “大胆假设,小心求证”,让我们带着这个猜想去看看AnnotationAwareAspectJAutoProxyCreator到底干了些什么?

    AnnotationAwareAspectJAutoProxyCreator生成动态代理类

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);
    
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
        // 一般不指定CustomTargetSource,所以不会进入这段代码,所以关键代码在
        // postProcessAfterInitialization中
        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        return null;
    }
    

    下面是创建动态代理类的关键代码。

    @Override
        public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                    //这边是创建代码类的关键代码
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            if (StringUtils.hasLength(beanName) && 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.
            //获取当前Bean配置的advice,这步是关键
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                //创建代理
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
    
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    

    层层dedug进去我们能看到下面这段代码,我们口中常说的JDK动态代理和Cglib动态代理就是在这边生成的。

    public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    
        @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                            "Either an interface or a target is required for proxy creation.");
                }
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    //生成JDK动态代理
                    return new JdkDynamicAopProxy(config);
                }
                //生成Cglib动态代理
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
        /**
         * Determine whether the supplied {@link AdvisedSupport} has only the
         * {@link org.springframework.aop.SpringProxy} interface specified
         * (or no proxy interfaces specified at all).
         */
        private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
            Class<?>[] ifcs = config.getProxiedInterfaces();
            return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
        }
    

    到此,我们已经简单分析了Spring动态代理类的生成流程。

    PS:关于InstantiationAwareBeanPostProcessor接口和BeanPostProcessor接口大家可以自行了解下,这两个接口是Spring中非常重要的接口。看懂了这两个接口,Spring很多“神秘”的功能你就能理解了。

    简单总结

    通过上面分析,其实我们发现如果不去看AOP动态代理类生成的细节的话,整个Spring AOP的流程还是挺简单的:

    • @EnableAspectJAutoProxy注解通过AopConfigUtils这个工具类注册AnnotationAwareAspectJAutoProxyCreator这个类,这个类实现了InstantiationAwareBeanPostProcessor接口,所以会在Bean实例化前后对Bean做一系列额外的操作;
    • AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization中会找出所有和当前Bean相关的Advice,如果找到就创建相应的动态代理类,如果找不到就不生成,返回原始类。

    所以整个大流程就这么简单。

    一些重要类:

    • @EnableAspectJAutoProxy;
    • AspectJAutoProxyRegistrar:注册AnnotationAwareAspectJAutoProxyCreator
    • AnnotationAwareAspectJAutoProxyCreator:AOP动态代理自动生成的处理类,其他类似的类有AspectJAwareAdvisorAutoProxyCreator和InfrastructureAdvisorAutoProxyCreator等;
    • AopConfigUtils:AOP配置工具类
    • ProxyFactory:代理工厂
    • AopProxy接口:常见实现类ObjenesisCglibAopProxy、JdkDynamicAopProxy

    参考

  • 相关阅读:
    API网络数据安全
    【值得收藏】一文掌握用户画像系统构建全流程
    手把手教你从0到1学会tensorflow进行模型训练,并能在网页轻松运行
    APP选择第三方消息推送平台时,有哪些需要关注的重要性能指标?
    如何防薅羊毛?个推基于大数据风控引擎助力APP反欺诈!
    微信为什么要搞一个小游戏?
    关于 JS 模块化的最佳实践总结
    张小龙2019微信公开课15个看点总结
    编程:从前有一个傻呆程序员,老婆交给他一项任务,他办了四次才满意
    JS是如何计算 1+1=2 的?
  • 原文地址:https://www.cnblogs.com/54chensongxia/p/13141362.html
Copyright © 2020-2023  润新知