Design pattern: Proxy Pattern

Static proxy:

An object uses multiple proxies. The class that implements the proxy is xxxProxy. Proxy also implements the interface of the proxy object, so nested proxy can be realized. The proxy class of static proxy is written in advance. The premise is that we know the proxy method of the proxy object by implementing the specified interface.

Dynamic proxy:

Through reflection, the JDK dynamically creates proxy objects. This method completely depends on the interface. The proxy object must implement an interface, otherwise the JDK does not know what method to write. This is also the limitation of JDK dynamic agent. The bottom layer of JDK dynamic agent is realized by asm class library. asm class library is a class library that can manipulate binary codes. asm directly modifies binary files to implement methods.
The following is a detailed explanation of the Proxy class of Java.

newProxyInstance (ClassLoader loader, class <? > [] interfaces, invocationhandler h) ClassLoader loader: classloader class of the proxied object <? > [] interfaces: the interface array implemented by the proxy object
InvocationHandler h: process of proxy method

code implementation

    public static void main(String[] args) {
        Tank tank = new Tank();
        Moveble proxy1 = (Moveble)Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{Moveble.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("proxy1");
                Object o = method.invoke(tank, args);
                return o;
            }
        });
        proxy1.move();
}

cglib

Compared with jdk dynamic agent, it has a wider scope of application, and asm is also used at the bottom. Cglib implements proxy by generating a subclass of the proxied object and overriding the parent method. The defect is that if the proxied object is modified by final, it cannot generate subclasses, and cglib cannot be used for proxy.

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Tank.class);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before");
                Object invoke = methodProxy.invokeSuper(o, objects);
                System.out.println("after");
                return invoke;
            }
        });
        Tank tank = (Tank)enhancer.create();
        tank.move();
    }

spring aop agent

First, add the dependencies required by spring

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.19</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.19</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.19</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.19</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

Write appcontext XML configuration file
Here's a note: the proxy target class in aop:config is set to true, otherwise an Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to will be reported. If the proxy class itself is an interface, it is true by default.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="Tank" class="StaticProxy.Tank"/>
    <bean id="TankProxy" class="spring.aop.TankProxy"/>

  <aop:config proxy-target-class="true">
        <aop:aspect id="proxy" ref="TankProxy" >
<!--           Cut plane: function of inserting-->
            <aop:pointcut id="move" expression="execution(void StaticProxy.Tank.move())"/>
<!--            Pointcut: the method of being cut in-->
            <aop:after method="after" pointcut-ref="move"/>
<!--            Execute after method-->
            <aop:before method="before" pointcut-ref="move"/>
<!--            Execute before method-->
         </aop:aspect>
    </aop:config>
</beans>

test

package spring.aop;

import StaticProxy.Tank;
import javafx.application.Application;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appContext.xml");
        Tank tank = (Tank)context.getBean("Tank");
        tank.move();
    }
}

Another way is to make it simpler and faster through spring annotations.
The configuration file is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <aop:aspectj-autoproxy/>

    <bean id="Tank" class="StaticProxy.Tank"/>
    <bean id="TankProxy" class="spring.aop.TankProxy"/>

</beans>

proxy class

package spring.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class TankProxy  {
    @Before("execution(void StaticProxy.Moveble.move())")
    void before(){
        System.out.println("before");
    }
    @After("execution(void StaticProxy.Moveble.move())")
    void after(){
        System.out.println("after");
    }
}

Do you remember our error report just now? hey! Using this method, the same error will be reported. Just change the type of cast to an interface and there will be no problem! For example, if Tank implements the Moveable interface, I will cast it as Moveable instead of directly cast it as Tank. Because if it is an interface, proxy target class defaults to true.

Tags: Design Pattern AOP Dynamic Proxy Singleton pattern cglib

Posted by bitt3n on Tue, 26 Apr 2022 07:59:29 +0300