Detailed agent model
proxy pattern
Proxy mode is a design mode that provides additional access to the target object, that is, access to the target object through the proxy object. In this way, it can provide additional function operations and expand the functions of the target object without modifying the original target object.
In short, agent mode is to set up an intermediate agent to control the access to the original target object, so as to enhance the function of the original object and simplify the access mode.
Static proxy
This proxy method requires that the proxy object and the target object implement the same interface.
Advantages: the function of the target object can be extended without modifying the target object.
Disadvantages:
Redundancy. Because the proxy object needs to implement an interface consistent with the target object, too many proxy classes will be generated.
Difficult to maintain. Once a method is added to the interface, both the target object and the proxy object must be modified.
Example: static proxy implementation for saving user functions
public interface IUser{ public void save(); }
Implementation class
public class IUserDao implenments IUser{ @overrier public void save(){ System.out.println("hello"); } }
The proxy class needs to implement IUserDao interface!
public ProxyUser implenments IUser{ private IUserDao userDao; public ProxyUser(IUserDao userDao){ this.userDao = userDao; } @Overrier public void save(){ System.out.println("hello"); userDao.save(); System.out.prinltn("goodBye"); } }
Test class
public class Test01 { public static void main(String[] args){ IUserDao IuserDao = new IUserDao(); ProxyUser proxyUser = new ProxyUser(IuserDao); proxyUser.save(); } }
Dynamic agent
JDK dynamic agent
Dynamic proxy uses JDK API to dynamically build proxy objects in memory, so as to realize the proxy function of target objects. Dynamic agent is also called JDK agent or interface agent.
The main differences between static agent and dynamic agent are:
The static proxy has been implemented at compile time. After compilation, the proxy class is an actual class file
The dynamic agent is generated dynamically at runtime, that is, there is no actual class file after compilation. Instead, the class bytecode is generated dynamically at runtime and loaded into the JVM
characteristic:
The dynamic proxy object does not need to implement the interface, but the target object must implement the interface, otherwise the dynamic proxy cannot be used.
Example: dynamic agent implementation for saving user functions
Interface class: IUserDao
package com.proxy; public interface IUserDao { public void save(); }
Target object: UserDao
package com.proxy; public class UserDao implements IUserDao{ @Override public void save() { System.out.println("Save data"); } }
Dynamic proxy object: UserProxyFactory
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { private Object target;// Maintain a target object public ProxyFactory(Object target) { this.target = target; } // Generate proxy object for target object public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Open transaction"); // Execute target object method Object returnValue = method.invoke(target, args); System.out.println("Commit transaction"); return null; } }); } }
Test class: TestProxy
package com.proxy; import org.junit.Test; public class TestProxy { @Test public void testDynamicProxy (){ IUserDao target = new UserDao(); System.out.println(target.getClass()); //Output target object information IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); System.out.println(proxy.getClass()); //Output proxy object information proxy.save(); //Execution agent method } }
Cglib dynamic agent
cglib (Code Generation Library) is a third-party code generation class library, which dynamically generates a subclass object in memory at runtime, so as to expand the function of the target object.
cglib features
One limitation of dynamic proxy in JDK is that objects using dynamic proxy must implement one or more interfaces.
If you want to proxy a class that does not implement an interface, you can use CGLIB implementation.
CGLIB is a powerful high-performance code generation package, which can extend Java classes and implement Java interfaces at run time.
It is widely used by many AOP frameworks, such as Spring AOP and dynaop, to provide them with method interception.
The bottom layer of CGLIB package is to convert bytecode and generate new classes by using a small and fast bytecode processing framework ASM.
Direct use of ASM is not encouraged because it requires you to be familiar with the internal structure of the JVM, including the format of class files and instruction sets.
The biggest difference between cglib and dynamic agent is
Objects that use dynamic proxies must implement one or more interfaces
The object using cglib proxy does not need to implement the interface, so that the proxy class does not invade.
To use cglib, you need to import the jar package of cglib. If you already have the jar package of spring core, you don't need to import it, because spring contains cglib.
Maven coordinates of cglib
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency>
Example: dynamic agent implementation for saving user functions
Target object: UserDao
package com.cglib; public class UserDao{ public void save() { System.out.println("Save data"); } }
Proxy objects: ProxyFactory
package com.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class ProxyFactory implements MethodInterceptor{ private Object target;//Maintain a target object public ProxyFactory(Object target) { this.target = target; } //Generate proxy object for target object public Object getProxyInstance() { //Tool class Enhancer en = new Enhancer(); //Set parent class en.setSuperclass(target.getClass()); //Set callback function en.setCallback(this); //Create subclass object proxy return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Open transaction"); // Method of executing target object Object returnValue = method.invoke(target, args); System.out.println("Close transaction"); return null; } }
Test class: TestProxy
package com.cglib; import org.junit.Test; public class TestProxy { @Test public void testCglibProxy(){ //Target object UserDao target = new UserDao(); System.out.println(target.getClass()); //Proxy object UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance(); System.out.println(proxy.getClass()); //Execute proxy object method proxy.save(); } }
summary
The implementation of static proxy is relatively simple. As long as the proxy object wraps the target object, the enhanced function can be realized. However, static proxy can only serve one target object. If there are too many target objects, many proxy classes will be generated.
JDK dynamic proxy needs the target object to implement the business interface, and the proxy class only needs to implement the InvocationHandler interface.
The class generated by dynamic proxy is class com.sun.proxy. $proxy4, and the class generated by cglib proxy is class com.cglib.UserDao$$EnhancerByCGLIB$2188b6.
The static agent generates class bytecode files during compilation, which can be used directly and with high efficiency.
The dynamic agent must implement the InvocationHandler interface. By reflecting the agent method, the system performance is consumed, but the number of agent classes can be reduced and the use is more flexible.
Cglib proxy does not need to implement the interface. It implements the proxy by generating class bytecode, which is slightly faster than reflection, and there is no performance problem. However, cglib inherits the target object and needs to rewrite the method, so the target object cannot be final.