Java-Analysis of the realization principle of Spring AOP from source code

Share a big cow's artificial intelligence tutorial. Zero-based! Easy to understand! Funny and humorous! Hope you join the artificial intelligence team too! Please click http://www.captainbed.net

What is AOP

AOP (Aspect-Oriented Programming) can be said to be the complement and improvement of OOP (Object-Oriented Programming). OOP introduces concepts such as encapsulation, inheritance, and polymorphism to establish a hierarchy of objects to simulate a collection of public behaviors. When we need to introduce public behaviors for scattered objects, OOP is powerless. In other words, OOP allows you to define a top-to-bottom relationship, but it is not suitable for defining a left-to-right relationship. For example, the log function. Log code is often distributed horizontally in all object levels, and has nothing to do with the core functions of the objects it is distributed to. The same is true for other types of code, such as security, exception handling, and transparent persistence. This kind of irrelevant code scattered everywhere is called cross-cutting code. In OOP design, it leads to a lot of code duplication, which is not conducive to the reuse of various modules.

The AOP technology is just the opposite. It uses a technology called "cross-cutting" to dissect the inside of the encapsulated object, and encapsulate those common behaviors that affect multiple classes into a reusable module, and name it. It is "Aspect", that is, the aspect. The so-called "aspect", simply put, is to encapsulate the logic or responsibilities that have nothing to do with the business but are called by the business modules, which is convenient for reducing the repetitive code of the system, reducing the coupling between modules, and is conducive to future development. Operability and maintainability. AOP represents a horizontal relationship. If the "object" is a hollow cylinder, which encapsulates the properties and behaviors of the object; then the aspect-oriented programming method is like a sharp edge, cutting these hollow cylinders Open to get the news inside. The cut surface is the so-called "cut surface". Then it restored these cut planes with ingenious skill, leaving no traces.

Using "cross-cutting" technology, AOP divides the software system into two parts: core concerns and cross-cutting concerns. The main process of business processing is the core concern, and the less relevant part is the crosscutting concern. A characteristic of crosscutting concerns is that they often occur in multiple places of the core concern, and they are basically similar everywhere. Such as authority authentication, log, transaction processing. The role of AOP is to separate the various concerns in the system and separate the core concerns from the cross-cutting concerns. The core idea of ​​AOP is to "separate the business logic in the application from the general services that support it."

The technology to achieve AOP is mainly divided into two categories: one is to use dynamic proxy technology, which uses intercepting messages to decorate the message to replace the execution of the original object behavior; the other is to use static weaving to introduce The specific syntax creates "aspects" so that the compiler can weave code about the "aspects" during compilation.

AOP usage scenarios

AOP is used to encapsulate cross-cutting concerns, which can be used in the following scenarios:

Authentication permissions

Caching

Context passing

Error handling

Lazy loading

Debugging

logging, tracing, profiling and monitoring

Performance optimization

Persistence

Resource pooling

Synchronization

Transactions

AOP related concepts

Aspect: The modularity of a focus, the realization of this focus may cross multiple objects. Transaction management is a good example of cross-cutting concerns in J2EE applications. The aspects are implemented with Spring's Advisor or interceptor.

Joinpoint: A clear point during program execution, such as a method call or a specific exception being thrown.

Advice: Actions performed by the AOP framework at a specific connection point. Various types of notifications include "around", "before" and "throws" notifications. Many AOP frameworks, including Spring, use interceptors as the notification model, maintaining a chain of interceptors "around" the connection point. Four advices are defined in Spring: BeforeAdvice, AfterAdvice, ThrowAdvice and DynamicIntroductionAdvice.

Pointcut: Specify a collection of connection points at which a notification will be triggered. The AOP framework must allow developers to specify entry points: for example, using regular expressions. Spring defines the Pointcut interface, which is used to combine MethodMatcher and ClassFilter, which can be clearly understood by the name. MethodMatcher is used to check whether the method of the target class can be applied to this notification, and ClassFilter is used to check whether the Pointcut should be applied to the target class on.

