This article can be understood as a preparation for Spring AOP. I will probably use the AOP provided by the Spring framework in the past, but I do not know much about AOP, so I intend to systematically comb through the proxy pattern and AOP.

A: Let me talk briefly about agency

The software world is an abstraction of The real world, so some concepts in The software world do not exist in a blank space but are modeled on The real world. Just like polymorphism, its official guide, The Java™ Tutorials, explained that polymorphism was, above all, a biological concept. Then, the agent mode in the design mode can also be considered as abstract from the real world, where the word agent is also very common, such as the real estate agent acting for the landlord’s house, the agent acting for the star to talk about business performance. Basically, an agent acts for the affairs of the represented within a certain scope.

So why do we need the proxy model? We can actually look to the real world for answers, why do landlords go to real estate agents? Because the landlord is also a human being and has his own life, he cannot put all his heart into renting out his house and is unwilling to make changes. The software proxy pattern of the world, I think that is the same reason, the original an object needs to undertake a function again, but we are not willing to modify the object of class, the reason may have a lot of, the old object may have external use, once a change may have other unexpected response, maybe we can’t change, and so on. There are a number of reasons, and modifications to a past class are not open and closed, open for extensions, closed for modifications. So we want to extend the functionality of the target without changing the functionality of the target object.

When most blogs on the web talk about the proxy pattern, they will start with an interface, then the requirement is to extend the implementation to enhance the interface implementation class, and then write code to demonstrate it. Something like this:

public interface IRentHouse {
    /** * Classes that implement this interface will be able to provide houses */
    void rentHouse(a);
}
public class Landlord implements IRentHouse {
    @Override
    public void rentHouse(a) {
        System.out.println("I offer you the house..... "); }}public class HouseAgent implements IRentHouse {

    @Override
    public void rentHouse(a) {
        System.out.println("Execute the method before the method of that class");
        IRentHouse landlord = new Landlord();
        landlord.rentHouse();
        System.out.println("Execute the method after the method of that class"); }}public class Test {
    public static void main(String[] args) {
        IRentHouse landlord = newHouseAgent(); landlord.rentHouse(); }}Copy the code

Test results:This is where many bloggers start with static proxies, enhancing the original class without changing it. Yes, this is a very realistic requirement, especially in the case of the original class is used more in the system, and the operation is relatively stable, once the change, no matter how carefully, can not guarantee that the original system will not have a bit of impact, the best way is not to change, how to enhance the original target object, Your new enhancement is usually to meet the needs of new callers, so I’ll create a new class for you to call. Perfect. So the question is, why do blogs start from interfaces?

Because the proxy class implements the same interface as the target class, the goal is to ensure that the internal structure of the proxy object is as consistent as possible with the target object, so that all our operations on the proxy object can be transferred to the target object, we only focus on enhancing the writing of the code.

From an object-oriented design point of view, if I don’t put a top-level interface at this point, like above I move the methods in the interface into the class, there’s no interface anymore. So what should you do to enhance at this point? Classes have properties and behaviors, but process-oriented languages do not provide these properties. When you learn Java, when you learn classes, then you learn abstract classes and interfaces, we can say that interfaces emphasize behavior, it’s a contract, and when you do object-oriented design, you abstract out the behavior of multiple classes and put it into interfaces, it’s more extensible, how do you understand that it’s more extensible? As above, program to the interface.

If we stubbornly don’t abstract and have the same methods in many classes, it will be difficult to extend and upgrade the old classes, and we will have to change the old classes. What about abstract classes? What about abstract classes? If an interface is an abstraction of the behavior of many classes, then an abstract class is an abstraction of the behavior of that class of objects, at different levels of abstraction. For example, dairy enterprises all have to achieve a standard, but how to achieve the country does not care. Abstract categories are birds, hummingbirds, eagles. The commonality of classes in this system, such as being able to fly.

In fact, static proxy is basically done at this point, the proxy model focuses on enhancing without changing the old class, so enhancement usually means method enhancement, behavior enhancement, and properties enhancement. So for extensibility, we can design the behavior in an interface or you can design it in an abstract class, so we can enhance it seamlessly. As for the design pattern, I think we should not be constrained. I think the design pattern is an idea, and the way to practice it is different.

Static agent to enhance the function of a class, then when we have a lot of system needs to be enhanced, just have a little not suitable for, under the assumption that there are thirty classes need to strengthen, and the design is good, is to place the behavior abstract interface, in this case, you can’t write 30 static proxy class. Of course we can’t write it ourselves, we let the JVM write it for us. This is also known as dynamic proxy.

A dynamic proxy

Look at the process of creating an object from another perspective

For a Java programmer, an object creation process might look like this:So let’s think about it a little bit, and take this object-oriented approach to the bottom line, and think about it, as Java programmers we use classes to model things in the real world, should we model the behavior of classes as well? That is, the Class that describes the Class, which is in the JDK: java.lang.class. Each Class has only one Class object. From this perspective, the relational structure of Java classes looks like this:So if I want to create an object, and the JVM does load that Class’s bytecode into the JVM, the object’s Class object is created before the object is created, so the object creation process looks like this:Before creating an object for any Class, the JVM first creates a Class object for each Class. There is only one Class object for each Class. It is then used to obtain meta information about the class when it is created.

Interface-based proxy

Here we reiterate our goals:

  • We have a bunch of classes, and then we want to enhance them without changing them, and we want to just focus on writing code that enhances the target object.
  • We also want programs to write these classes, not programmers, because there are too many.

For the first goal, we can make the target object and the proxy object implement a common interface, so that we can just focus on writing the target object code.

