美文网首页
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