Role of agent:
- You can hide the concrete implementation of the delegate class
- You can decouple the client from the delegate class and do some enhancements without modifying the delegate class.
1.1 Static Proxy
1.1.1 Principle of Static Proxy
- Static proxies require manual writing of proxy classes as well as implementation of delegate interfaces;
- Inject the delegate class into the proxy class;
- In the proxy class, enhance the methods in the delegate class (adding before, after, and other method details).
- Static proxies can be implemented through aggregation, where the proxy class holds a reference to the delegate class.
Conclusion: The limitation of static proxies is that the proxy class must be written before it is run. Once there are hundreds or thousands of methods in the delegate interface, the proxy class has a lot of redundant code.
1.2 Dynamic Proxy
The Java VIRTUAL machine class loading process is divided into five stages: loading, verification, preparation, parsing, and initialization. Dynamic proxies are ways to compute the bytecode of the proxy class, based on the interface or target object, and then load it into the JVM for use. There are two ways to implement dynamic proxy:
- By implementing the interface -> JDK dynamic proxy
- By inheriting classes -> CGLIB dynamic proxy
1.2.1 JDK Dynamic Proxy
The JDK dynamic Proxy involves two main categories: Java. Lang. Reflect. The Proxy and Java lang. Reflect. InvocationHandler
java.lang.reflect.InvocationHandler
:Object Invoke (Object Proxy, Method Method, Object[] args) defines the actions that a proxy Object wants to perform when calling a Method. It is used to focus Method calls on dynamic proxy objects. Java.lang.reflect. Proxy: static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) constructs a new instance of the proxy class that implements the specified interface. All methods call the invoke method of the given processor object
[Usage steps] :
- Define logical processing objects
XxxHandler
And implementInvocationHandler
Interface; inXxxHandler
Maintains a target object, which is the proxied object (real subject role); ininvoke
Method to write the logical handling of a method call.
// Loghandler logical processing objects are defined here. public class LogHandler implements InvocationHandler { Object target; Public LogHandler(Object target) {this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(target, args); after(); return result; Private void before() {system.out.println (string.format ("log start time [%s] ", new Date())); Private void after() {system.out.println (string.format ("log end time [%s] ", new Date())); }}Copy the code
- Dynamically generate proxy tired objects in the control layer.
public class Client2 { public static void main(String[] args) throws IllegalAccessException, InstantiationException {// Set variables to store dynamic proxy classes, The default name for $Proxy0 format name / / System. The getProperties () setProperty (" sun. Misc. ProxyGenerator. SaveGeneratedFiles ", "true"); Create the propped object, UserServiceImpl UserServiceImpl = new UserServiceImpl(); // 2. Obtain the corresponding ClassLoader ClassLoader ClassLoader = userServiceImpl.getClass().getClassLoader(); UserServiceImpl implements only one interface, UserService. Class[] interfaces = userServiceImpl.getClass().getInterfaces(); // create a call request handler that will be passed to the proxy class to handle all method calls on the proxy object. UserServiceImpl InvocationHandler logHandler = new logHandler (userServiceImpl); In this process, A.dk dynamically creates bytecode B in memory equivalent to the.class file based on the passed parameter information. And then convert to the corresponding class based on the corresponding bytecode, C. Then call newInstance() to create a proxy instance */ UserService proxy = (UserService) proxy.newProxyInstance (classLoader, interfaces, logHandler); // Invoke the proxy method proxy.select(); proxy.update(); / / save the JDK dynamic proxy generated proxy class, the class name is saved as a UserServiceProxy ProxyUtils. GenerateClassFile (userServiceImpl. GetClass (), "UserServiceProxy"); }}Copy the code
The principle is as follows:
XxxProxy
It inherits the Proxy class and implements all the interfaces of the Proxy- Classes and all methods are public final, so proxy classes can only be used, not inherited
- Each Method is described by a Method object, which is created in a static block of code and named in the m + number format
- When a method is called
super.h.invoke(this, m1, (Object[])null)
; Call, wheresuper.h.invoke
Is actually passed to when the proxy is createdProxy.newProxyInstance
的LogHandler
Object, which inheritsInvocationHandler
Class that is responsible for the actual call processing logic.
1.2.2 CGLIB Dynamic Proxy
CGLIB proxy steps:
- Create the Enhancer Enhancer.
- Set up the superclass for the enhancer
setSuperclass(Xxx)
That is, the target object Xxx to be proxied. CGLIB implements dynamic proxies by inheritance in this way.- Setting interception Objects
setCallback(XxxInterceptor)
Intercepting objectsXxxInterceptor
To achieve theMethodInterceptor
interface- Create a proxy class object with create().
public class Target{ public void f(){ System.out.println("Target f()"); } public void g(){ System.out.println("Target g()"); }} public class MyInterceptor implements MethodInterceptor {/** * @param method implements MethodInterceptor The @param Objects array represents a list of arguments. Primitive data types are passed in wrapper types such as int-->Integer, long-long, and double--> double * @param methodProxy represents a proxy for methods. The invokeSuper method refers to the invocation of the proxied Object method. * @return execution result * @throws Throwable */ @Override public Object Intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("I am intercept begin"); InvokeSuper (obj, args); System.out.println("I am intercept end"); return null; }} public class Test {public static void main(String[] args) {// Used to save generated decompile code System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code"); // instantiate a class generator Enhancer eh = new Enhancer(); // Set the Target class eh.setSuperclass(target.class); Eh.setcallback (new MyInterceptor()); // Generate the proxy class and return an instance Target t = (Target) eh.create(); t.f(); t.g(); }}Copy the code
The output
Log Start time [Fri Dec 21 00:06:40 CST 2018] UserDao Queries selectById log end time [Fri Dec 21 00:06:40 CST 2018] log Update log end time [Fri Dec 21 00:06:40 CST 2018]Copy the code
It is also possible to enter multiple interceptor objects and use the CallbackFilter CallbackFilter to perform different callback logic for different methods, or to perform no callback at all.
public class DaoFilter implements CallbackFilter { @Override public int accept(Method method) { if ("f".equals(method.getName())) {// Callback list first interceptor return 0; } // Callback is the second interceptor in the list, return 2 is the third, and so on; }}Copy the code
Test join:
// Set multiple interceptors, noop. INSTANCE is an empty interceptor, SetCallbacks (new Callback[]{logInterceptor, logInterceptor2, noop.instance}); enhancer.setCallbackFilter(new DaoFilter());Copy the code
1.2.3 Comparison between JDK Dynamic Proxy and CGLIB Dynamic Proxy
- JDK dynamic proxies are implemented based on Java reflection. Business classes that implement their own business interfaces are required to generate proxy objects in this way. JDK dynamic proxies can only Proxy interfaces because the Proxy class itself extends Proxy, and Java does not allow multiple inheritance, but does allow multiple interfaces to be implemented.
- CGLIB is implemented based on the ASM mechanism by generating subclasses of business classes as proxy classes.
- CGLib takes a lot more time to create proxy objects than the JDK, so for singleton objects that don’t need to be created frequently, CGLib works better than the JDK approach.
- At the same time, because CGLib is subclassed dynamically, there is no proxy for final methods.