This is the 8th day of my participation in Gwen Challenge


The secret of Spring AOP is to enhance existing functionality. If we want to get all the existing capabilities of a class, the first thing that comes to mind is to extract the interface and let our enhanced class implement this interface, which is the functionality of the proxy.

1. First acquaintance with agents

The Proxy mode is a structural design mode. It mainly solves the problem of excessive coupling when accessing objects directly

Proxy is a common design pattern whose purpose is to provide other objects with a proxy to control access to an object. The proxy class is responsible for preprocessing messages for the delegate class, filtering and forwarding messages, and for subsequent processing of messages after they are executed by the delegate class.

In order to maintain consistency of behavior, proxy classes and delegate classes often implement the same interface, so there is no difference between them to visitors. Through the middle layer of proxy class, it can effectively control the direct access to the delegate class object, and also can hide and protect the delegate class object well. At the same time, it also reserves space for the implementation of different control strategies, so as to obtain greater flexibility in the design.

More popular said, agent problem when two classes need communication, introducing the third-party proxy class, decoupling the relationship will be two classes, let us understand the proxy class to go only, and the presence of the agent can also let us finish with another class of the relationship between the unified management, but remember, the proxy class and delegate class to implement the same interface, Because the proxy really calls the methods of the delegate class.

Example: If a business needs to be handled by a delegate class, it can be handled in the delegate class and then invoked by the implementation class

Proxy classes can be divided into two categories, depending on when the agent was created:

Static: A programmer creates a proxy class or a specific tool automatically generates source code and compiles it. The.class file for the proxy class exists before the program runs.

Dynamic: created dynamically while the program is running using reflection.

1.1 Static Proxy

Interface:

public interface UserManager {
	void addUser(String userName);
}
Copy the code

Implementation class:

public class UserManagerImpl implements UserManager { @Override public void addUser(String userName) { System.out.println("UserManagerImpl add user name is:" + userName); }}Copy the code

The proxy class:

public class UserManagerImplProxy implements UserManager { private UserManagerImpl userManager; public UserManagerImplProxy(UserManagerImpl userManager) { this.userManager = userManager; } @Override public void addUser(String userName) { System.out.println("before add user, user name is :" + userName); this.userManager.addUser(userName); System.out.println("after add user, user name is :" + userName); }}Copy the code

Client call:

@Test public void test_static_proxy() { UserManagerImplProxy proxy = new UserManagerImplProxy(new UserManagerImpl()); proxy.addUser("steven"); } Before add user, user name is: Steven UserManagerImpl add user name is: Steven after add user, user name is: StevenCopy the code

Advantages:

The proxy enables the client to know nothing about what the implementation class is and how it does it, and the client only needs to know about the proxy (uncoupling). For the client code above, newUserManagerImpl() can apply a factory to hide it, just as an example.

Disadvantages:

1) The proxy class implements the same interface as the delegate class, and the proxy class implements the same methods through the delegate class. This leads to a lot of code duplication. If an interface adds a method, all proxy classes need to implement the method in addition to all implementation classes. Increases the complexity of code maintenance.

2) Proxy objects serve only one type of objects, if you want to serve multiple types of objects. You have to proxy for every kind of object, and static proxies don’t work when your program is a little larger. The code above provides a proxy only for access to the UserManager class, but if we were to provide a proxy for other classes, such as the Department class, we would need to add the proxy for The Department class again.

For example, the proxy can uniformly manage the implementation class. For example, before calling the implementation class, logs and other information need to be printed. In this way, we only need to add a proxy class, add the log printing function in the proxy class, and then call the implementation class, so as to avoid modifying the implementation class. Satisfies what we call the open close principle. However, if you want each implementation class to add log printing function, you need to add multiple proxy classes, and each method in the proxy class need to add log printing function (delete, modify, and query proxy method above need to add log printing function).

That is, static proxy classes can only serve specific interfaces (services). If you want to serve multiple interfaces, you need to create many proxy classes.

1.2 Dynamic Proxy

Based on the above introduction, you will find that each proxy class can only serve one interface, so there will inevitably be many proxy classes in program development

Therefore, we will find a way to do all the proxy function through a proxy class, so we need to use dynamic proxy

In the example above, an agent can only delegate one type, and the proxied object is determined at the compiler. Dynamic proxy is realized at run time through reflection mechanism, and can proxy various types of objects

In Java to realize the dynamic Proxy mechanism, need Java. Lang. Reflect. InvocationHandler interface and Java. Lang. Reflect. The Proxy class support

Java. Lang. Reflect the InvocationHandler interface are defined as follows:

Take a 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 method needs to dynamically generate a class that implements the interface for which the proxy is required, and then create 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

