Teach you to write an AOP framework

Source: Internet
Author: User
Tags throwable

 

Why AOP?

 

AOP (aspect-oriented programming), which means that the polygon is programmed on the plane. The traditional OOP object is equivalent to standing in a god mode from the top down, the inside of a block is an object, by any combination of me, and AOP is different is that he is a spectator shenfa, from the "side" to see the entire system module, See where you can jianfengchazhen the logic of the obligation you want to deal with.


code duplication is the ultimate code smell. It's a sign, that something was very wrong with implementation or design. (Duplicate code makes the quality of the code very bad.) If this happens, it must be the implementation or the design environment is out of the question)


OOP itself is strongly opposed to "reinvent the wheel ", but sometimes it seems helpless to duplicate code, and AOP itself is a good idea to solve the problem.

Abstract for half a day, or use an example to explain it more vividly. If you want to make a permission system, then you must add a permission judgment before a lot of business logic-only the ones that meet the criteria will be able to do the following. If you take advantage of traditional thinking, it is clear that you will encapsulate the business logic that makes authority judgments, and then execute the following code that handles permission judgments before each business logic executes. such as :

See no, every time a judge each time a judgment, if let these permissions to judge the code scattered in all corners of the system, it would be a nightmare! Even with OOP , it is useless to put the business of permission checking in a class. Because there is always an indelible figure (Dosecuritycheck) at the beginning of each business code.

At this point, theAOP brother finally hit the stand, to play the game! The man immediately said, put the business logic code, I will handle!

He first regards the portion of the permission processing as a aspect(tangent) and then tries to weave(weave) The facets into the appropriate position in the business logic at run time. For example, like this:

In this way,AOP succeeded in helping me to insert the Permission Validation section into the calling code before executing it. Specifically called which method in fact AOP does not know, as long as you weave the plane into the user login, then call the user login, as long as you weave into the user query, then call the user query. And it's not just a way to drop a method, it can be called next to the row.

This is just one of the great uses, and there are places where you can use AOP like logging, performance analysis, transaction processing, and more.

Think of AOP as complementing, not competing with, OOP. AOP can supplement OOP where it is weak. (AOP has no competitive relationship with OOP, but AOP can be a good complement to OOP.)

AOPBasic terminology

 

L Aspect: Is the code snippet you want to weave into the program, such as permission handling, logging, and so on.

L Weaving (Weaving): is the process of adding additional business logic to the specified program, such as inserting permission validation into the user's login process.

l Advice (Notification): Indicates where the program is woven into the plane, such as the front of the weave, or the back of the weave, or throw an exception when weaving.

L Joinpoint (Connection point): Represents the target method of the object that the program is woven into, that is, the target of the agent.

L Pointcut (pointcut): Indicates which programs are woven into slices, which are a collection of connection points, such as user login and user queries, which need to be woven into.

To make it easier for users to use AOP, several types of notifications need to be defined.

L Before: Pre-notification, notify before business logic

L After: Notification after the normal end of business logic

L End: Notification will be executed at the end of the notification, regardless of whether the business logic is completed properly

L Error: Notify when the business logic throws an exception

 

AOPExecution process

 

Demonstrates the AOP core invocation process by invoking the AOP proxy class, starting a call (front) notification / interceptor chain after completion, and then calling (back, end) notification when the target method is called and finally comes back / Interceptor chain.


This completes the process of adding a piece of business logic to a program (the target method) in AOP , followed by a sequence of business logic, and with great lethality, the scope of the target method can be arbitrarily controlled.

 

Hands-on implementation of aAOP

 

The foreplay is so long, the climax will not be short! The AOP , which was written this time, referenced a lot of Spring code, absorbing the nutrients that the master added.

Using the principle of test-driven development, let's consider how we will use (write good test code), then think about how the API is designed (to write the interface well), and finally consider the implementation of the problem.

