What is AOP
AOP (aspect-orientedprogramming, aspect-oriented programming) can be said to complement and improve OOP (object-oriented programing, object-oriented programming). OOP introduces concepts such as encapsulation, inheritance, and polymorphism to create an object hierarchy that simulates a collection of public behavior. When we need to introduce public behavior to scattered objects, oop seems powerless. In other words,OOP allows you to define relationships from top to bottom, but it is not appropriate to define left-to-right relationships . such as logging capabilities. Log code is often spread horizontally across all object hierarchies, and has nothing to do with the core functionality of the objects it spreads to. This is true for other types of code, such as security, exception handling, and transparent persistence. This irrelevant code scattered around is called crosscutting (cross-cutting) code, and in OOP design, it leads to a lot of duplication of code, rather than the reuse of individual modules.
AOP, on the contrary, uses a technique called "crosscutting" that splits the encapsulated object interior and encapsulates public behavior that affects multiple classes into a reusable module, called "Aspect", which is the facet . The so-called "aspect", in a nutshell, is to encapsulate the logic or responsibility that is not related to the business, but for the common invocation of the business module, to reduce the duplication of code in the system, to reduce the coupling between modules, and to facilitate future operability and maintainability. AOP represents a horizontal relationship, if the "object" is a hollow cylinder, which encapsulates the properties and behavior of the object, then the aspect-oriented programming approach is like a razor that cuts through the hollow cylinders to get inside the message. The cut-off aspect is the so-called "facet". Then it hands the cut-off slices with a clever capture of the heavens, leaving no traces.
Using "crosscutting" techniques,AOP divides software systems into two parts: core concerns and crosscutting concerns. The main process of business process is the core concern, and the part that has little relation is the crosscutting concern. one feature of crosscutting concerns is that they often occur in many of the core concerns and are essentially similar everywhere. such as permission authentication, logging, transaction processing. The role of AOP is to separate the various concerns in the system, separating the core concerns from the crosscutting concerns. As Avanade's senior program architect Adam Magee says, the core idea of AOP is "separating the business logic in the application from the generic services that support it." ”
The implementation of AOP technology, mainly divided into two categories: first, the use of dynamic Agent technology, the use of interception of messages to decorate the message to replace the original object behavior, the second is the use of static weaving, the introduction of a specific syntax to create "aspects", so that the compiler can be woven into the relevant "aspects" The code.
AOP Usage Scenarios
AOP is used to encapsulate crosscutting concerns, which can be used in the following scenarios:
Authentication Permissions
Caching Cache
Context Passing content delivery
Error handling Errors Handling
Lazy Loading lazily loaded
Debugging commissioning
Logging, tracing, profiling and monitoring record tracking optimization calibration
Performance Optimization Performance optimization
Persistence persistence
Resource Pooling resource Pool
Synchronization synchronization
Transactions transactions
AOP Related Concepts
Aspect (Aspect): A focus of modularity, this focus implementation may be additional crosscutting multiple objects. Transaction management is a good example of crosscutting concerns in the Java EE application. The aspect is implemented with spring's advisor or interceptor.
Connection point (Joinpoint): an explicit point during program execution, such as a call to a method or a particular exception being thrown.
Notification (Advice): The action performed by the AOP framework at a specific connection point. Various types of notifications include "around", "before", and "throws" notifications. The notification type is discussed below. Many of the AOP frameworks, including spring, are notification models with interceptors, maintaining a chain of interceptors around the connection point. Four Advice:beforeadvice, Afteradvice, Throwadvice and dynamicintroductionadvice are defined in spring
Pointcut (Pointcut): Specifies a collection of connection points to which a notification will be raised. The AOP framework must allow developers to specify pointcuts: for example, using regular expressions. Spring defines the Pointcut interface for combining Methodmatcher and Classfilter, which can be clearly understood by name, and Methodmatcher is used to check whether the method of the target class can be applied to this notification. And Classfilter is used to check if pointcut should be applied to the target class.
Introduce (Introduction): Add a method or field to the class being notified. Spring allows the introduction of new interfaces to any notified object. For example, you can simplify caching by using an introduction that enables any object to implement the IsModified interface. To use introduction in spring, you can implement notifications through Delegatingintroductioninterceptor. Configure the interfaces to be implemented by the advice and proxy classes through Defaultintroductionadvisor
Target object: The object that contains the connection point. Also known as a notified or Proxied object. Pojo
AOP Proxy: An AOP framework-created object that contains notifications. In spring, an AOP proxy can be either a JDK dynamic agent or a cglib proxy.
Weaving (Weaving): Assembling aspects to create a notified object. This can be done at compile time (for example, with the ASPECTJ compiler), or at run time. Spring, like other pure Java AOP frameworks, completes weaving at run time.
How to use spring AOP
You can use spring AOP in a configuration file or programmatically.
The configuration can be done through an XML file, in about four ways:
1. Configure Proxyfactorybean, explicitly set advisors, advice, target, etc.
2. Configure Autoproxycreator, this way, or as before, using the defined bean, but from the container is actually a proxy object
3. Configure with <aop:config>
4. Configure by <aop:aspectj-autoproxy>, use ASPECTJ annotations to identify notifications and pointcuts
You can also use Spring AOP in a programmatic way using proxyfactory, and Proxyfactory provides a way to set the target object, advisor and other related configurations, and finally get the proxy object through the GetProxy () method
Examples of specific uses can be Google. omitted here
Generation of Spring AOP proxy objects
Spring provides two ways to generate proxy objects: Jdkproxy and Cglib, which are generated by aopproxyfactory depending on the configuration of the Advisedsupport object. The default policy is to use the JDK dynamic Agent technology if the target class is an interface, otherwise use Cglib to generate the proxy. Let's look at how Spring uses the JDK to generate proxy objects, and the generated code is placed in the Jdkdynamicaopproxy class, directly on the relevant code:
/** * <ol> * <li> get the proxy class to implement the interface, in addition to the advised object configured, will also add Springproxy, advised (opaque=false) * < li> check the interface above to get an interface that 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);}
That this is actually very clear, the note I have also written clearly, no longer repeat.
The problem is that the proxy object is generated, and how is the facet woven into it?
We know that Invocationhandler is the core of the JDK dynamic agent, and the method calls to the generated proxy objects are delegated to the Invocationhandler.invoke () method. And by Jdkdynamicaopproxy's signature we can see that this class actually implements the Invocationhandler, and below we analyze the Invoke () method implemented in this class to see how the 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.targetSource; Class targetclass = null; Object target = null; The try {//eqauls () method, with the target object, does not implement this method if (!this.equalsdefined && Aoputils.isequalsmethod (method)) { Return (Equals (Args[0])? Boolean.TRUE:Boolean.FALSE); }//hashcode () method, with the target object not implemented by this method if (!this.hashcodedefined && Aoputils.ishashcodemethod (method)) { Return Newinteger (Hashcode ()); The//advised interface, or the method defined in its parent interface, directly reflects the call without applying the notification 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; }//Get the target object's class target = Targetsource.gettarget (); if (target! = null) {Targetclass = Target.getclass (); }//Gets the list of interceptor that can be applied to this method lists chain = This.advised.getInterceptorsAndDynamicInterceptionAdvice (M Ethod,targetclass); If there is no notification (interceptor) that can be applied to this method, this direct reflection calls Method.invoke (target, args) if (Chain.isempty ()) {RetVal = Aoputils.invokejoinpointusingreflection (Target,method, args); } else {//create methodinvocation invocation = Newreflectivemethodinvocation (proxy, Target, meth OD, 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 ' an D 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 has come fromtarge TSource. Targetsource.releasetarget (target); } if (Setproxycontext) {//Restore old proxy. Aopcontext.setcurrentproxy (Oldproxy); } } }
The main process can be described as: Get a chain of notifications that can be applied to this method (Interceptor Chain), apply notifications if there is one, and execute joinpoint; If not, the direct reflection executes the joinpoint. The key here is how the notification chain is obtained and how it is executed, under one analysis.
First, as you can see from the code above, the notification chain is obtained through the Advised.getinterceptorsanddynamicinterceptionadvice () method, and we look at the implementation of this method:
Public List<object>getinterceptorsanddynamicinterceptionadvice (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 acquisition work is actually made by Advisorchainfactory. Getinterceptorsanddynamicinterceptionadvice () This method to complete, the obtained results will be cached.
The following is an analysis of the implementation of this method:
/** * Get the Advisor list from the provided configuration instance config and traverse through the advisors. If it's introductionadvisor, * Determines whether the advisor can be applied to the target class Targetclass. If it is pointcutadvisor, then judge * Whether this advisor can be applied to the target method. The advisor that meets the criteria is converted to the Interceptor list by Advisoradaptor. */Publiclist Getinterceptorsanddynamicinterceptionadvice (advised config, Methodmethod, Class targetclass) {//This is somewhat t Ricky. We have to process introductions first,//and we need to preserve order in the ultimate list. List interceptorlist = new ArrayList (config.getadvisors (). length); See if it contains introductionadvisor boolean hasintroductions = Hasmatchingintroductions (Config,targetclass); Here you actually register a series of Advisoradapter to convert the advisor to 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: The location of the two methods can be interchanged in this place. Convert Advisor to Interceptor methodinterceptor[]interceptors = Registry.getinterceptors (adviso R); Checks whether the pointcut of the current advisor can match the current method methodmatcher mm =pointcutadvisor.getpointcut (). Getmethodmatcher (); if (Methodmatchers.matches (Mm,method, Targetclass, hasintroductions)) {if (mm.is Runtime ()) {//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= re Gistry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); }} else {interceptor[] interceptors =registry.getinterceptors (advisor); Interceptorlist.addall (Arrays.aslist (interceptors)); }} return interceptorlist;}
When this method is completed, the advisor configured in advised to be applied to the connection point or target class is all converted to Methodinterceptor.
And then we'll see how the Interceptor chain works.
if (Chain.isempty ()) { RetVal = aoputils.invokejoinpointusingreflection (Target,method, args); } else { // Create methodinvocation invocation = newreflectivemethodinvocation (proxy, Target, method, args, Targetclass, chain); RetVal = Invocation.proceed (); }
As can be seen from this code, if the resulting interceptor chain is empty, then direct reflection calls the target method, otherwise create methodinvocation, call its proceed method, trigger the execution of the interceptor chain, to see 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 done, then Execute Joinpoint return invokejoinpoint (); } Object Interceptororinterceptionadvice = This.interceptorsAndDynamicMethodMatchers.get (++this.currentin Terceptorindex); If you want to dynamically match Joinpoint if (interceptororinterceptionadvice instanceof Interceptoranddynamicmethodmatcher) {// Evaluate Dynamic Method Matcher here:static part would already has//been evaluated and found to match. Interceptoranddynamicmethodmatcher DM = (interceptoranddynamicmethodmatcher) Interceptororinterceptiona Dvice; Dynamic match: Whether the run-time parameter satisfies the match condition if (dm.methodMatcher.matches (This.method, this.targetclass,this.arguments)) { Executes the current Intercetpor Returndm.interceptor.invoKe (this); } else {//If the dynamic match fails, skip the current Intercetpor and call the next interceptor return proceed (); }} else {//It ' s an interceptor, so we just invoke it:the Pointcutwill has//been Eva Luated statically before this object is constructed. Executes the current Intercetpor return ((methodinterceptor) interceptororinterceptionadvice). Invoke (this); }}
Spring AOP Implementation principles