@test public void test_dynamic_proxy() {/** * Then the object that creates this class * needs to generate a class, which also needs to be loaded 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[]{UserManager.class}, invocationHandler); // The generated proxy object implements the UserManager interface UserManager UserManager = (UserManager) obj; userManager.addUser("steven"); }Copy the code

After running the test, we can see that nothing happens. This is because we did not add implementation logic to the proxy object at all. But where is the implementation logic added? In the InvocationHandler, of course. Let’s take a look at the code with the implementation logic added:

@Test public void test_dynamic_proxy() { ClassLoader classLoader = this.getClass().getClassLoader(); InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("add user:" + Arrays.toString(args)); return null; }}; Object obj = Proxy.newProxyInstance(classLoader, new Class[]{UserManager.class}, invocationHandler); UserManager userManager = (UserManager) obj; userManager.addUser("steven"); } add user:[Steven]Copy the code

Here we find that all the implementation logic of the UserManager interface invokes the logic of the invoke method. In fact, except calling the native method of the proxy object, all other methods invoking the proxy object are essentially invoking the invoke method, and the return value of the Invoke method is the return value of invoking the proxy method.

2. Simple and AOP

Once we have some understanding of dynamic proxies, we can implement the most basic version of AOP, which, of course, is so incomplete that it can’t even be called an AOP implementation.

public class UserManagerHandle implements InvocationHandler { private UserManager userManager; public UserManagerHandle(UserManager userManager) { this.userManager = userManager; } @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("Before add user"); this.userManager.addUser(String.valueOf(objects[0])); System.out.println("After add user"); return null; }}Copy the code

Testing:

@Test public void test_aop() { ClassLoader classLoader = this.getClass().getClassLoader(); UserManagerHandle userManagerHandle = new UserManagerHandle(new UserManagerImpl()); Object obj = Proxy.newProxyInstance(classLoader, new Class[]{UserManager.class}, userManagerHandle); UserManager userManager = (UserManager) obj; userManager.addUser("steven"); } // Output: Before add user UserManagerImpl add user name is: Steven After add userCopy the code

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 userManager, enhanced to System.out.println(“Before add user”); And the System. The out. Println (” After the add user “); , the pointcut is the addUser() method call. In fact, you can still think of it as the original AOP.

3. Improve the AOP

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.

Add a front enhancement and a back interface:

public interface BeforeAdvice { void before(); } public interface AfterAdvice { void after(); } @Setter public class ProxyFactory { private Object targetObject; // Target object private BeforeAdvice BeforeAdvice; Private AfterAdvice AfterAdvice; Public ProxyFactory(Object targetObject) {this.targetobject = targetObject; } public Object creatProxy() { InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object Invoke (Object Proxy, Method Method, Object[] args) throws Throwable {// Invoke (Object Proxy, Method Method, Object[] args) throws Throwable {// Invoke (Object proxy, Method Method, Object[] args) throws Throwable { = null) { beforeAdvice.before(); } Object result = method.invoke(targetObject, args); // Call the target method of the target object // perform subsequent enhancement if (afterAdvice! = null) { afterAdvice.after(); } // Return the target object return value return result; }}; ClassLoader classLoader = this.getClass().getClassLoader(); Class[] interfaces = targetobject.getClass ().getinterfaces (); Return proxy. newProxyInstance(classLoader, interfaces, invocationHandler); }}Copy the code

Testing:

@Test public void test_aop() { ProxyFactory proxyFactory = new ProxyFactory(new UserManagerImpl()); proxyFactory.setBeforeAdvice(() -> { System.out.println("before action"); }); proxyFactory.setAfterAdvice(() -> { System.out.println("after action"); }); UserManager userManager = (UserManager)proxyFactory.creatProxy(); userManager.addUser("steven"); } Before Action UserManagerImpl add user name is: Steven after actionCopy the code

AOP (AspectOrientedProgramming) : The log records, performance statistics, security control, transaction processing, the exception handling code division from the business logic code, such as through the separation of these actions, we hope they can be independent to the guidance of business logic method, and then change the behavior does not affect the business logic code – decoupling.

The UserManagerImplProxy class has two methods system.out.println (“before Action “) and system.out.println (” After Action “) These are the two intercepts before and after you do the core action, and these are the two intercepts that are the foundation of AOP, and in OOP, System.out.println(“before Action “), the core action, and system.out.println (“after Action “) are three actions that are always together in multiple classes, but the logic they need to accomplish is different. For example, system.out.println (“before Action “) may do a permission check. In all classes it does a permission check. In each class the core action is different. It does logging in all classes. Because in all classes, the operations before the core code do the same logic as the operations after the core code, we need to extract them, analyze, design, and code them separately. This is our AOP idea. In short, AOP simply abstracts OOP further, making the responsibility of a class more monolithic.

Dynamic proxy advantages:

The biggest advantage of dynamic proxies compared to static proxies is that all methods declared in the interface are moved to a centralized method of the calling handler (InvocationHandler.invoke). In this way, when the number of interface methods is large, we can do flexible processing without the need for each method to be mediated like a static proxy. And the application of dynamic proxy makes our class responsibility more simple, more reusable

4. To summarize

Agency is when a person or agency acts on behalf of another person or agency. In some cases, a client may not want or be able to reference an object directly, and a proxy object can act as an intermediary between the client and the target object.

Proxy object is a layer of proxy object, inside the proxy object to do some extra work, for example, users need to go to Facebook, ordinary network cannot directly access, network proxy helps users to climb the wall, then visit Facebook. That’s where agency comes in.

All round the static and dynamic agents, they can achieve the same function, and we see from the static to the dynamic proxy agent of this process, we will find that in fact the dynamic proxy just made a further abstraction and encapsulation, the class to make its reusability and ease of further ascension which not only conforms to the object-oriented design concept, with the figure of AOP, This also gives us a reference to class abstraction. About the relationship between dynamic proxy and AOP, PERSONALLY feel that AOP is an idea, and dynamic proxy is an implementation of AOP ideas!

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) {
        this.setTarget(target);
        this.setInterfaces(ClassUtils.getAllInterfaces(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(@Nullable 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