Dynamic proxies proxy mode -JDK

Dynamic proxies proxy mode -JDK

No. JavaStorm public attention for more technical.

Provided on behalf of another object in order to control client access to the object. Which is defined as: alias or placeholder to provide access to this object to another object. Specifically, you can view github.com/UniqueDong/...

What is a proxy

Mandarin speaking is a design pattern in the hope that code reuse. With the way we used to visit different objects, proxy mode is not directly through the target object, but through proxy access our target audience and methods. Because sometimes we are unable to establish a direct contact with the target object or, we have to control client access. So they come to visit our real object through a proxy.

Like "Customer" -> "star brokers" -> "star." We are not directly linked with the star, the star's busy, sing and dance to hot head movie, good enough for the price, brokerage personnel informed the star took on the task.

main character

Proxy mode

  • Subject : Theme interface is to be implemented in the proxy class and proxy class.
  • ConcreteSubject : the proxy class, also known as the delegate class, which is the real object, the real work.
  • Proxy : the role of the proxy class, actor intermediaries, control client access to the real object, generating a proxy object lets transparent to the client calls, we can shield or by proxy processing for the real object.

scenes to be used

  • More generally appear in our popular open source frameworks inside, such as Mybatis we can execute SQL calls to the real logic of the interface via Mapper, its essence is the use of dynamic proxy, navigate to the real implementation of the Statement.
  • Spring AOP is the use of dynamic proxies, such as the Spring transaction when the method is invoked by Spring transaction management, when in fact he will generate a proxy class that encapsulates our transaction processing logic in order to achieve enhanced transaction, code reuse. We turned on the liberation of the transaction, execution logic, template code to submit the transaction or roll back the transaction, and we just feel at ease to write roadbed code. Here used a total of "template method pattern" dynamic proxy mode. About " template method " pattern can refer to the history of the article.
  • There Dubbo RPC framework, is also using dynamic proxies. For consumers we will be able to call functions only through the interface provided by those who realize, as the same as a local call. It encapsulates our network transmission, serialization, tedious details of decoding encoded. A proxy class is generated for the low-level details of the shield. It allows us to transparently calls.

We load is different according to the timing of the proxy class, the agent will be divided into static and dynamic proxy agent. If we determine that the proxied class in the code compile time which one, then you can directly use a static proxy; If you are unsure, then the dynamic loading mechanism can use the class, during which the code is running to load the proxy class that is dynamic agents, such as Spring AOP framework and RPC mechanism.

Static proxy code examples

We follow the UML class diagrams to implement a simple static proxy mode, first create a Subject interface.

public interface Subject {

    public void request();
}

复制代码

To create a real object that implements this interface

public class RealSubjbect implements Subject {

    @Override
    public void request() {
        System.out.println("this is RealSubjbect.request()");
    }
}

复制代码

Create our proxy class, holding a reference to the real object, while achieving Subject interface.

public class ProxySubject implements Subject {

    private Subject realSubjbect = null;

    /**
     * 除了代理真实角色做该做的事,代理角色提供附加操作
     * 如
     */
    @Override
    public void request() {
        preRequest(); //真实角色操作前的附加操作

        if (realSubjbect == null) {
            realSubjbect = new RealSubjbect();
        }
        realSubjbect.request();

        postRequest();//真实角色操作后的附加操作
    }
    /**
     *	真实角色操作前的附加操作
     */
    private void postRequest() {
        System.out.println("真实角色操作后的附加操作");

    }

    /**
     *	真实角色操作后的附加操作
     */
    private void preRequest() {
        System.out.println("真实角色操作前的附加操作");

    }
}

复制代码

Write our client interface, called on the proxy class.

public class Client {

    public static void main(String[] args) {
        Subject subject = new ProxySubject();
        subject.request(); //代理者代替真实者做事情
    }
}
复制代码

The results are shown below print, we realized the control of the real object, and add some operations. Like function Spring AOP implementation of the same.

真实角色操作前的附加操作
this is RealSubjbect.request()
真实角色操作后的附加操作
复制代码

Shortcoming

  1. An interface proxy object to serve only one type of object, if you want to proxy many ways, is bound to have carried the agent for each method, the static agent in the slightly larger size of the program will not be able to perform competently.
  2. If the interface to add a method, in addition to all the implementation class needs to implement this method, all proxy class also need to implement this method. It increases the complexity of code maintenance.

Further, if you want to use proxy mode according to the above method, the true role (the delegate) must already exist in advance, and as a proxy object internal property. However, the actual use, a role must correspond to a real acting role, if heavy use can lead to rapid expansion of the class; moreover, does not know in advance if the real role (the delegate), how to use a proxy it? This problem can be solved by Java's dynamic proxy class.

Dynamic Proxy

concept

Dynamic proxy class JVM The reflection source is dynamically generated during program execution, the proxy class bytecode file does not exist. Relations proxy class and delegate class is determined at runtime.

Method to realize

There are two main ways:

  1. Use the JDK implementation. Reverse delegate class has an interface.
  2. Use CGLIB achieve. Modified class can not be final, modified only public methods.

JDK dynamic proxies

UML class diagram is shown below

JDK dynamic proxies

  • Create your own Handlerrealization InvocationHandler, while holding a real object reference, will depend on our business class.
  • By proxy object Proxy.newProxyInstance()is generated, and since the method in Handlerobject and the real object.

By class diagram In fact, we can also know that when the dynamic proxy by proxy bytecode generated calls will be entrusted to Handler invoke method. Meanwhile Handler and hold real business objects. We are able to control their behavior before executing the real object calls and visits. The main advantage:

  • Implementation hiding delegate class, the caller only needs to interact and proxy classes.
  • Decoupling, additional processing do not change under the circumstances delegate class code. Such as adding serialization, network transmission details at Dubbo client calls.

