zoukankan      html  css  js  c++  java
  • Spring 集成 AspectJ的原理

    今天试着用一下Spring + AspectJ的AOP,遇到了点问题,没有拦截到Bean的方法,比如controller里面的方法。网上搜索了大堆东西,都没有什么用了,而且看Spring官网的Reference也没有看出个什么问题。这种情况下只好去看源码分析一下了,首先我们使用的是注解的方式:

    spring的beans.xml配置文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
            >
        
        <context:component-scan base-package="org.mosmith.togethertravel" />
        ....
    
        <aop:aspectj-autoproxy />
    </beans>

    其实上面的的<aop:aspectj-autoroxy /> 对应了注解@EnableAspectJAutoProxy。从下面EnableAspectJAutoProxy的定义可以看到,注解导入了AspectJAutoProxyRegistrar配置类。

    @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}.
         */
        boolean proxyTargetClass() default false;
    
    }

    AspectJAutoProxyRegistrar主要在BeanDefinitionRegistry里面注册了AnnotationAwareAspectJAutoProxyCreator。从AnnotationAwareAspectJAutoProxyCreator的继承结构可以看出AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor,类似EnableWebMvc,这个类用来整合AspectJ和Spring的IOC容器。

    我们跳到继承结构上与AspectJ相关的annotation处理的相关方法,这个方法重写了父类的findCandidateadvisors(父类主要处理xml方式配置的advisor,这里添加了对注解声明的Adivisor的扫描,我们跳到BeanFactoryAspectJAdvisorsBuilder里面的buildAspectJAdvisors方法,从这个方法可以看到它从BeanFactory,也就是我们的Context里面取出所有的Bean,然后检查这个Bean是否有@Aspect注解,如果有的话则构建Advisor元数据,这里不直接构建Advisor实例,因为它由Spring IOC容器来管理。

    public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
            ...
        @Override
        protected List<Advisor> findCandidateAdvisors() {
            // Add all the Spring advisors found according to superclass rules.
            List<Advisor> advisors = super.findCandidateAdvisors();
            // Build Advisors for all AspectJ aspects in the bean factory.
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
            return advisors;
        }
            ...
    }
    /**
     * Helper for retrieving @AspectJ beans from a BeanFactory and building
     * Spring Advisors based on them, for use with auto-proxying.
     *
     * @author Juergen Hoeller
     * @since 2.0.2
     * @see AnnotationAwareAspectJAutoProxyCreator
     */
    public class BeanFactoryAspectJAdvisorsBuilder {
    
            ....
        /**
         * Look for AspectJ-annotated aspect beans in the current bean factory,
         * and return to a list of Spring AOP Advisors representing them.
         * <p>Creates a Spring Advisor for each AspectJ advice method.
         * @return the list of {@link org.springframework.aop.Advisor} beans
         * @see #isEligibleBean
         */
        public List<Advisor> buildAspectJAdvisors() {
            List<String> aspectNames = null;
    
            synchronized (this) {
                aspectNames = this.aspectBeanNames;
                if (aspectNames == null) {
                    List<Advisor> advisors = new LinkedList<Advisor>();
                    aspectNames = new LinkedList<String>();
                    String[] beanNames =
                            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                    for (String beanName : beanNames) {
                        if (!isEligibleBean(beanName)) {
                            continue;
                        }
                        // We must be careful not to instantiate beans eagerly as in this
                        // case they would be cached by the Spring container but would not
                        // have been weaved
                        Class<?> beanType = this.beanFactory.getType(beanName);
                        if (beanType == null) {
                            continue;
                        }
                        if (this.advisorFactory.isAspect(beanType)) {
                            aspectNames.add(beanName);
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory =
                                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                }
                                else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                advisors.addAll(classAdvisors);
                            }
                            else {
                                // Per target or per this.
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName +
                                            "' is a singleton, but aspect instantiation model is not singleton");
                                }
                                MetadataAwareAspectInstanceFactory factory =
                                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
    
            if (aspectNames.isEmpty()) {
                return Collections.emptyList();
            }
            List<Advisor> advisors = new LinkedList<Advisor>();
            for (String aspectName : aspectNames) {
                List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
                if (cachedAdvisors != null) {
                    advisors.addAll(cachedAdvisors);
                }
                else {
                    MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                }
            }
            return advisors;
        }
        ....
    }

    我们再去看一下Bean在实例化的时候,Spring通过getAdvicesAndAdvisorsForBean方法取出拦截此bean的Advisor Bean,然后创建代理,然后返回代理,这样子就可以拦截到这个bean的方法调用了。但通过调试发现这里并不是AspectJ实现拦截的地方,AspectJ实现拦截的地方在AbstractAutoProxyCreator.postProcessAfterInitialization方法,它调用wrapIfNecessary方法来对Bean进行Wrap,Wrapper类是利用cglib动态生成的子类。

    @SuppressWarnings("serial")
    public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
            implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        ....
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            Object cacheKey = getCacheKey(beanClass, beanName);
    
            if (beanName == null || !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;
                }
            }
    
            // 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.
            if (beanName != null) {
                TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
                if (targetSource != null) {
                    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;
        }
        ....


        /**
         * Create a proxy with the configured interceptors if the bean is
         * identified as one to proxy by the subclass.
         * @see #getAdvicesAndAdvisorsForBean
         */
        @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;
        }

    .... }
  • 相关阅读:
    Spring Boot Sample 033之swagger3.0
    Spring Boot Sample 025之spring-boot-security-oauth2
    Spring Boot Sample 024之spring-boot-data-influxdb
    docker 安装redis /mysql/rabbitmq
    发布视频文件,并配置vtt格式的字幕文件
    Windows控制台用copy命令合并二进制文件
    Solaris修改IP地址
    为java程序配置网络访问代理
    apache2 httpd.conf 反向代理设置实例
    Apache配置正向代理与反向代理
  • 原文地址:https://www.cnblogs.com/mosmith/p/6785708.html
Copyright © 2011-2022 走看看