What about the second goal? We know that interfaces cannot be instantiated, and we said that the target object has a Class object with its constructor, fields and so on. We can agent for the target Class by Class Class object, the write about enhance code, provides the Java JDK. Lang. Reflect. InvocationHandler (interface) and Java. Lang. Reflect. The Proxy Class to help us produce the interface implementation Class at runtime.Let’s recall our requirement that we do not want to delegate classes and let the JVM write. So how do you let the JVM know which class you want to proide? General design thinking is that first you have to tell the proxy class and the target class what interface they need to implement together, you have to tell the target class that you want to delegate which class is loaded by which class loader. This is the Proxy: getProxyClass method. Let’s take a look at this method:

public static Class<? > getProxyClass(ClassLoader loader, Class<? >... interfaces)throws IllegalArgumentExceptionCopy the code

The first parameter is the classloader of the target class, and the second parameter is the interface implemented by both the proxy class and the target class. What about augmentation? What about enhancement? GetProxyClass is the proxy Class that the JDK created for us, but it doesn’t have an ontology (or the JDK removed the Class after it was created for us). Instead, it has to use the Class object. Create objects indirectly by reflecting interfaces. So the above static proxy if transformed into a static proxy, can be transformed like this:

 private static void dynamicProxy(a) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /** * The first argument is the loader of the target class * the second argument is the interface that the target class and proxy class need to implement */Class<? > rentHouseProxy = Proxy.getProxyClass(IRentHouse.class.getClassLoader(), IRentHouse.class);// Classes that are dynamically propped up by the JDK have a constructor of type InvocationHandler. We get it by reflectionConstructor<? > constructor = rentHouseProxy.getConstructor(InvocationHandler.class);// Reflection creates an object, passing it an InvocationHandler object, which is called first when methods in the interface implemented by both the target class and the proxy class are called
        // The Invoke method of InvocationHandler has methods that the target object needs to enhance. The parameters required for the method call to be enhanced for the target object
        IRentHouse iRentHouseProxy = (IRentHouse) constructor.newInstance(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                IRentHouse iRentHouse = new Landlord();
                System.out.println("Before method call.............");
                Object result = method.invoke(iRentHouse, args);
                System.out.println("After method call.............");
                returnresult; }}); iRentHouseProxy.rentHouse(); }Copy the code

If I want to dynamically proxy another class, do I have to write another class? In fact, I could have written this:

private static Object getProxy(final Object target) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<? > proxyClazz = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces()); Constructor<? > constructor = proxyClazz.getConstructor(InvocationHandler.class); Object proxy = constructor.newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {system.out.println (method.getName() + "Method start execution.........." ); Object object = method.invoke(target, args); System. Out.println (method getName () + "method performs end..." ); return object; }}); return proxy; }Copy the code

So we’re passing in the target object, so it’s more generic. In fact, it could have been:

private static Object getProxyPlus(final Object target) { Object proxy = 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 (" method starts execution.........." ); Object obj = method.invoke(target, args); System.out.println(" method execution ends.........." ); return obj; }}); return proxy; }Copy the code

Introduction to CGLIB agents

And then I want to enhance a class that happens not to implement an interface? This is where Cglib comes in, but it’s similar. If you have an interface, I’ll create an implementation class for you. If you don’t have an interface to enhance, I’ll dynamically subclass it. The idea of cglib is that you can inherit all the public methods of a parent class by “inheriting” and then override them, enhancing them as you override them.

Cglib implements a MethodInterceptor interface, which provides a MethodInterceptor interface.We can do this:

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Method before execution");
        System.out.println(args);
        Object returnValue = methodProxy.invokeSuper(obj, args);
        System.out.println("Method executed after execution");
        returnreturnValue; }}Copy the code

Note that calling a method that calls a proxy class in the Intercept method returns to the Intercept method, creating an infinite loop. Intercept can be considered to be an enhanced method of a proxy class. Calling it on its own causes recursion, so the call above search uses methodProxy to call the function of the inherited parent class, which is a proxy class. Test code:

private static void cglibDemo(a) {
        // Start with Enhancer
        Enhancer enhancer = new Enhancer();
        // Set the class to be enhanced.
        enhancer.setSuperclass(Car.class);
        // Sets the actual enhancer of the enhancer class
        enhancer.setCallback(new MyMethodInterceptor());
        // Create the actual proxy class
        Car car = (Car) enhancer.create();
        System.out.println(car.getBrand());
    }
Copy the code

This enhancement is made to all public methods of a class. This is the introduction of Cglib, in the study of Cglib dynamic proxy also looked up some information on the Internet, how to say? Always not so satisfactory, there is always such and such defects. Think or do their own translation, but the Cglib document is a little bit huge, think or not put here, I hope you pay attention to understand the idea of good.

To summarize

Whether dynamic or static agents are focused on in don’t modify the original class to strengthen, on the basis of static agent is our manual writing target enhancement, the agency in our class there are a lot of time on some is not applicable, we don’t want to for each need to increase the tired is added a proxy class. That is, let the JVM create proxy classes for us when we need dynamic proxies. Proxy classes are also created in two forms, one interface-based (officially provided by the JDK) and the other class-based (done by Cglib). Interface-based is the implementation class that creates the interface at run time, and class-based is the subclass that creates the class to be enhanced at run time.

The resources

  • Design Patterns (4) – Understand the agency pattern
  • What does Java dynamic proxy do?
  • What is a Java bean concept?
  • Class loading and Class objects
  • CGLIB(Code Generation Library) details
  • Java dynamic proxy JDK dynamic proxy and CGLIB dynamic proxy
  • Java Proxy and CGLIB dynamic Proxy