美文网首页
spring aop源码分析(一)

spring aop源码分析(一)

作者: 定金喜 | 来源:发表于2021-04-13 23:20 被阅读0次

    以简单的例子为例说明aop的流程,源码如下:

    package com.renlijia;
    
    import com.AppWebConfig;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.context.ConfigurableApplicationContext;
    
    /**
     * @Author: ding
     * @Date: 2020-06-06 13:02
     */
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class AppStart {
        public static void main(String[] args) throws Exception{
    
    
            SpringApplication springApplication = new SpringApplication(AppWebConfig.class);
            ConfigurableApplicationContext context = springApplication.run(args);
        }
    }
    
    package com;
    
    import com.renlijia.annotation.Test1;
    import com.renlijia.controller.Student;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.context.annotation.*;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    
    /**
     * @Author: ding
     * @Date: 2021-01-18 13:15
     */
    @ComponentScan(basePackages = {"com"})
    @Configuration
    @EnableAutoConfiguration
    @EnableAspectJAutoProxy
    @ServletComponentScan
    @EnableWebMvc
    @Import(Student.class)
    public class AppWebConfig {
    }
    
    package com.renlijia.controller;
    
    import com.dxc.MyEnum;
    import com.google.common.collect.Maps;
    import com.renlijia.config.MyConfig;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.context.annotation.AutoProxyRegistrar;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternUtils;
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Array;
    import java.text.MessageFormat;
    import java.util.Map;
    
    /**
     * @Author: ding
     * @Date: 2020-12-08 09:57
     */
    @Controller
    public class TestController {
    
        @Autowired
        private Person person;
    
        @Autowired
        private Student student;
    
        @Autowired
        private GenericApplicationContext applicationContext;
    
        @Autowired
        private ResourceLoader resourceLoader;
    
        @Autowired
        private DispatcherServlet dispatcherServlet;
    
        @Autowired
        private Environment environment;
    
        @Autowired
        private BeanFactory beanFactory;
    
        @Autowired
        private MyConfig myConfig;
    
        private String name = "2222";
    
        @RequestMapping("/initBillData")
        public String initBillData(HttpServletRequest request, HttpServletResponse response) throws Exception{
            //request.getRequestDispatcher("/forward").forward(request,response);
            return "dxc.htm";
        }
    
        @RequestMapping("/redirect")
        public String redirect(HttpServletRequest request, HttpServletResponse response) throws Exception{
            response.sendRedirect("/forward");
            return "有兴趣来我们公司吗?";
        }
    
        @RequestMapping("/forward")
        @ResponseBody
        public User forward(HttpServletRequest request, HttpServletResponse response) throws Exception{
            User user = new User();
            user.setAge(100);
    
            return user;
        }
    
        @RequestMapping("/f1")
        @ResponseBody
        public User f1() throws Exception{
    
            MyEnum.getInstance().setAge(110000);
    
            System.out.println(environment.getProperty("java.runtime.name"));
    
            final TestController testController = applicationContext.getBean(TestController.class);
    
            ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
    
            ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
            MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
            org.springframework.core.io.Resource[] resources = resolver.getResources("classpath*:com/renlijia/**/*.class");
    
            for (org.springframework.core.io.Resource r : resources) {
                MetadataReader reader = metaReader.getMetadataReader(r);
                System.out.println(reader.getClassMetadata().getClassName());
            }
    
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            request.getSession();
    
            User user = new User();
            user.setAge(100);
            user.setUserId("123455533");
            return user;
        }
    
        @RequestMapping("/f2")
        public ModelAndView errorHtml(HttpServletRequest request,
                                      HttpServletResponse response) {
            Map<String, String> model = Maps.newHashMap();
            model.put("param1", "22222");
            return new ModelAndView("dxc.htm", model);
        }
    
        public static void main(String[] args) {
            System.out.println(AutoProxyRegistrar.class.getName());
        }
    
    }
    
    package com.renlijia;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    /**
     * @Author: ding
     * @Date: 2021-02-06 21:33
     */
    @Aspect
    @Component
    public class AdviceTest {
    
        @Pointcut("execution(public * com.renlijia.controller.TestController.*(..))")
        public void pointCut() {
    
        }
    
        @AfterReturning(pointcut="pointCut()", returning="returnValue")
        public void log(JoinPoint point, Object returnValue) {
            System.out.println("@AfterReturning:模拟日志记录功能...");
            System.out.println("@AfterReturning:目标方法为:" +
                    point.getSignature().getDeclaringTypeName() +
                    "." + point.getSignature().getName());
            System.out.println("@AfterReturning:参数为:" +
                    Arrays.toString(point.getArgs()));
            System.out.println("@AfterReturning:返回值为:" + returnValue);
            System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());
    
        }
    
        @After(value = "pointCut()")
        public void releaseResource(JoinPoint point) {
            System.out.println("@After:模拟释放资源...");
            System.out.println("@After:目标方法为:" +
                    point.getSignature().getDeclaringTypeName() +
                    "." + point.getSignature().getName());
            System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));
            System.out.println("@After:被织入的目标对象为:" + point.getTarget());
        }
    }
    

    这个例子很简单,就是在调用TestController所有方法之前和返回后打印一些信息,来分析下整个aop的流程,首先开启aop是通过EnableAspectJAutoProxy完成,EnableAspectJAutoProxy注解的内容为:


    所以真正加载的是AspectJAutoProxyRegistrar,然后我们进入这个类去看:



    核心是调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);注册,一层层进入,最后看到的是这块代码:



    这个代码很简单,就是先判断系统有没有AUTO_PROXY_CREATOR_BEAN_NAME这个bean,有的话根据优先级去加载,如果没有则创建beanDefinition,这个class是AnnotationAwareAspectJAutoProxyCreator,查看一下该类的继承关系图:

    所以该类是BeanPostProcessor接口的实现类,该接口有两个方法:



    实现这个接口的类,所有bean在初始化前后就会调用,我们看看AnnotationAwareAspectJAutoProxyCreator实现这两个接口的地方:

    postProcessAfterInitialization这个是核心,每个bean初始化后会走到这里,这个方法会调用包装方法wrapIfNecessary

    这个方法的功能是对bean进行包装,如果该bean不需要包装,则直接返回原bean,满足需要包装的条件去看getAdvicesAndAdvisorsForBean方法,该方法最后调用的是这块代码
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
            List<Advisor> candidateAdvisors = findCandidateAdvisors();
            List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
            extendAdvisors(eligibleAdvisors);
            if (!eligibleAdvisors.isEmpty()) {
                eligibleAdvisors = sortAdvisors(eligibleAdvisors);
            }
            return eligibleAdvisors;
        }
    
    protected List<Advisor> findCandidateAdvisors() {
            Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
            return this.advisorRetrievalHelper.findAdvisorBeans();
        }
    
    protected List<Advisor> findAdvisorsThatCanApply(
                List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    
            ProxyCreationContext.setCurrentProxiedBeanName(beanName);
            try {
                return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
            }
            finally {
                ProxyCreationContext.setCurrentProxiedBeanName(null);
            }
        }
    

    这块代码的逻辑是,先查询Advisors,在例子中有两个advisors,断点查看



    就是AdviceTest切面中的两个切点,findAdvisorsThatCanApply功能是找到切点满足该bean匹配条件的Advisors,因为这两个切点配置的正则表达式com.renlijia.controller.TestController.*(..)),所以对于TestController这个bean,返回的就是这两个Advisors,回到wrapIfNecessary代码块,如果返回的满足条件的Advisors不为空,则对该bean进行代理,代理代码:

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
            if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
                AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
            }
    
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.copyFrom(this);
    
            if (!proxyFactory.isProxyTargetClass()) {
                if (shouldProxyTargetClass(beanClass, beanName)) {
                    proxyFactory.setProxyTargetClass(true);
                }
                else {
                    evaluateProxyInterfaces(beanClass, proxyFactory);
                }
            }
    
            Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
            proxyFactory.addAdvisors(advisors);
            proxyFactory.setTargetSource(targetSource);
            customizeProxyFactory(proxyFactory);
    
            proxyFactory.setFrozen(this.freezeProxy);
            if (advisorsPreFiltered()) {
                proxyFactory.setPreFiltered(true);
            }
    
            return proxyFactory.getProxy(getProxyClassLoader());
        }
    

    根据配置的参数,会使用jdk或者cglib进行代理的生成,是否使用jdk还是cglib根据注解EnableAspectJAutoProxy中的proxyTargetClass决定:



    选择代理方式的代码:

    @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)) {
                    return new JdkDynamicAopProxy(config);
                }
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
    

    JdkDynamicAopProxy其实就是对应jdk代理的InvocationHandler,初步的分析到此,下一步分析代理中的执行的具体逻辑。

    相关文章

      网友评论

          本文标题:spring aop源码分析(一)

          本文链接:https://www.haomeiwen.com/subject/hzrhlltx.html