@Testpublic void Testtranscation () {//Creates a dynamic proxy factory, which is the initial point of invoking the dynamic proxy implementation AOP Aopproxyfactory proxy = new Aopproxyfactory ();// Create the target object Proxy.settarget (new Aopdemo ());//Set individual advice so that these adviceproxy.addadvice can occur when the specified method of the target object is called (new Transactionaspect ());//Gets the proxy object Iaopdemo p = (iaopdemo) proxy.getproxy ();//invokes the specified method of the target object by proxy object P.dosomething ();}


Referring to the API design of Spring , we give the above test code. This allows the AOP Agent factory to be configured with target objects, and a wide variety of notifications, currently only transaction-processing notifications. It then obtains the proxy object of the target object through the agent factory and completes the type conversion process. Finally, the specified method is called to complete the transaction process around this method.

To set the target object's method Needless to say, is the fancy of that object, set in, so that can get a proxy to help him do ...

Add the implementation of the notification, I want to design a little more useful. What does it mean to be useful? That is to give you some interface,before, after,Error,End , etc., as long as you define the aspect class (tangent) to implement any one interface, can be guaranteed to execute as shown in the name of the interface. For example, I implemented the before interface and his abstract method, and added a "log" function in it, so that I can later in my target method to complete a log before the process of logging. Here we are using the aspect facets of the transaction processing:

@Component @match (methodmatch = "org.*.dosomething") public class Transactionaspect implements before, End, Error {@ Overridepublic void Error (method, object[] args, Exception e) {System.out.println ("rollback TRANSACTION");} @Overridepublic void Before (method method, object[] args) {System.out.println ("open Transaction");} @Overridepublic void End (method, object[] args, Object retVal) {System.out.println ("close Transaction");}}


In the agent factory, we use object target to store this target object and use the collection to record all the notifications involved in that class.

Private Object target;private list<advice> advicelist = new arraylist<advice> ();/** *  * @Title: Settarget * @Description: Set Target * @param @param target settings file * @return void return type * @throws */public void settarget (Object ta Rget) {this.target = target;} public void Addadvice (Advice Advice) {if (Advice = = null) throw new NullPointerException ("added notification cannot be empty"); Advicelist.add ( Advice);}

After you finish configuring the Agent factory, you need to get the proxy object through this agent factory. Before we get the proxy object, we initialize the previously completed configuration (the target method, the notification collection) to the Advisedsupport object, passing the object as a whole to the subsequent proxy implementations (jdk,cglib Completes the initialization of the proxy class, as well as the notification and invocation of the target method.

public Object GetProxy () {if (target = = null) throw new NullPointerException ("target object cannot be empty"); Advisedsupport config=new advisedsupport (target, advicelist); Aopproxy Proxy = null;//If the target object implements the interface, the JDK dynamic agent is preferred, and if no interface is implemented, only the Cglib dynamic agent can be used; if (Config.hasinterfaces ()) {Logger.info ("Using JDK dynamic Agent");p roxy = new Jdkaopproxy (config);} else {logger.info ("with cglib dynamic proxy");p roxy = new Cglibaopproxy (config);} return Proxy.getproxy ();}


Here we will judge according to different circumstances whether he chooses the JDK dynamic agent or the Cglib dynamic agent.

Here, for example, the JDK dynamic agent, Cglib is left to the reader to analyze itself:


public class Jdkaopproxy implements Aopproxy, Invocationhandler {private Advisedsupport config;public jdkaopproxy ( Advisedsupport config) {this.config = config;} @Overridepublic Object GetProxy () {return proxy.newproxyinstance (Config.getclassloader (), config.getinterfaces (), this);} @Overridepublic object Invoke (Object proxy, Method method, object[] args) throws Throwable {return new REFLECTIVEMETHODINV Ocation (Config.getinterceptors (), Config.getmatchers (), Args,method, Config.gettarget ()). proceed ();}}



Here we first initialize the proxy class in GetProxy , and then when the method of the proxy class is called, the target method call is completed, and this step is done by the reflectivemethodinvocation object. This class implements the methodinvocationin order to complete the subsequent callback process, as can be seen later. In this reflectivemethodinvocation class, we store enough information.

/** * Notify advice */private list<methodinterceptor> chain;/** * Matching information configured for each advice */private list<matcher> matches;/** * Parameters required for the execution of the target method */private object[] arguments;/** * Target methods */private method method;/** * Target object */private Object Targ et;/** * Index of the method to be performed to record the current advice chain (chain) */private int index;



The purpose is simple: it is to integrate multiple notification chains and the method of the target object itself, to form a logical perfect chain--the front-end notification in front of the target method is lined up, if the target method throws an exception to execute the error notification, once the normal execution of the target method to execute the post-notification, A notification is executed at the end of the notification, whether it is a normal execution of the target method or an exception is thrown.

Here's a look at the core approach to handling a series of method calls in this class Proceed ():

@Overridepublic Object Proceed () throws Throwable {//When the chain is finished, call the target method if (index = = chain.size ()) return Invokejoinpoint (); Matcher Matcher = matches.get (index);//view matches, if (Matcher.matches (This.method, This.target.getClass ())) {return Chain.get (index++). Invoke (this);} else {Index++;return proceed ();}} /** *  * @Title: Invokejoinpoint * @Description: Call connection Point method * @param @return * @param @throws throwable settings file * @return OBJ ECT return type * @throws */protected Object invokejoinpoint () throws Throwable {return Method.invoke (target, arguments);}



It is simple to call the target method when the chain is finished. Otherwise, point to the method on the chain. Here is a procedure for detecting whether a match is being made, that is, the slice class that I gave to me, that is, the slice of the transaction transactionaspect configured with an annotation match, which indicates that when the target class is a class under the Org package, I'm going to intercept his dosomething method and add a transaction around this method.

@Component @match (methodmatch = "org.*.dosomething") public class Transactionaspect implements before, End, Error {@ Overridepublic void Error (method, object[] args, Exception e) {System.out.println ("rollback TRANSACTION");} @Overridepublic void Before (method method, object[] args) {System.out.println ("open Transaction");} @Overridepublic void End (method, object[] args, Object retVal) {System.out.println ("close Transaction");}}



If the specific judgment matches, I am using a regular expression, that is, when a method of the target class is called, once the regular expression that matches the Methodmatch configuration is detected, the specified logic is added before and after the method. If a mismatch is found, this continues to look for the next notification on the chain until the entire notification chain is completed.

There is certainly a problem here: in the chain to get a notice, when the notification is executed, how to ensure that the front-end notification is the previous execution, the post-notification is back to execute it? and ensure that the subsequent notification invocation process is executed after the call is completed?

In fact, Spring uses a very ingenious programming technique here, which is handled by the polymorphic principle and the callback function.

Chain.get (index++). Invoke (this);



Here we get the index notice and get an interface type, but the implementation class betrays his nature, indicating whether it is pre-or post-or otherwise. When the invoke is executed, a reference to the object of the methodinvocation implementation class Reflectivemethodinvocation is passed in. The method that is called so is actually the following :

Can be found, when the implementation of the class is a post-notification, I will choose Afterinterceptor to execute, when the pre-notification, will choose Beforeinterceptor to execute. That is, when the appropriate notification is encountered, the appropriate interceptor is used for processing.

An example of a method interceptor for a previously placed notification:

public class Beforeinterceptor implements Methodinterceptor {private before Advice;public beforeinterceptor (before Advice) {This.advice =advice;} @Overridepublic Object Invoke (methodinvocation mi) throws Throwable {Advice.before (Mi.getmethod (), mi.getarguments ()) ; return Mi.proceed ();}}



See here is a sense of déjà vu, this is not the beginning of the authority to verify the replica? Yes, it is. It is very clear here that the business logic in before is executed before the target method call, and then the Mi.proceed () is recalled to our methodinvocation implementation class . the proceed method in Reflectivemethodinvocation.

In this way, the chain is guaranteed to be executed in order.

Take a look at the output of our test case :

Open the transaction and haha ha ... Close transaction



It is not yet possible to integrate IOC and AOP, and there is no mention of how the IOC can be used in the Web MVC framework, but these will continue to emerge in our future blogs.

Source

Finally, release the source code: https://github.com/mjaow/my_spring

Teach you to write an AOP framework

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.