• spring aop


    源码大约分两步分,第一部分是在spring ioc初始化过程中加载所有aop标签到容器中,绑定代理类生成器AspectJAwareAdvisorAutoProxyCreator。
    第二部分是在调用getBean实例化bean后,通过BeanPostProcessor的回调机制生成代理类。

    如何使用

    @Aspect
    public class VocalConcert {
    
    //定义一个切点,下面通知中可以直接使用,避免重复代码
        @Pointcut("execution(* com.aop.test1.Song.song(..))")
        public void doing() {
        }
    
        @Before("doing()")
        public void checking() {
            System.out.println("检票之后,找位子坐下");
        }
    
        @AfterReturning("doing()")
        public void beautiful() {
            System.out.println("演唱会进入精彩部分的时候,鼓掌!");
        }
    
        @AfterReturning("doing()")
        public void leave() {
            System.out.println("结束后,我们离开场地");
        }
    }
    

    结合上面代码分析什么叫切面、切点、通知、引入?
    切面:被声明为@Aspect的整个类VocalConcert叫做切面。
    切点:@Pointcut该注解定义切点,用来指明要拦截的目标类的方法
    通知:切面中定义的那些before、after等等逻辑可以叫做通知。
    引入:上面涉及到所有的都是对类中方法的增加,引入是对类的增强
    通知:通知包括以下几种:
    1、前置通知
    2、后置通知 在目标方法执行完之后执行
    3、返回通知 在目标方法正常返回后执行,如果发生异常,目标方法没有返回,则不执行
    4、异常通知
    5、环绕通知 前置通知+后置通知

    spring支持的4种aop类型:
    1、经典spring aop:不在使用
    2、AspectJ切面:和spring没有什么关系,AspectJ框架。可以对字段构造器等进行增强。不做深入学习。
    3、纯pojo切面:基于aop的命名空间, 在xml中定义切面
    4、注解驱动的切面:对纯pojo切面进行改进,不在xml中定义切面,通过声明切面类+注解
    关注的是3和4,就是spring中传统的xml和注解两种方式。

    纯pojo切面(xml)

    1、定义切面类

     public class SleepAspect {
            public void before(){
                System.out.println("睡觉之前拖衣服");
            }
            public void afterReturning(){
                System.out.println("睡觉");
            }
            public void afterThrowing(Exception ex) throws Exception {
                System.out.println("出大事了,有bug");
                System.out.println(ex.getMessage());
            }
            public Object around(ProceedingJoinPoint pjp) throws Throwable{
                Object proceed =null;
                if (!"".equals("admin")){
                    System.out.println("核心方法被执行");
                    proceed = pjp.proceed(pjp.getArgs());
                    System.out.println("核心方法执行完");
                }
                return proceed;
            }
        }
    

    2、声明目标类
    3、xml中声明切面

    <!--将切面类和目标类都交给spring容器-->
        <bean id="sleep" class="com.yztc.pojo.SleepDaoImpl"/>
        <bean id="sleepAspect" class="com.yztc.pojo.SleepAspect"/>
    
        <!--配置AOP-->
        <aop:config>
            <!--切面-->
            <aop:aspect id="aspect" ref="sleepAspect">
                <!--切入点的配置-->
                <aop:pointcut id="pointcut" expression="execution(* com.yztc.pojo.*.Sleep.*(..))"/>
    
                <aop:before method="before" pointcut-ref="pointcut"/>
                <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="pointcut"/>
                <aop:around method="around" pointcut-ref="pointcut"/>
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    

    注解驱动的切面

    这种方式就是在纯pojo切面基础上通过注解进行改进,不用在xml中声明切面。
    1、定义一个切面

    @Aspect
    public class VocalConcert {
    
    //定义一个切点,下面通知中可以直接使用,避免重复代码
        @Pointcut("execution(* com.aop.test1.Song.song(..))")
        public void doing() {
        }
    
        @Before("doing()")
        public void checking() {
            System.out.println("检票之后,找位子坐下");
        }
    
        @AfterReturning("doing()")
        public void beautiful() {
            System.out.println("演唱会进入精彩部分的时候,鼓掌!");
        }
    
        @AfterReturning("doing()")
        public void leave() {
            System.out.println("结束后,我们离开场地");
        }
    }
    

    2、开启切面功能

    <?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:aop="http://www.springframework.org/schema/aop"   //这里引入aop命名空间
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <context:component-scan base-package="com.aop.test1"/>
    
        <bean class="com.aop.test1.VocalConcert" id="concert"/>
        
        <aop:aspectj-autoproxy/>   //开启spring切面功能
    
    </beans>
    

    返回顶部

    AopNamespaceHandler

    aop自定义标签解析器,用来解析aop自定义标签。
    AopNamespaceHandler和aop标签是如何映射的?
    xml中aop的命名空间 -- spring aop jar包下的META-INF下的spring.handlers。
    AopNamespaceHandler :该类的init方法中绑定了几种aop标签对应的parser

    public class AopNamespaceHandler extends NamespaceHandlerSupport {
    	@Override
    	public void init() {
    		// In 2.0 XSD as well as in 2.1 XSD.
    		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());// 
    		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
     
    		// Only in 2.0 XSD: moved to context namespace as of 2.1
    		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    	}
    }
    

    aop标签包含好几种,每一种都对应自己的parser,这种映射关系在上面AopNamespaceHandler的init方法中指定。具体标签包括哪些呢?

    <aop:config>
            <!--切面-->
            <aop:aspect id="aspect" ref="sleepAspect">
                <!--切入点的配置-->
                <aop:pointcut id="pointcut" expression="execution(* com.yztc.pojo.*.Sleep.*(..))"/>
    
                <aop:before method="before" pointcut-ref="pointcut"/>
                <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="pointcut"/>
                <aop:around method="around" pointcut-ref="pointcut"/>
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>
    

    上面代码中:config、aspect等都属于aop标签。

    返回顶部

    获取标签解析器

    回顾ioc初始化流程,
    DefaultBeanDefinitionDocumentReader.parseBeanDefinitions:

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            if (delegate.isDefaultNamespace(root)) {
                NodeList nl = root.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {//循环解析所有标签
                    Node node = nl.item(i);
                    if (node instanceof Element) {
                        Element ele = (Element) node;
                        if (delegate.isDefaultNamespace(ele)) {
                            parseDefaultElement(ele, delegate);//默认标签
                        }
                        else {
                            delegate.parseCustomElement(ele);//自定义标签
                        }
                    }
                }
            }
            else {
                delegate.parseCustomElement(root);
            }
        }
    
    
    //BeanDefinitionParserDelegate
    public BeanDefinition parseCustomElement(Element ele) {
            return parseCustomElement(ele, null);
        }
        public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
            String namespaceUri = getNamespaceURI(ele);
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    
    
    //NamespaceHandlerSupport
    @Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            return findParserForElement(element, parserContext).parse(element, parserContext);
        }
    
    
    
    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    		String localName = parserContext.getDelegate().getLocalName(element);
    		BeanDefinitionParser parser = this.parsers.get(localName);
    		if (parser == null) {
    			parserContext.getReaderContext().fatal(
    					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    		}
    		return parser;
    	}
    

    首先获取标签解析器

    因为aop最外层标签是config标签,所以先看下ConfigBeanDefinitionParser的parse方法
    ConfigBeanDefinitionParser.parse:

     public BeanDefinition parse(Element element, ParserContext parserContext) {
            CompositeComponentDefinition compositeDef =
                    new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
            parserContext.pushContainingComponent(compositeDef);
    
            //向spring容器注册AspectJAwareAdvisorAutoProxyCreator
            configureAutoProxyCreator(parserContext, element);
    
            List<Element> childElts = DomUtils.getChildElements(element);
            for (Element elt: childElts) {
                String localName = parserContext.getDelegate().getLocalName(elt);
                if (POINTCUT.equals(localName)) {//切点
                    parsePointcut(elt, parserContext);
                }
                else if (ADVISOR.equals(localName)) {//通知
                    parseAdvisor(elt, parserContext);
                }
                else if (ASPECT.equals(localName)) {//切面
                    parseAspect(elt, parserContext);
                }
            }
    
            parserContext.popAndRegisterContainingComponent();
            return null;
        }
    

    1、向spring容器注册代理类生产器AspectJAwareAdvisorAutoProxyCreator
    2、遍历所有aop标签下的所有子节点,分别定义三个入口,切面、切点、通知

    返回顶部

    解析切面

    接下来该解析切面,注意,切面里边包含通知和切点,所以这个方法也包含了通知和切点的解析
    ConfigBeanDefinitionParser.parseAspect:遍历所有aop标签,解析相应的通知和切点

     private void parseAspect(Element aspectElement, ParserContext parserContext) {
            String aspectId = aspectElement.getAttribute(ID);
            String aspectName = aspectElement.getAttribute(REF);
    
            try {
                this.parseState.push(new AspectEntry(aspectId, aspectName));
                List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
                List<BeanReference> beanReferences = new ArrayList<BeanReference>();
    
                List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
                for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
                    Element declareParentsElement = declareParents.get(i);
                    beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
                }
    
                //获取切面所有子节点进行遍历
                NodeList nodeList = aspectElement.getChildNodes();
                boolean adviceFoundAlready = false;
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Node node = nodeList.item(i);
                    if (isAdviceNode(node, parserContext)) {
                        if (!adviceFoundAlready) {
                            adviceFoundAlready = true;
                            if (!StringUtils.hasText(aspectName)) {
                                parserContext.getReaderContext().error(
                                        "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                                        aspectElement, this.parseState.snapshot());
                                return;
                            }
                            beanReferences.add(new RuntimeBeanReference(aspectName));
                        }
                        //如果标签是通知,进入解析通知标签方法
                        AbstractBeanDefinition advisorDefinition = parseAdvice(
                                aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                        beanDefinitions.add(advisorDefinition);
                    }
                }
    
                AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
                        aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
                parserContext.pushContainingComponent(aspectComponentDefinition);
    
                List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
                for (Element pointcutElement : pointcuts) {//获取切面下所有切点标签进行解析
                    //进入切点标签的解析方法入口
                    parsePointcut(pointcutElement, parserContext);
                }
    
                parserContext.popAndRegisterContainingComponent();
            }
            finally {
                this.parseState.pop();
            }
        }
    

    该方法拿到切面下所有的切点和通知标签进行遍历解析。切点调用parsePointcut解析,通知调用parseAdvice解析。

    解析通知

    //ConfigBeanDefinitionParser
     private AbstractBeanDefinition parseAdvice(
                String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
                List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    
            try {
                this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
    
                // create the method factory bean
                RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
                methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
                methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
                methodDefinition.setSynthetic(true);
    
                // create instance factory definition
                RootBeanDefinition aspectFactoryDef =
                        new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
                aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
                aspectFactoryDef.setSynthetic(true);
    
                // 声明通知为BeanDefinition
                AbstractBeanDefinition adviceDef = createAdviceDefinition( //入口
                        adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
                        beanDefinitions, beanReferences);
    
                // 对上面创建的BeanDefinition包装一下
                RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
                advisorDefinition.setSource(parserContext.extractSource(adviceElement));
                advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
                if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
                    advisorDefinition.getPropertyValues().add(
                            ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
                }
    
                // register the final advisor
                parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
    
                return advisorDefinition;
            }
            finally {
                this.parseState.pop();
            }
        }
    
    
    
     private AbstractBeanDefinition createAdviceDefinition(
                Element adviceElement, ParserContext parserContext, String aspectName, int order,
                RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
                List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    
            //入口
            RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
            adviceDefinition.setSource(parserContext.extractSource(adviceElement));
    
            adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
            adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    
            if (adviceElement.hasAttribute(RETURNING)) {
                adviceDefinition.getPropertyValues().add(
                        RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
            }
            if (adviceElement.hasAttribute(THROWING)) {
                adviceDefinition.getPropertyValues().add(
                        THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
            }
            if (adviceElement.hasAttribute(ARG_NAMES)) {
                adviceDefinition.getPropertyValues().add(
                        ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
            }
    
            ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
            cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
    
            Object pointcut = parsePointcutProperty(adviceElement, parserContext);
            if (pointcut instanceof BeanDefinition) {
                cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
                beanDefinitions.add((BeanDefinition) pointcut);
            }
            else if (pointcut instanceof String) {
                RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
                cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
                beanReferences.add(pointcutRef);
            }
    
            cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    
            return adviceDefinition;
        }
    
        private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
            String elementName = parserContext.getDelegate().getLocalName(adviceElement);
            if (BEFORE.equals(elementName)) {
                return AspectJMethodBeforeAdvice.class;
            }
            else if (AFTER.equals(elementName)) {
                return AspectJAfterAdvice.class;
            }
            else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
                return AspectJAfterReturningAdvice.class;
            }
            else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
                return AspectJAfterThrowingAdvice.class;
            }
            else if (AROUND.equals(elementName)) {
                return AspectJAroundAdvice.class;
            }
            else {
                throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
            }
        }
    

    该方法为每个通知实例化一个bean到spring容器。
    不同类型的通知生成的RootBeanDifination的class属性不同

    返回顶部

    解析切点

    //ConfigBeanDefinitionParser
    private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
            String id = pointcutElement.getAttribute(ID);
            String expression = pointcutElement.getAttribute(EXPRESSION);
    
            AbstractBeanDefinition pointcutDefinition = null;
    
            try {
                this.parseState.push(new PointcutEntry(id));
                //将pointcut创建成一个BeanDefinition
                pointcutDefinition = createPointcutDefinition(expression);//入口
                pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
    
                String pointcutBeanName = id;
                if (StringUtils.hasText(pointcutBeanName)) {
                    parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
                }
                else {
                    pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
                }
    
                parserContext.registerComponent(
                        new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
            }
            finally {
                this.parseState.pop();
            }
    
            return pointcutDefinition;
        }
        protected AbstractBeanDefinition createPointcutDefinition(String expression) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
            beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
            beanDefinition.setSynthetic(true);
            beanDefinition.getPropertyValues().add(EXPRESSION, expression);
            return beanDefinition;
        }
    

    为每个切点实例化一个bean到spring容器

    返回顶部

    代理类的创建

    到这里,在ioc启动过程中,通过aop解析器把aop标签声明成BeanDefinition到spring容器中,已经完毕。
    同时注意上面注册了一个类AspectJAwareAdvisorAutoProxyCreator,该类是BeanPostProcessor的下层类,同时封装了为普通bean生成代理类的方法。
    这就意味着,在调用getBean获取bean实例之后,通过BeanPostProcessor的回调机制,通过spring容器中的相关aop标签和AspectJAwareAdvisorAutoProxyCreator类就可以创建代理类。

    BeanPostProcessor回调为入口:

    //AbstractAutoProxyCreator
    @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            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 (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;
            }
    
            //判断是否需要生成代理类
            //入口1
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(//入口2  生成代理的方法
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
    
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
    
    @Override
        protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
            /***
             * findEligibleAdvisors方法是拿当前Advisor对应的expression做了两层判断:
             *
             * 目标类必须满足expression的匹配规则
             * 目标类中的方法必须满足expression的匹配规则,当然这里方法不是全部需要满足expression的匹配规则,有一个方法满足即可
             */
            List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
            if (advisors.isEmpty()) {
                return DO_NOT_PROXY;
            }
            return advisors.toArray();
        }
    

    1、根据bean名称去spring容器获取所有的asvisor,注意:这个advisor包含切面和通知
    2、匹配切点规则是否满足这个类,如果满足调用createProxy创建代理类

    接下来就是创建代理类的方法

      protected Object createProxy(
                Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    
            if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
                AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
            }
    
            // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.copyFrom(this);
    
            //<aop:config>这个节点中proxy-target-class="false"或者proxy-target-class不配置,即不使用CGLIB生成代理。
            // 如果满足条件,进判断,获取当前Bean实现的所有接口,讲这些接口Class对象都添加到ProxyFactory中。
            if (!proxyFactory.isProxyTargetClass()) {
                if (shouldProxyTargetClass(beanClass, beanName)) {
                    proxyFactory.setProxyTargetClass(true);
                } else {
                    evaluateProxyInterfaces(beanClass, proxyFactory);
                }
            }
    
            // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
            Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
            for (Advisor advisor : advisors) {
                proxyFactory.addAdvisor(advisor);
            }
    
            proxyFactory.setTargetSource(targetSource);
            customizeProxyFactory(proxyFactory);
    
            proxyFactory.setFrozen(this.freezeProxy);
            if (advisorsPreFiltered()) {
                proxyFactory.setPreFiltered(true);
            }
    
            // 3.调用getProxy获取bean对应的proxy
            return proxyFactory.getProxy(getProxyClassLoader());//入口
        }
    

    1、创建代理类工厂
    2、将所有满足条件的advisor bean添加到代理工厂中
    3、通过代理工厂创建代理类

    public Object getProxy(ClassLoader classLoader) {
            return createAopProxy().getProxy(classLoader);//入口1 + 入口2
        }
    

    1、通过createAopProxy选择是jdk还是cglib
    2、通过getProxy创建代理类

      // createAopProxy()方法就是决定究竟创建何种类型的proxy
        protected final synchronized AopProxy createAopProxy() {
            if (!this.active) {
                activate();
            }
            return getAopProxyFactory().createAopProxy(this);
        }
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            // 1.ProxyConfig的isOptimize方法为true,这表示让Spring自己去优化而不是用户指定
            // 2、ProxyConfig的isProxyTargetClass方法为true,这表示配置了proxy-target-class="true"
            // 3、ProxyConfig满足hasNoUserSuppliedProxyInterfaces方法执行结果为true,
            // 这表示<bean>对象没有实现任何接口或者实现的接口是SpringProxy接口
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
    
                // 2.如果目标类是接口或者是代理类,则直接使用JDKproxy
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
    
                // 3.其他情况则使用CGLIBproxy
                return new ObjenesisCglibAopProxy(config);
            } else {
                return new JdkDynamicAopProxy(config);
            }
        }
    

    如果配置了就按照配置的选择,如果目标类是接口直接使用jdk代理

    以jdk动态代理为例继续走下去。

    public Object getProxy(ClassLoader classLoader) {
            Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
            findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    
            // JDK proxy 动态代理的标准用法
            return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
        }
    

    jdk动态代理创建。

    返回顶部

    执行目标方法

    我们知道,jdk动态代理通过代理类调用目标方法的时候,实际调用的是自定义拦截器的invoke方法,aop的自定义拦截器为JdkDynamicAopProxy。

    //知道,jdk动态代理通
     @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MethodInvocation invocation;
            Object oldProxy = null;
            boolean setProxyContext = false;
    
            TargetSource targetSource = this.advised.targetSource;
            Class<?> targetClass = null;
            Object target = null;
    
            try {
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                    return equals(args[0]);
                }
                else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    return hashCode();
                }
                else if (method.getDeclaringClass() == DecoratingProxy.class) {
                    return AopProxyUtils.ultimateTargetClass(this.advised);
                }
                //没有拦截器的时候,进入这个方法,直接通过反射调用目标方法
                else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);//入口1
                }
    
                Object retVal;
    
                if (this.advised.exposeProxy) {//将代理类暴漏到AopContext中
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
    
                // 获取目标对象
                target = targetSource.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
    
                // 获取拦截器链 advisor
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//入口3
    
                if (chain.isEmpty()) {
                    // 如果拦截器为空,那么直接调用目标对象的目标方法 进入该方法
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                }
                else {
                    // 否则,需要先调用拦截器然后再调用目标方法,通过构造一个ReflectiveMethodInvocation来实现
                    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    // 沿着拦截器链前行 进入该方法
                    retVal = invocation.proceed();//入口2
                }
    
                Class<?> returnType = method.getReturnType();
                if (retVal != null && retVal == target &&
                        returnType != Object.class && returnType.isInstance(proxy) &&
                        !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    retVal = proxy;
                }
                else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                    throw new AopInvocationException(
                            "Null return value from advice does not match primitive return type for: " + method);
                }
                return retVal;
            }
        }
    
    
        public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
                throws Throwable {
            ReflectionUtils.makeAccessible(method);
            // 通过反射的方式直接调用目标对象方法
            return method.invoke(target, args);
        }
         
    
    
    
    
    @Override
        public Object proceed() throws Throwable {
            // 从索引为-1的拦截器开始调用,按顺序递增,直到没有拦截器了,然后开始调用目标对象的方法
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
            // 沿着定义好的拦截器链进行获取然后逐个处理
            Object interceptorOrInterceptionAdvice =
                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                // 这里是触发匹配判断的地方,如果和定义的pointcut匹配,那么这个advice将得到执行
                InterceptorAndDynamicMethodMatcher dm =
                        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
                if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                    return dm.interceptor.invoke(this);
                }
                else {
                    return proceed();
                }
            }
            else {
                // 如果是一个interceptor,那么直接调用这个interceptor的invoke方法 进入此方法来分析通知(拦截器)是如何起作用的
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    
    
     @Override
        public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
                Advised config, Method method, Class<?> targetClass) {
            // 所有的advistor在config中已经持有了,可以直接使用
            // This is somewhat tricky... We have to process introductions first,
            // but we need to preserve order in the ultimate list.
            List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
            Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
            boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
            AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    
            for (Advisor advisor : config.getAdvisors()) {
                if (advisor instanceof PointcutAdvisor) {
                    // Add it conditionally.
                    PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                    if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                        //通过AdvisorAdapterRegistry注册器,把从config中获取的advistor进行适配,从而获得拦截器,再把它放入List中。这就是拦截器注册的过程。
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                        if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                            if (mm.isRuntime()) {
                                // Creating a new object instance in the getInterceptors() method
                                // isn't a problem as we normally cache created chains.
                                for (MethodInterceptor interceptor : interceptors) {
                                    interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                                }
                            }
                            else {
                                interceptorList.addAll(Arrays.asList(interceptors));
                            }
                        }
                    }
                }
                else if (advisor instanceof IntroductionAdvisor) {
                    IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                    if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                        Interceptor[] interceptors = registry.getInterceptors(advisor);
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
                else {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
    
            return interceptorList;
        }
    
    1. 将所有的通知封装成一个拦截器链表,然后按顺序执行,最后通过反射调用目标方法
    2. AopContext内部通过ThreadLocal来存储了代理类,如果expose-proxy属性开启,这里还没将代理类放入到AopContext中。

    返回顶部

  • 相关阅读:
    登录界面的实现
    构建之法阅读笔记02
    第三周周总结
    四则运算 2
    构建之法阅读笔记01
    随机生成四则运算题目
    Node.js_express_服务器渲染页面 ejs
    BOM 浏览器对象模型_window.navigator
    Node.js_express_浏览器存储技术 Cookie(服务器将少量数据交于浏览器存储管理)
    BOM 浏览器对象模型_XMLHttpRequest 对象
  • 原文地址:https://www.cnblogs.com/yanhui007/p/12595510.html
Copyright © 2020-2023  润新知