Java framework --- spring AOP implementation principle --- spring AOP
What is AOP?
AOP (Aspect-OrientedProgramming) can be said to be a supplement and perfection of OOP (Object-Oriented Programing, object-oriented programming. OOP introduces concepts such as encapsulation, inheritance, and Polymorphism to establish an object hierarchy to simulate a set of public behaviors. When we need to introduce public behavior to scattered objects, OOP seems powerless. That is to say, OOP allows you to define the relationship from top to bottom, but it is not suitable for defining the relationship from left to right. For example, log function. Log Code is often horizontally distributed across all object layers, but it has nothing to do with the core functions of the objects it spreads. This is also true for other types of code, such as security, exception handling, and transparent persistence. This unrelated code distributed everywhere is called cross-cutting code. in OOP design, it leads to a large number of code duplication, which is not conducive to the reuse of each module.
The AOP technology is the opposite. It uses a technology called "cross-cutting" to segment the encapsulated object, encapsulate the public behaviors that affect multiple classes into a reusable module and name it "Aspect", that is, Aspect. The so-called "aspect" is simply to encapsulate the logic or responsibilities that are irrelevant to the business but are called by the business module to reduce repeated system code, reduce the coupling between modules and facilitate future operability and maintainability. AOP represents a horizontal relationship. If "object" is a hollow cylinder that encapsulates the attributes and behaviors of objects, then the method for Aspect-Oriented Programming is, it is like a sharp blade that splits these hollow cylinders to obtain internal messages. The cut section is the so-called "aspect. Then it restored these cut sections with the skill of winning the power of heaven without leaving any trace.
Using the "cross-cutting" technology, AOP divides the software system into two parts: the core focus and the cross-cutting focus. The main process of business processing is the core focus, and the part that has little to do with it is the cross-cutting focus. One feature of cross-cutting concerns is that they often occur in multiple places of core concerns, and are similar everywhere. For example, permission authentication, logs, and transaction processing. The function of Aop is to separate the various concerns in the system from the core concerns and the cross-cutting concerns. As Adam Magee, Avanade's senior Solution Architect, said, the core idea of AOP is to "separate the business logic in applications from the general services it supports ."
The technology for Implementing AOP is mainly divided into two categories: one is to use dynamic proxy technology, and use the method of intercepting messages to describe the message to replace the execution of the original object behavior; the second is to use static weaving to introduce specific syntax to create "aspect", so that the compiler can weave "aspect" code during compilation.
Use Cases of AOP
AOP is used to encapsulate cross-cutting concerns. It can be used in the following scenarios:
Authentication permission
Caching Cache
Context passing content transmission
Error handling
Lazy loading
Debugging
Logging, tracing, profiling and monitoring record tracking optimization Calibration
Performance optimization
Persistence
Resource pooling Resource pool
Synchronization
Transactions transaction
AOP concepts
Aspect (Aspect): modularization of a focus, which may cause cross-cutting of multiple objects. Transaction Management is a good example of cross-cutting concerns in J2EE applications. With the Spring Advisor or interceptor.
Joinpoint: a specific point in the execution process, such as a call to a method or a specific exception, is thrown.
Advice: The action performed by the AOP framework at a specific connection point. Various types of notifications include "around", "before", and "throws. The notification type is discussed below. Many AOP frameworks, including Spring, use the interceptor as the notification model to maintain an interceptor chain around the connection points. Spring defines four advice: BeforeAdvice, AfterAdvice, ThrowAdvice and DynamicIntroductionAdvice
Pointcut: Specifies a set of connection points that a notification will be triggered. The AOP framework must allow developers to specify the entry point, for example, using a regular expression. Spring defines the Pointcut interface, which is used to combine MethodMatcher and ClassFilter. It can be clearly understood through the name. MethodMatcher is used to check whether the method of the target class can be applied with this notification, classFilter is used to check whether Pointcut should be applied to the target class.
Introduction: adds a method or field to the notified class. Spring allows the introduction of new interfaces to any notified objects. For example, you can use an introduction to implement the IsModified interface for any object to simplify the cache. Use Introduction in Spring. You can use DelegatingIntroductionInterceptor to implement notifications, and use DefaultIntroductionAdvisor to configure Advice and the interface to be implemented by the proxy class.
Target Object: The Object that contains the connection point. It is also called a notification or proxy object. POJO
AOP Proxy: the object created by the AOP framework, including notifications. In Spring, the AOP proxy can be JDK dynamic proxy or CGLIB proxy.
Weaving: Assemble to create a notification object. This can be completed during compilation (for example, using the AspectJ compiler) or at runtime. Like other pure Java AOP frameworks, Spring completes weaving at runtime.
How to Use Spring AOP
You can use Spring AOP through configuration files or programming.
The configuration can be performed through an xml file. There are four methods:
1. Configure ProxyFactoryBean and explicitly set advisors, advice, and target.
2. Configure AutoProxyCreator. In this way, the defined bean is used as before, but what is obtained from the container is actually a proxy object.
3. Use <aop: config> to configure
4. Configure through <aop: aspectj-autoproxy> and use the AspectJ annotation to identify the notification and entry point
You can also use ProxyFactory to program Spring AOP. You can set the target object, advisor, and other related configurations through the method provided by ProxyFactory, and finally get the proxy object through the getProxy () method.
For more information, see google.
Generation of Spring AOP proxy objects
Spring provides two methods to generate proxy objects: JDKProxy and Cglib. The specific method used to generate the proxy objects is determined by AopProxyFactory Based on the configuration of the AdvisedSupport object. The default policy is to use JDK dynamic proxy technology if the target class is an interface. Otherwise, Cglib is used to generate a proxy. Next, let's take a look at how Spring uses JDK to generate proxy objects. The specific generated code is put in the JdkDynamicAopProxy class and the relevant code is directly used:
/*** <Ol> * <li> obtain the interface to be implemented by the proxy class. Besides the interfaces configured in the Advised object, SpringProxy and Advised (opaque = false) are added) * <li> check whether the preceding interface defines equals or hashcode * <li> call Proxy. newProxyInstance create proxy Object * </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 );}
This is actually very clear. I have already made it clear in the comments and will not repeat it again.
The following question is: how is the cut surface woven when a proxy object is generated?
We know that InvocationHandler is the core of JDK dynamic proxy, and the method calls of the generated proxy object will be delegated to the InvocationHandler. invoke () method. Through the JdkDynamicAopProxy signature, we can see that this class actually implements InvocationHandler. Next we will analyze the invoke () implemented in this class () method To see how Spring AOP is woven into the plane.
PublicObject invoke (Object proxy, Method method, Object [] args) throwsThrowable {MethodInvocation invocation = null; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.tar getSource; class targetClass = null; Object target = null; try {// eqauls () method. if (! This. inclusdefined & AopUtils. is%smethod (method) {return (equals (args [0])? Boolean. TRUE: Boolean. FALSE);} // hashCode () method. The target object does not implement this method if (! This. hashCodeDefined & AopUtils. isHashCodeMethod (method) {return newInteger (hashCode ();} // the method defined in the Advised interface or its parent interface is directly reflected and called. 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 (p Roxy); setProxyContext = true;} // obtain the target object class target = targetSource. getTarget (); if (target! = Null) {targetClass = target. getClass () ;}// obtain the List of Interceptor List chains that can be applied to this method = this. advised. getInterceptorsAndDynamicInterceptionAdvice (method, targetClass); // if no notification can be applied to this method (Interceptor), this directly reflects the call method. invoke (target, args) if (chain. isEmpty () {retVal = AopUtils. invokeJoinpointUsingReflection (target, method, args);} else {// create MethodInvocation invocation = newReflectiveMethodInvocation (pro Xy, 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 yes, apply the notification and execute joinpoint. If no, then joinpoint is directly reflected. The key here is how the notification chain is obtained and how it is executed. Next we will analyze it one by one.
First, from the code above, we can see that 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; }
We can see that the actual acquisition is actually completed by the AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice () method, and the obtained results will be cached.
The following describes the implementation of this method:
/*** Obtain the advisor list from the provided configuration instance config and traverse and process the advisor. if it is IntroductionAdvisor, * determines whether the Advisor can be applied to the target class targetClass. if it is PointcutAdvisor, determine whether * This Advisor can be applied to the target method. convert the qualified Advisor into an Interceptor list through AdvisorAdaptor. */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); // check whether IntroductionAdvisor boolean hasIntroductions ctions = contains (config, targetClass); // a series of advisoradapters are actually registered here to convert the Advisor into MethodInterceptor AdvisorAdapterRegistry = 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: The locations of these two methods can be swapped. // The Advisor can be converted to Interceptor MethodInterceptor [] interceptors = registry. getInterceptors (advisor); // check whether the pointcut of the current advisor matches the current method 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 this method is executed, the Advisor that can be applied to the connection point or target class in the Advised is converted into MethodInterceptor.
Next, let's look at how the interceptor chain works.
If (chain. isEmpty () {retVal = AopUtils. invokeJoinpointUsingReflection (target, method, args);} else {// create MethodInvocation invocation = invoke (proxy, target, method, args, targetClass, chain); retVal = invocation. proceed ();}
From this code, we can see that if the interceptor chain is empty, we will directly call the target method. Otherwise, we will create MethodInvocation, call its proceed method, and trigger the execution of the interceptor chain. Let's take a 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) {// If Interceptor is finished, run joinPoint return invokeJoinpoint ();} Object interceptorOrInterceptionAdvice = this. interceptorsAndDynamicMethodMatchers. get (++ this. currentInterceptorIndex); // if You Want to dynamically match joinPoint if (specified instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. interceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; // dynamic match: whether the runtime parameter meets the matching condition if (dm. methodMatcher. matches (this. method, this.tar getClass, this. arguments) {// execute the current Intercetpor returndm. interceptor. invoke (this);} when the else {// dynamic match fails, It skips the current Intercetpor and calls the next Interceptor return proceed () ;}} else {/It's an interceptor, so we just invoke it: The pointcutwill have // been evaluated statically before this object was constructed. // execute the current Intercetpor return (MethodInterceptor) interceptorOrInterceptionAdvice ). invoke (this );}}