From dynamic proxy implementation to Spring AOP
1. Spring AOP
Spring is a lightweight container, and the core concepts of the entire Spring series are IoC and AOP. It can be seen that AOP is one of the cores of the Spring framework, plays a very important role in the application, and is also the foundation of other Spring components. AOP (Aspect Oriented Programming), that is, aspect-oriented programming, can be said to be the supplement and improvement of OOP (Object Oriented Programming, object-oriented programming). OOP introduces concepts such as encapsulation, inheritance, and polymorphism to establish an object hierarchy that simulates a collection of common behaviors. However, OOP allows developers to define vertical relationships, but it is not suitable for defining horizontal relationships, such as logging functions.
The basic knowledge of AOP is not the focus of this article. We mainly look at the underlying implementation mechanism of the core functions of AOP: the implementation principle of dynamic proxy. The interception function of AOP is realized by the dynamic proxy in java. Aspect logic is added on the basis of the target class to generate an enhanced target class (this aspect logic is executed either before the target class function is executed, or after the target class function is executed, or when the target class function throws an exception. Different cut-in timings correspond to Different types of Interceptor, such as BeforeAdviseInterceptor, AfterAdviseInterceptor and ThrowsAdviseInterceptor, etc.).
So how does the dynamic proxy implement the weaving of the aspect logic (advise) into the target class method? Below we will introduce and implement the two dynamic proxies used in AOP in detail.
Two dynamic proxies are used in the source code of AOP to implement the interception function: jdk dynamic proxy and cglib dynamic proxy. Both methods exist at the same time, each with advantages and disadvantages. The jdk dynamic proxy is implemented by the reflection mechanism inside java, and the bottom layer of the cglib dynamic proxy is implemented by asm. In general, the reflection mechanism is more efficient in the process of generating classes, while asm is more efficient in the related execution process after the class is generated (the class generated by asm can be cached to solve the problem of inefficiency in the asm generation process) .
Let's take an example to implement these two methods separately.
2. JDK dynamic proxy
2.1 Define interface and implementation class
public interface OrderService { public void save(UUID orderId, String name); public void update(UUID orderId, String name); public String getByName(String name);}
The above code defines an intercepted object interface, the cross-cutting concern. The following code implements the intercepted object interface.
public class OrderServiceImpl implements OrderService { private String user = null; public OrderServiceImpl() { } public OrderServiceImpl(String user) { this.setUser(user); } //... @Override public void save(UUID orderId, String name) { System.out.println("call save() method, save:" + name); } @Override public void update(UUID orderId, String name) { System.out.println("call update() method"); } @Override public String getByName(String name) { System.out.println("call getByName() method"); return "aoho"; } }
2.2 JDK dynamic proxy class
public class JDKProxy implements InvocationHandler { //The target object that needs to be proxied private Object targetObject; public Object createProxyInstance(Object targetObject) { this.targetObject = targetObject; return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //The proxied object OrderServiceImpl bean = (OrderServiceImpl) this.targetObject; Object result = null; //Aspect logic (advise), here before the target class code is executed System.out.println("---before invoke----"); if (bean.getUser() != null) { result = method.invoke(targetObject, args); } System.out.println("---after invoke----"); return result; } //... }
The above code implements the dynamic proxy class JDKProxy, implements the InvocationHandler interface, and implements the invoke method in the interface. When the client calls the business method of the proxy object, the proxy object executes the invoke method, and the invoke method delegates the call to the targetObject, which is equivalent to calling the method of the target object. Before the invoke method is delegated, the authority is judged and the method is intercepted.
2.3 Testing
public class AOPTest { public static void main(String[] args) { JDKProxy factory = new JDKProxy(); //Proxy dynamically creates a proxy instance that conforms to a certain interface for the InvocationHandler implementation class OrderService orderService = (OrderService) factory.createProxyInstance(new OrderServiceImpl("aoho")); //The dynamically generated proxy object is used to orderService proxy executor orderService.save(UUID.randomUUID(), "aoho"); } }
The result is as follows:
---before invoke---- call save() method, save:aoho ---after invoke----
3. CGLIB bytecode generation
3.1 Classes to be proxied
CGLIB can generate proxies both for classes of interfaces and for classes. In the example, implement a proxy to the class.
public class OrderManager { private String user = null; public OrderManager() { } public OrderManager(String user) { this.setUser(user); } //... public void save(UUID orderId, String name) { System.out.println("call save() method, save:" + name); } public void update(UUID orderId, String name) { System.out.println("call update() method"); } public String getByName(String name) { System.out.println("call getByName() method"); return "aoho"; } }
The implementation of this class is the same as the above interface implementation, in order to maintain unity.
3.2 CGLIB dynamic proxy class
public class CGLibProxy implements MethodInterceptor { // CGLib needs the proxy target object private Object targetObject; public Object createProxyObject(Object obj) { this.targetObject = obj; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); //The parameter of the callback method is the proxy class object CglibProxy, and finally the enhanced target class calls the intercept method in the proxy class object CglibProxy enhancer.setCallback(this); // Enhanced target class Object proxyObj = enhancer.create(); // return the proxy object return proxyObj; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; //Aspect logic (advise), here before the target class code is executed System.out.println("---before intercept----"); obj = method.invoke(targetObject, args); System.out.println("---after intercept----"); return obj; } }
The above implements the method of creating a subclass and the method of proxy. The getProxy(SuperClass.class) method creates a proxy object by extending the class of the parent class by entering the bytecode of the parent class. The intercept() method intercepts all method calls of the target class, obj represents the instance of the target class, method is the reflection object of the method of the target class, args is the dynamic input parameter of the method, and methodProxy is the instance of the proxy class. method.invoke(targetObject, args) invokes the method in the parent class through the proxy class.
3.3 Testing
public class AOPTest { public static void main(String[] args) { OrderManager order = (OrderManager) new CGLibProxy().createProxyObject(new OrderManager("aoho")); order.save(UUID.randomUUID(), "aoho"); }
The result is as follows:
---before intercept---- call save() method, save:aoho ---after intercept----
4. Summary
This article mainly talks about two ways of Spring Aop dynamic proxy implementation, and introduces their advantages and disadvantages respectively. The premise of the application of jdk dynamic proxy is that the target class is based on a unified interface. Without this premise, the jdk dynamic proxy cannot be applied. It can be seen that the jdk dynamic proxy has certain limitations. The dynamic proxy implemented by the third-party class library such as cglib is more widely used and has more advantages in efficiency.
The JDK dynamic proxy mechanism is a delegation mechanism. It does not require a third-party library. As long as the JDK environment is required, the proxy can be used to dynamically implement the interface class. In the dynamically generated implementation class, the hanlder is delegated to call the original implementation class method; CGLib must rely on The class library of CGLib uses the inheritance mechanism, which is the inheritance relationship between the proxy class and the proxy class, so the proxy class can be assigned to the proxy class. If the proxy class has an interface, the proxy class can also be assigned to the interface. .
0 Comments