preface

I decided to explain dynamic proxy from the shallow to the deep, and then use dynamic proxy to implement a simple AOP, feel that this can make people have a more profound understanding of the principle of AOP, I hope to help you.

Welcome to the group to exchange and learn together, our exchange and sharing group:1149778920Cipher: CSDN

The blogger here has sorted out a variety of materials including but not limited to: JAVA foundation and advanced classes, Spring, Spring Boot, Spring MVC, MyBatis, MySQL, JVM and so on, free to share with you into the group partners

What is dynamic proxy

A dynamic proxy is a Java method that dynamically creates an implementation object for a specified set of interfaces (at runtime, objects that implement a specified set of interfaces).

Such as:

Object obj = method (new Class[]{a.class, b.class})Copy the code

First experience of dynamic proxy

Let’s experience dynamic proxy in Java according to the above ideas, first we need to write two interfaces.

interface A {
    public void a();
}
interface B {
    public void b();
}
Copy the code

Let’s first look at the code for the dynamic proxy:

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)Copy the code

This is the method that creates the Proxy object in the dynamic Proxy class. Here are the three parameters of the method:

  • ClassLoader: the loader method dynamically generates A class that implements both interfaces A and B, and then creates objects for that class. We need to generate a class that also needs to be loaded into the method area, so we need a ClassLoader to load the class

  • Class<? >[] interfaces: we need an array of proxy object implementations

  • InvocationHandler H: Calls the handler

You might be confused about InvocationHandler here, but I’m just going to put it in a nutshell. Let’s now create a proxy object using dynamic proxies.