Introduction: Adding methods or fields to the notified class. Spring allows the introduction of new interfaces to any notified object. For example, you can use an import to make any object implement the IsModified interface to simplify caching. To use Introduction in Spring, you can implement notification through DelegatingIntroductionInterceptor, and configure the interface to be implemented by Advice and proxy classes through DefaultIntroductionAdvisor.

Target Object: The object that contains the connection point. Also called the notified or proxied object.

AOP Proxy: An object created by the AOP framework, including notifications. In Spring, the AOP proxy can be a JDK dynamic proxy or a CGLIB proxy.

Weaving: Assemble aspects to create a notified object. This can be done at compile time (for example using the AspectJ compiler) or it can be done at runtime. Spring, like other pure Java AOP frameworks, completes the weaving at runtime.

Spring AOP components

The following class diagram lists the main AOP components in Spring:

How to use Spring AOP

You can use Spring AOP through configuration files or programmatically.

The configuration can be done through the xml file, there are about four ways:

  1. Configure ProxyFactoryBean to explicitly set advisors, advice, target, etc.
  2. Configure AutoProxyCreator. In this way, you still use the defined bean as before, but what you get from the container is actually a proxy object.
  3. Configure via <aop:config>.
  4. Configure via <aop: aspectj-autoproxy>, and use AspectJ annotations to identify notifications and entry points.

You can also directly use the ProxyFactory to use Spring AOP programmatically. Through the methods provided by the ProxyFactory, you can set the target object, advisor and other related configurations, and finally get the proxy object through the getProxy() method.

Spring AOP proxy object generation

Spring provides two ways to generate proxy objects: JDKProxy and Cglib. Which method is used to generate it is determined by AopProxyFactory according to the configuration of the AdvisedSupport object. The default strategy is to use JDK dynamic proxy technology if the target class is an interface, otherwise use Cglib to generate a proxy. Let's study how Spring uses JDK to generate proxy objects. The specific generated code is placed in the JdkDynamicAopProxy class. The relevant code is as follows:

/**
    * <ol>
    * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false)
    * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口
    * <li>调用Proxy.newProxyInstance创建代理对象
    * </ol>
    */
   public Object getProxy(ClassLoader classLoader) {
       if (logger.isDebugEnabled()) {
           logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());
       }
       Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);
       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

The proxy object is generated, how are the aspects woven?

We know that InvocationHandler is the core of the JDK dynamic proxy, and the method invocation of the generated proxy object will be delegated to the InvocationHandler.invoke() method. And through the signature of JdkDynamicAopProxy, we can see that this class actually implements InvocationHandler. Next, we will analyze the invoke() method implemented in this class to see how Spring AOP is woven into the aspect.

publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {
       MethodInvocation invocation = null;
       Object oldProxy = null;
       boolean setProxyContext = false;
 
       TargetSource targetSource = this.advised.targetSource;
       Class targetClass = null;
       Object target = null;
 
       try {
           //eqauls()方法,具目标对象未实现此方法
           if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){
                return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);
           }
 
           //hashCode()方法,具目标对象未实现此方法
           if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){
                return newInteger(hashCode());
           }
 
           //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知
           if (!this.advised.opaque &&method.getDeclaringClass().isInterface()
                    &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations onProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);
           }
 
           Object retVal = null;
 
           if (this.advised.exposeProxy) {
                // Make invocation available ifnecessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
           }
 
           //获得目标对象的类
           target = targetSource.getTarget();
           if (target != null) {
                targetClass = target.getClass();
           }
 
           //获取可以应用到此方法上的Interceptor列表
           List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
 
           //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)
           if (chain.isEmpty()) {
                retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
           } else {
                //创建MethodInvocation
                invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();
           }
 
           // Massage return value if necessary.
           if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)
                    &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned"this" and the return type of the method
                // is type-compatible. Notethat we can't help if the target sets
                // a reference to itself inanother returned object.
                retVal = proxy;
           }
           return retVal;
       } finally {
           if (target != null && !targetSource.isStatic()) {
                // Must have come fromTargetSource.
               targetSource.releaseTarget(target);
           }
           if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
           }
       }
    }

