Analysis of Springboot source agency three tricks

Summary:

In the Springversion of the change process, notes there have been many changes, but the design agency has also undergone a subtle change from Spring1.xthe ProxyFactoryBeanhard-coded island Spring2.xof Aspectjnotes, and finally to an automatic proxy now widely known.

file

Description:

  • ProxyConfigAgent configuration class
  • AdvisedSupportAchieved Advised, the packaging of Adviceand Advisoroperation
  • ProxyCreatorSupportThe main class and its subclasses is to use the factory to help create a proxy jdkor cglibproxy object
  • ProxyProcessorSupportThis class and its subclasses with what we currently have to do more, the use of post-processor for automatic proxy processing

ProxyFactoryBean

    package com.github.dqqzj.springboot.aop;
    
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.aop.TargetSource;
    import org.springframework.aop.framework.ProxyFactoryBean;
    import org.springframework.aop.target.SingletonTargetSource;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * @author qinzhongjian
     * @date created in 2019-08-24 11:05
     * @description: TODO
     * @since JDK 1.8.0_212-b10
     */
    @Component
    public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            if (!method.getName().equals("toString")) {
                System.out.println(target.getClass().getName() + "#" + method.getName());
            }
        }
        /**
         * 代理的目标对象  效果同setTargetSource(@Nullable TargetSource targetSource)
         * TargetSource targetSource = new SingletonTargetSource(aopService);
         * 可以从容器获取,也可以类似下面这样直接new,使用区别需要熟悉spring机制。
         * factoryBean.setTarget(new AopService());
         *
         * 设置需要被代理的接口  效果同factoryBean.setProxyInterfaces(new Class[]{AopService.class});
         * 若没有实现接口,那就会采用cglib去代理
         * 如果有接口不指定的话会代理所有的接口,否则代理指定的接口
         *
         *  setInterceptorNames方法源代码中有这样的一句话:Set the list of Advice/Advisor bean names. This must always be set
         *  to use this factory bean in a bean factory.
         */
        @Bean
        public ProxyFactoryBean proxyFactoryBean(AopService aopService) {
            ProxyFactoryBean factoryBean = new ProxyFactoryBean();
            factoryBean.setTarget(aopService);
            //factoryBean.setInterfaces(AopService.class);
    
            factoryBean.setInterceptorNames("myMethodBeforeAdvice");
            //是否强制使用cglib,默认是false的
            //factoryBean.setProxyTargetClass(true);
            return factoryBean;
        }
    
    }

file

Source analysis:

        @Override
        @Nullable
        public Object getObject() throws BeansException {
            //根据我们配置的interceptorNames来获取对应的Advisor并加入通知器执行链中
            initializeAdvisorChain();
            if (isSingleton()) {
                //生成singleton的代理对象,会利用DefaultAopProxyFactory去生成代理
          //在内部如果你手动没有去设置需要被代理的接口,Spring会代理你所有的实现接口。
                return getSingletonInstance();
            }
            else {
                if (this.targetName == null) {
                    logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                            "Enable prototype proxies by setting the 'targetName' property.");
                }
          //和单利非常类似 只不过没有缓存了
                return newPrototypeInstance();
            }
        }
        private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
            if (this.advisorChainInitialized) {
                return;
            }
            if (!ObjectUtils.isEmpty(this.interceptorNames)) {
                // 最后一个不能是全局的suffix *,除非我们指定了targetSource之类的
                if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                        this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                    throw new AopConfigException("Target required after globals");
                }
                for (String name : this.interceptorNames) {
                    // 如国拦截器的名称是以*结尾的,说明它要去全局里面都搜索出来
                    // 全局:去自己容器以及父容器中找,类型为Advisor.class的,名称是以这个名称为开头的prefix的Bean.
                    if (name.endsWith(GLOBAL_SUFFIX)) {
                        addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                                name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                    }
                    // 一般的情况下我们都是精确匹配
                    else {
                        Object advice;
                        if (this.singleton || this.beanFactory.isSingleton(name)) {
                            // 从容器里获取该bean
                            advice = this.beanFactory.getBean(name);
                        }
                        // 原型处理
                        else {
                            advice = new PrototypePlaceholderAdvisor(name);
                        }
                        addAdvisorOnChainCreation(advice, name);
                    }
                }
            }
            this.advisorChainInitialized = true;
        }
      // 将advice对象添加到通知器链中
        private void addAdvisorOnChainCreation(Object next, String name) {
            // 这里调用namedBeanToAdvisor做了一下适配:成统一的Advisor 
            Advisor advisor = namedBeanToAdvisor(next);
            addAdvisor(advisor);
        }
    //方法中首先会调用namedBeanToAdvisor(next)方法,将从ioc容器获取的普通对象转换成通知器Advisor对象
        private Advisor namedBeanToAdvisor(Object next) {
            try {
                return this.advisorAdapterRegistry.wrap(next);
            }
        }

