preface

Proxy is a design pattern in which we use proxy objects instead of accessing real objects to extend the functionality of the target object by providing additional functional operations without modifying the original target object.

The proxy mode includes static proxy and dynamic proxy. Dynamic proxy is divided into JDK proxy and CGLIB proxy. Before we look at the code, let’s understand a few concepts: interfaces, interface implementation classes, and proxy classes.

An interface implementation class is a class to be proxied. If you do not want the interface implementation class to be accessed directly, you can generate a proxy class by proxy, meaning that a proxy class is generated by static or dynamic proxies. Then there is the question, how does the proxy class relate to the proxied class? Answer first, brother or father.

Static agent

From the JVM’s point of view, static proxies encode related classes into class files at compile time and then read the class files directly through the classloader at runtime. Since they are compiled first, this approach is not as flexible as dynamic proxies, and the proxied classes need to implement interfaces. Code implementation: the code is not shown here, because the actual application scenarios are very few, the general logic is to create a proxy class implementation interface, in use, you can pass other interface implementation class instances to this created proxy class, by proxy class to access the interface implementation class.

A dynamic proxy

Dynamic proxies are more flexible in that they dynamically generate bytecode like code at run time and load it into the JVM. The JDK proxy requires the propped class to implement the interface; the CGLIB proxy does not.

The JDK agent

1. The OrderService interface:

public interface OrderService {

    List<Order> findOrders(a);
}
Copy the code

2.OrderService interface implementation class (proxied class) :

public class OrderServiceImpl  implements OrderService{

    @Override
    public List<Order> findOrders(a) {
        System.out.println("Add products");
        List<Order> orders = new ArrayList<>();
        orders.add(new Order(111."Electronic products"."2020-10-10".3000));
        orders.add(new Order(222."Book Products"."2020-10-10".3000));
        orders.add(new Order(111."Sports products"."2020-10-10".3000));
        returnorders; }}Copy the code

3. Proxy Generation:

public class JdkProxy<T> {

    // Target object
    Object target;

    public JdkProxy(Object target) {
        this.target = target;
    }

    public Object getProxyInstance(a){
        return 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("Start transaction");
                // Call the target method
                Object result = method.invoke(target,args);
                System.out.println("Close transaction");
                returnresult; }}); }}Copy the code

4. Actual use

public class TestProxy {

    public static void main(String[] args) {
        JdkProxy<OrderServiceImpl> jdkProxy = new JdkProxy<>(new OrderServiceImpl());
        // Get the object representing the class
        OrderService orderService = (OrderService)jdkProxy.getProxyInstance();
        orderService.findOrders();
        System.out.println("131313"); }}Copy the code
  • Tips:

In the above code, an error is reported if the OrderService in this line is replaced with OrderServiceImpl to receive the proxy class

OrderService orderService = (OrderService)jdkProxy.getProxyInstance();
Copy the code

Why? Because the generated proxy class is a sibling of the proxied interface implementation class OrderServiceImpl, receiving with OrderServiceImpl is bound to report an error.

A dynamic proxy

1. The proxied class does not need to implement the interface, and OrderServiceImpl does not implement OrderService

public class OrderServiceImpl {
    @Override
    public List<Order> findOrders(a) {
        System.out.println("Add products");
        List<Order> orders = new ArrayList<>();
        orders.add(new Order(111."Electronic products"."2020-10-10".3000));
        orders.add(new Order(222."Book Products"."2020-10-10".3000));
        orders.add(new Order(111."Sports products"."2020-10-10".3000));
        returnorders; }}Copy the code

2. Import POM files. To use CGLIB, manually add dependencies

<dependency> <groupId>cglib</ artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>Copy the code

3. Use directly

public class TestProxy {
    public static void main(String[] args) {
        / / class
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderServiceImpl.class);
        / / intercept
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("Advance notice");
                // Call the method of the target class
                Object result = methodProxy.invokeSuper(o, args);
                System.out.println("Post notification");
                return null; }});// The proxy class is generated
        OrderServiceImpl orderService = (OrderServiceImpl)enhancer.create();
        orderService.findOrders();
        System.out.println("Test..."); }}Copy the code

Enhancer is an enhanced class that implements MethodInterceptor using an anonymous inner class and creates a proxy class through create() of the Enhancer class

  • Tips:

The agent class generated by CGLIB is parent to the proxied class OrderServiceImpl. CGLIB uses the extends keyword internally, so the generated agent class can be received with OrderServiceImpl

conclusion

This article explains the differences between static proxies, which are compiled at compile time, and dynamic proxies, which are compiled at run time. There are two kinds of dynamic proxy: JDK proxy and CGLIB proxy. JDK proxy needs to implement interfaces, but CGLIB proxy does not have this limitation. In JDK proxy, the proxied class and the proxy class are brothers, and in CGLIB proxy, the proxied class and the proxy class are the parent.

Finally, Spring automatically uses dynamic proxies in JDK and JDK: 1. If the target object implements the interface, AOP 2 is implemented using the JDK’s dynamic proxy by default. CGLIB can also be forced to implement AOP 3 if the target object implements an interface. If the target object does not implement an interface and must switch between Cglib libraries, Spring will automatically switch between JDK and Cglib