The main process can be briefly described as: Get the Interceptor Chain that can be applied to this method, if there is, apply the notification, and execute the joinpoint; if not, then directly reflect the execution of the joinpoint. The key here is how the notification chain is obtained and how it is executed, which are analyzed one by one below.

First of all, as can be seen from the above code, the notification chain is obtained through the Advised.getInterceptorsAndDynamicInterceptionAdvice() method. Let's look at the implementation of this method:

public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
                   MethodCacheKeycacheKey = new MethodCacheKey(method);
                   List<Object>cached = this.methodCache.get(cacheKey);
                   if(cached == null) {
                            cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                                               this,method, targetClass);
                            this.methodCache.put(cacheKey,cached);
                   }
                   returncached;
         }

You can see that the actual fetching work is actually done by the AdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice() method, and the fetched results will be cached.

Let's analyze the implementation of this method:

/**
    * 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,
    * 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断
    * 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.
    */
    publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {
       // This is somewhat tricky... we have to process introductions first,
       // but we need to preserve order in the ultimate list.
       List interceptorList = new ArrayList(config.getAdvisors().length);
 
       //查看是否包含IntroductionAdvisor
       boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);
 
       //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor
       AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
 
       Advisor[] advisors = config.getAdvisors();
        for (int i = 0; i <advisors.length; i++) {
           Advisor advisor = advisors[i];
           if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;
                if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                    //TODO: 这个地方这两个方法的位置可以互换下
                    //将Advisor转化成Interceptor
                    MethodInterceptor[]interceptors = registry.getInterceptors(advisor);
 
                    //检查当前advisor的pointcut是否可以匹配当前方法
                    MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();
 
                    if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {
                        if(mm.isRuntime()) {
                            // Creating a newobject instance in the getInterceptors() method
                            // isn't a problemas we normally cache created chains.
                            for (intj = 0; j < interceptors.length; j++) {
                               interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));
                            }
                        } else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
           } else if (advisor instanceof IntroductionAdvisor){
                IntroductionAdvisor ia =(IntroductionAdvisor) advisor;
                if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
                    Interceptor[] interceptors= registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
           } else {
                Interceptor[] interceptors =registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
           }
       }
       return interceptorList;
}

After the execution of this method is completed, the Advisor configured in the Advised that can be applied to the connection point or target class is all converted into a MethodInterceptor.

Next, let's look at how the interceptor chain we get works.

if (chain.isEmpty()) {
                retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
            } else {
                //创建MethodInvocation
                invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();
            }

It can be seen from this code that if the interceptor chain obtained is empty, the target method is directly invoked by reflection, otherwise a MethodInvocation is created, its proceed method is invoked, and the execution of the interceptor chain is triggered. Let’s look at the specific code:

public Object proceed() throws Throwable {
       //  We start with an index of -1and increment early.
       if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {
           //如果Interceptor执行完了,则执行joinPoint
           return invokeJoinpoint();
       }
 
       Object interceptorOrInterceptionAdvice =
           this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
       
       //如果要动态匹配joinPoint
       if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){
           // Evaluate dynamic method matcher here: static part will already have
           // been evaluated and found to match.
           InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
           //动态匹配:运行时参数是否满足匹配条件
           if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {
                //执行当前Intercetpor
                returndm.interceptor.invoke(this);
           }
           else {
                //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor
                return proceed();
           }
       }
       else {
           // It's an interceptor, so we just invoke it: The pointcutwill have
           // been evaluated statically before this object was constructed.
           //执行当前Intercetpor
           return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
       }
}

This is the execution process of the interceptor chain.

Guess you like

Origin blog.csdn.net/chimomo/article/details/110875796