This article is about a difficult problem in Java. By using the Java core library to implement simple AOP methods and analyze and compare the instance code. The following is all content:
Spring is a very popular open source framework, and AOP (sectional programming) is one of the most important concepts of Spring. In order to better understand and learn AOP's ideas, using the core library to achieve it at a time is a good way.
First, let’s introduce the concept of AOP. AOP (Aspect Oriented Programming), that is, tangential-oriented programming. The so-called tangential-oriented programming is the idea of designing code from the perspective of a cross-sectional area. The traditional OOP idea is to use encapsulation inheritance and polymorphism to construct a vertical hierarchical relationship, but it is not suitable to define horizontal relationships. AOP idea provides a good supplement to this.
For example, log management code is often horizontally scattered in many object levels, but it has nothing to do with the core functions of the corresponding objects. There are also many similar codes, such as permission verification, debug output, transaction processing, etc., which are also the same. This is not conducive to code reuse and management.
At this time, AOP technology came into being. It uses "crosscutting" technology to penetrate deep into the encapsulation object, encapsulate the common behaviors that affect multiple classes into a reusable module, and name it "Aspect", that is, the slicing. The so-called "section" is simply encapsulated by logic or responsibilities that are not related to the business but are called jointly by the business module, which is convenient for reducing the system's duplicate code, reducing the coupling between modules, and conducive to subsequent operability and maintainability.
So how is AOP implemented?
The answer is dynamic proxy (there will be another chapter on the proxy for details, so I won’t go into details here). There are two ways to implement dynamic proxy, one is JDK dynamic proxy, and the other is CGLib dynamic proxy.
Then use two methods to make a simple chestnut.
Let’s design a scenario first, suppose we have a computing interface ICalculator and a calculator class CalculatorImpl that implements this interface.
public interface ICalculator { //Addition operation public int add(int a,int b); //Subtraction public int subtract(int a,int b); //Multiple public int multiply(int a,int b); //Dividation public int define(int a,int b);} public class CalculatorImpl implements ICalculator{ @Override public int add(int a, int b) { return a + b; } @Override public int subtract(int a, int b) { return a - b; } @Override public int multiply(int a, int b) { return a * b; } @Override public int define(int a, int b) { return a / b; }}How to record the total number of times the calculator method is used without changing the internal code of the original calculator class?
With dynamic proxy, it is actually very simple. First create a class and implement the InvocationHandler interface, override the invoke method.
public class TestHandler implements InvocationHandler { private Object targetObject; private int useTimes; //Bind the delegate object and return the proxy class public Object bind(Object targetObject){ this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //do something before(); Object result = method.invoke(targetObject,args); after(); return result; } private void before(){ System.out.println("we can do something before calculate."); } private void after(){ useTimes++; System.out.println("used: "+useTimes+" times"); }}Although there seems to be a bit too much code, the main method is the invoke method. The Object result = method.invoke(targetObject,args); it is equivalent to continuing to use the original parameters to execute the original method. The before and after here are customized functions, which can do some things we want to do before and after the object code is executed, such as the usage count here.
In the bind method, the target proxy object is passed in and a proxy class instance is returned. Next, let's see how to use:
public class TestProxy { public static void main(String[] args) { TestHandler proxy = new TestHandler(); ICalculator calculate = (ICalculator)proxy.bind(new CalculatorImpl()); int result = calculate.add(1,2); System.out.println("result is:"+result); result = calculate.subtract(3,2); System.out.println("result is:"+result); result = calculater.multiply(4,6); System.out.println("result is:"+result); result = calculater.devide(6,2); System.out.println("result is:"+result); }}We first define a TestHandler, and then obtain a proxy instance through the bind method, and then we can use this instance directly. The operation results are as follows:
we can do something before calculate. Used: 1 result is:3we can do something before calculate. Used: 2 result is: 1we can do something before calculate. Used: 3 result is: 24we can do something before calculate. Used: 4 result is: 3
In this way, we implement the code extension without modifying the internal code of CalculatorImpl.
Next, use CGLib to implement it once.
First create a class to implement the MethodInterceptor interface and override the intercept method. Other codes are similar to using JDK proxy, but the process of obtaining proxy objects is different.
public class CGLibProxy implements MethodInterceptor { private int useTimes; private Object target; public Object getInstance(Object target){ this.target=target; Enhancer enhancer =new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object result = methodProxy.invokeSuper(o,objects); after(); return result; } private void before(){ System.out.println("we can do something before calculate."); } private void after(){ useTimes++; System.out.println("Used: "+useTimes+" times"); }}Test it out:
public class TestCGLibProxy { public static void main(String[] args) { CGLibProxy cgLibProxy = new CGLibProxy(); ICalculator calculate = (ICalculator) cgLibProxy.getInstance(new CalculatorImpl()); int result = calculate.add(1,2); System.out.println("result is:"+result); result = calculate.subtract(3,2); System.out.println("result is:"+result); result = calculate.multiply(4,6); System.out.println("result is:"+result); result = calculate.devide(6,2); System.out.println("result is:"+result); }}The operation results are as follows:
we can do something before calculate. Used: 1 result is:3we can do something before calculate. Used: 2 result is: 1we can do something before calculate. Used: 3 result is: 24we can do something before calculate. Used: 4 result is: 3
Now we get the same result. (Two packages are required, cglib-2.2.2.jar asm-3.3.jar)
Both methods have their own strengths. JDK proxy needs to set up an interface before implementing the proxy. This is its disadvantage and its advantage. The disadvantage is that this will be a little more troublesome, and it cannot proxy those already encapsulated and does not implement the interface. The CGLib proxy method does not require the use of interfaces. But it is also because of this that the JDK proxy only intercepts methods that overwrite interfaces in the class, while CGLib intercepts all method calls of the class. Both have their pros and cons, so specific circumstances need to be analyzed. In Spring, two proxy modes are used mixed.