Code

By class diagram, we create a JDK dynamic proxy can be divided into three steps.

  1. Define business interfaces and implementation class is the agent.
  2. New Custom InvocationHandler implement the InvocationHandlerinterface and is dependent (real object referenced) proxy class.
  3. By Proxy.newProxyInstance()create specific proxy object methods.

Now we have a demand, call log to record our unified business logic, or transaction control, where we would write a log record is our proxy class log. Spring AOP similar simulation function, a simplified version of the example so that we understand their usage scenarios and principles.

The first step: we define your own business interface OrderServicewithProductService

public interface OrderService {
    String createOder(String no);
}

public interface ProductService {
    String getProduct(String no);
}
复制代码

Business logic implementation class

public class OrderServiceImpl implements OrderService {
    @Override
    public String createOder(String no) {
        return "生成订单" + no + "成功";
    }
}

public class ProductServiceImpl implements ProductService {
    @Override
    public String getProduct(String no) {
        return "获取商品" + no + "成功";
    }
}
复制代码

Step two: Defining Our Handler achieve the JDK Invocationhandler, the commission will hold a reference defined Objecttype, so you can delegate the proxy class for all logging needs, and we have a bind()method for setting the target at the same time returns the corresponding proxy class to let client calls. The third step of Proxy.newProxyInstance () method we bind on the inside. Code more concise.

public class LoggerInterceptor implements InvocationHandler {

    /**
     * 持有委托类的引用
     */
    private Object target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println(proxy.getClass().getInterfaces()[0]);
        //通过反射调用
        System.out.println("Entered " + target.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
        //调用目标对象的方法
        Object result = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println("执行结果:" + result.toString());
        System.out.println("共耗时:" + (end - start));
        return result;
    }

    /**
     * 获取代理类:并将 target 委托类绑定到 我们定义的Targe中
     * @param target 真实的委托类
     * @return
     */
    public synchronized Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}
复制代码

Finally, we start a new category to see results, there are two business logic classes require logging. Specific code comments See

public class BootStrap {
    public static void main(String[] args) {
      // 新建业务逻辑
        OrderService orderService = new OrderServiceImpl();
        ProductService productService = new ProductServiceImpl();
			// 新建我们的 日志 Handler
        LoggerInterceptor loggerInterceptor = new LoggerInterceptor();

      //绑定 委托类同时生产代理类调用 。
        OrderService orderServiceProxy = (OrderService) loggerInterceptor.bind(orderService);
        orderServiceProxy.createOder("12927381");

        ProductService productServiceProxy = (ProductService) loggerInterceptor.bind(productService);
        productServiceProxy.getProduct("34010234");
    }
}

复制代码

Print is as follows: We can see before and after each interface to perform the way we are agents of both print log and returns the result. In fact, Spring AOP is achieved through dynamic proxy.

nterface com.zero.headfirst.proxy.service.OrderService
Entered com.zero.headfirst.proxy.service.OrderServiceImpl-createOder,with arguments{12927381}
执行结果:生成订单12927381成功
共耗时:2
interface com.zero.headfirst.proxy.service.ProductService
Entered com.zero.headfirst.proxy.service.ProductServiceImpl-getProduct,with arguments{34010234}
执行结果:获取商品34010234成功
共耗时:1

复制代码

CGLIB dynamic proxy

CGLIB is a powerful, high-performance code generation package. It is widely used by many AOP frameworks, such as Spring AOP interception method provided for them (interception). CGLIB bottom package is through the use of a small, fast bytecode processing framework ASM, to convert byte code and generate a new class.

Final class can not be modified, and the final method, the interface may not be defined.

Use is simple: we must first add cglib dependence, a new class that implements MethodInterceptor.

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoggerProxy implements MethodInterceptor {


    /**
     * 创建代理类
     * @param targetClass 委托类
     * @return
     */
    public Object bind(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        //设置回调方,当客户端通过代理调用方法的时候会会调用我们重写的 intercept() 方法
        enhancer.setCallback(this);
        //创建代理类
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();
        //通过反射调用
        System.out.println("Entered " + o.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
        Object result = methodProxy.invokeSuper(o, args);
        long end = System.currentTimeMillis();
        System.out.println("执行结果:" + result.toString());
        System.out.println("共耗时:" + (end - start));
        return result;
    }
}

复制代码

Is not it simple? New Enhancer, as long as the delegate class and set the callback class. Here we can create a different kind of proxy corresponding callback using the factory pattern. Here a simple example do not write.

Next, look at our test results and class

public class CglibBootStrap {
    public static void main(String[] args) {

        LoggerProxy loggerProxy = new LoggerProxy();

        OrderService orderService = (OrderService) loggerProxy.bind(OrderServiceImpl.class);
        orderService.createOder("12873051209g");


        ProductService productProxy = (ProductService) loggerProxy.bind(ProductServiceImpl.class);
        productProxy.getProduct("2780935782309");


    }
}

复制代码

Printing results are shown below

Entered com.zero.headfirst.proxy.service.OrderServiceImpl$$EnhancerByCGLIB$$54d983a1-createOder,with arguments{12873051209g}
执行结果:生成订单12873051209g成功
共耗时:14
Entered com.zero.headfirst.proxy.service.ProductServiceImpl$$EnhancerByCGLIB$$4e2c7c36-getProduct,with arguments{2780935782309}
执行结果:获取商品2780935782309成功
共耗时:5

复制代码

Guess you like

Origin juejin.im/post/5d7b46f1f265da03a53a65ed