@test public void test1() {/** * the ClassLoader method needs to dynamically generate A class that implements both A and B interfaces, and then create an object that generates A class and loads it into the method area. So we need a ClassLoader to load this Class * * 2, Class[] interfaces * we need an array of proxy object implementations * * 3, InvocationHandler * calls the handler */ ClassLoader classLoader = this.getClass().getClassLoader(); // Create an empty implementation call handler here. InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }}; Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler); // The generated proxy object implements interface A and B A A = (A) obj; B b = (B) obj; }Copy the code

Tested code to run successfully, the generated proxy objects really achieved A interface and B interface, but I think you will be on the proxy object how to implement the interface and B definitely interested, you must want to know if you are using A proxy object call the corresponding interface method is interested in what happens, let’s together to explore:

Add the following code to the code above

a.a();
b.b(); 
Copy the code
  • 1
  • 2



We can see that nothing happened. This is because we did not add implementation logic to the proxy object at all. But where is the implementation logic added? Ha ha, of course it’s in the InvocationHandler. Let’s take a look at the code with the implementation logic added:

@test public void test2() {/** * the ClassLoader method generates A class that implements both A and B interfaces, and then generates A class that loads into the method area. So we need a ClassLoader to load the Class interfaces * * 2, Class[] interfaces * * we need an array of interfaces implemented by the proxy object * * 3, InvocationHandler * calls the handler * * all the interfaces implemented by the proxy object, Invoke () to InvocationHandler */ ClassLoader ClassLoader = this.getClass().getClassLoader(); // Create an empty implementation call handler here. InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {system.out.println (" hello !!!!") ); // Note that a little bit of logic is added here: return null; }}; Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler); // The generated proxy object implements interface A and B A A = (A) obj; B b = (B) obj; a.a(); b.b(); }Copy the code

Screenshot below:



Here we found A interface and B interface implementation logic is invoked in the invoke this method of logic, apart from native method calls the proxy object, call the proxy objects of the nature of all other methods are invoked in the invoke method, below we look at the third instance, let’s have A more profound understanding on dynamic proxy.

Public void test3() {public void test3() {public void test3() {public void test3() {/** * So we need a ClassLoader to load the Class interfaces * * 2, Class[] interfaces * * we need an array of interfaces implemented by the proxy object * * 3, InvocationHandler * calls the handler * * all the interfaces implemented by the proxy object, Invoke () to InvocationHandler */ ClassLoader ClassLoader = this.getClass().getClassLoader(); // Create an empty implementation call handler here. InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {system.out.println (" hello !!!!") ); return "Hello"; // return "Hello"}}; Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler); // The generated proxy object implements interface A and B A A = (A) obj; B b = (B) obj; a.toString(); // Notice that the toString() method is called. Public Object aaa(String s1, int I); public Object aaa(String s1, int I); Object hello = a.aaa("Hello", 100); System.out.println(obj.getClass()); System.out.println(hello); system.out.println (hello); } ' '! [insert picture description here] (https://img-blog.csdnimg.cn/20201203201646464.png?x-oss-process=image/watermark, type_ZmFuZ3poZW5naGVpdGk, sha dow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NwcmluZ0Jvb3Rf,size_16,color_FFFFFF,t_70) ! [insert picture description here] (https://img-blog.csdnimg.cn/20201203201713229.png?x-oss-process=image/watermark, type_ZmFuZ3poZW5naGVpdGk, sha Dow_10 text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NwcmluZ0Jvb3Rf, size_16 color_FFFFFF, t_70) when we call the proxy object method, the corresponding relationship as shown above. Once we have some understanding of dynamic proxies, we can implement the most basic version of AOP. Of course, this is a very incomplete implementation of AOP, and it can't even be called an AOP implementation. Java package Demo2; /** * Created by Yifan Jia on 2018/6/5. */ // public void server(); }Copy the code

Then the implementation class of the interface is given:

package demo2; /** * Created by Yifan Jia on 2018/6/5. */ public class ManWaiter implements Waiter { @Override public void server() { System.out.println(" in service "); }}Copy the code

We then enhance the above ManWaiter with a dynamic proxy:

package demo2; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by Yifan Jia on 2018/6/5. */ public class Demo2 { @Test public void test1() { Waiter waiter = new ManWaiter(); waiter.server(); } @Test public void test2() { Waiter manWaiter = new ManWaiter(); ClassLoader classLoader = this.getClass().getClassLoader(); Class[] interfaces = {Waiter.class}; InvocationHandler invocationHandler = new WaiterInvocationHandler(manWaiter); // Get the proxy object, Waiter Waiter = (Waiter) proxy. newProxyInstance(classLoader, interfaces, invocationHandler); waiter.server(); }} class WaiterInvocationHandler implements InvocationHandler {private Waiter Waiter; WaiterInvocationHandler(Waiter waiter) { this.waiter = waiter; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {system.out.println (" Hello "); waiter.server(); // Call the target object's method system.out.println (" goodbye "); return null; }}Copy the code

The results are as follows:



This is not AOP, you must say. The enhanced code is hardcoded into the Invoke method, so calm down, we already enhanced the object we need to enhance. The possible target object here is manWaiter, enhanced to System.out.println(” hello “); And system.out.println (” goodbye “); , the pointcut is the server() method call. In fact, you can still think of it as the original AOP.

Fourth, perfect AOP implementation

We can see a lot of problems with our initial implementation of AOP. For example, we can’t hardcode the enhanced logic into code. We need to implement variable enhancements. We still refer to the Waiter interface and Manwaiter implementation class above. Then we add a pre-enhanced interface:

/** * BeforeAdvice public interface BeforeAdvice {public void before(); }Copy the code

Add another afterenhance interface:

public interface AfterAdvice {
    public void after();
}
Copy the code

We encapsulate the code that generates the proxy object as a class:

package demo3; import com.sun.org.apache.regexp.internal.RE; import org.junit.After; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * ProxFactory is used to generate the proxy object * it takes all the parameters: Target object, enhancement, */ ** * Created by Yifan Jia on 2018/6/5. */ ** * Created by Yifan Jia on 2018/6/5. Do the pre-enhancement first, then the target method, Public class ProxyFactory {private Object targetObject; public class ProxyFactory {private Object targetObject; // Target object private BeforeAdvice BeforeAdvice; Private AfterAdvice AfterAdvice; Public Object creatProxy() {/** * gives three arguments */ ClassLoader ClassLoader = this.getClass().getClassLoader(); Class[] interfaces = targetobject.getClass ().getinterfaces (); InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {/** * this is executed when calling methods on proxy objects */ if(beforeAdvice! = null) { beforeAdvice.before(); } Object result = method.invoke(targetObject, args); Afteradvice.after (); // Call the target method of the target object. // Return the target object return value return result; }}; /** */ interface.interface (); /** */ interface.interface (); return proxyObject; } //get and set methods are omittedCopy the code

We can then use the creatProxy() method to get the proxy object by injecting the relevant parameters into the ProxyFactory:

package demo3; import org.junit.Test; /** * Created by Yifan Jia on 2018/6/5. */ public class Demo3 { @Test public void tset1() { ProxyFactory proxyFactory = new ProxyFactory(); / / create factory proxyFactory. SetTargetObject (new ManWaiter ()); / / set the target object. / / set up front to enhance proxyFactory setBeforeAdvice (new BeforeAdvice () {@ Override public void before () { System.out.println(" hello customer "); }}); / / set the rear enhanced proxyFactory. SetAfterAdvice (new AfterAdvice () {@ Override public void after () {System. Out. Println (" customer goodbye "); }}); Waiter waiter = (Waiter) proxyFactory.creatProxy(); waiter.server(); }}Copy the code

The results are as follows:



Isn’t it amazing that we can now customize arbitrary augmentation logic?

Five, dynamic proxy implementation AOP summary

Through the above, we have implemented a very crude AOP implementation through dynamic proxies, and the AOP implementation here still leaves a lot to be desired. ProxyFactory is the most basic version of Spring’s dynamic proxy implementation. ProxyFactory is the most basic version of Spring’s dynamic proxy implementation. You can study it if you are interested.

public class ProxyFactory extends ProxyCreatorSupport {
    public ProxyFactory() {
    }

    public ProxyFactory(Object target) {
        Assert.notNull(target, "Target object must not be null");
        this.setInterfaces(ClassUtils.getAllInterfaces(target));
        this.setTarget(target);
    }

    public ProxyFactory(Class... proxyInterfaces) {
        this.setInterfaces(proxyInterfaces);
    }

    public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
        this.addInterface(proxyInterface);
        this.addAdvice(interceptor);
    }

    public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {
        this.addInterface(proxyInterface);
        this.setTargetSource(targetSource);
    }

    public Object getProxy() {
        return this.createAopProxy().getProxy();
    }

    public Object getProxy(ClassLoader classLoader) {
        return this.createAopProxy().getProxy(classLoader);
    }

    public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
        return (new ProxyFactory(proxyInterface, interceptor)).getProxy();
    }

    public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {
        return (new ProxyFactory(proxyInterface, targetSource)).getProxy();
    }

    public static Object getProxy(TargetSource targetSource) {
        if(targetSource.getTargetClass() == null) {
            throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
        } else {
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.setTargetSource(targetSource);
            proxyFactory.setProxyTargetClass(true);
            return proxyFactory.getProxy();
        }
    }
}
Copy the code

The article ends with

** This post about Spring Aop has ended here. The blogger here gives you a benefit ~ **

**

The following are some data screenshots (all data have been integrated into a document, compressed PDF packaging processing). **