DefaultAdvisorAdapterRegistry

file

This class also allows us to customize the adapter, and then registered to the inside on the line.

      @Override
        public void registerAdvisorAdapter(AdvisorAdapter adapter) {
            this.adapters.add(adapter);
        }

IoC containers from ProxyFactoryBean

file

ProxyFactory

file

Description: This class is usually springtheir own internal use our custom, then it is difficult to integrate with the container, which are generally returned prototype model agency

AspectJProxyFactory

file

summary:

根据以上案例可以发现 都是首先进行AdvisedSupport的准备,然后交给子类ProxyCreatorSupport根据条件
得到JDK或者CGLIB的AopProxy,当代理对象被调用的时候在invoke或者intercept方法中会调用ProxyCreatorSupport的getInterceptorsAndDynamicInterceptionAdvice方法去初始化advice和各个方法之间的映射关系并缓存
Acting same method does not take effect reason?

Many times agents will find methods and non-proxy method calls and call order does not take effect relationship in the same class, we refactor the code to analyze the reasons

    public class AspectJProxyFactoryApplication {
        public static void main(String[] args) {
            AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new AopService());
            // 注意:此处得MyAspect类上面的@Aspect注解必不可少
            proxyFactory.addAspect(MyAspect.class);
            //proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
            AopService proxy = proxyFactory.getProxy();
            proxy.test();
        }
    }
    @Aspect
    public class MyAspect {
        //@Pointcut("execution(* com.github..aop.*.*(..))")
        @Pointcut("execution(* com.github..aop.AopService.hello(..))")
        private void pointcut() {
        }
    
        @Before("pointcut()")
        public void before() {
            System.out.println("-----------MyAspect#before-----------");
        }
    }
    @Service
    public class AopService {
        public String hello() {
            System.out.println("hello, AopService");
            return "hello, AopService";
        }
        public String test() {
            System.out.println("test");
            return hello();
        }
    }

The answer is no effect, what caused it? In fact, the last point of my knowledge the above summary.

file

This time chainwithout our notice in the inside,
file

file

According to our final program execution, to modify the expression below the cut point, if the above example to see if the following advice can be ignored, that is, whether the main enhancement is the first entry function can match the cut-off point on our subsequent expression the simply will not care whether you can match on.

    @Aspect
    public class MyAspect {
        @Pointcut("execution(* com.github..aop.*.*(..))")
        //@Pointcut("execution(* com.github..aop.AopService.hello(..))")
        private void pointcut() {
        }
    
        @Before("pointcut()")
        public void before() {
            System.out.println("-----------MyAspect#before-----------");
        }
    }

file

file

After the process will follow the normal code for executing the process

if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
   return invokeJoinpoint();
}

If the method calls of the same class all want notifier to take effect how to do? This notice must be added to make the execution chain for the job, according to the above stated content can achieve this goal.

Guess you like

Origin www.cnblogs.com/qinzj/p/11404858.html