Implement multiple proxies using cglib

Source: Internet
Author: User
Tags call back
Preface

Due to the design of cglib itself, a proxy (JDK proxy is acceptable) cannot be encapsulated outside the proxy. the following error is usually reported:

Caused by: java.lang.ClassFormatError: Duplicate method name "newInstance" with signature "..........at java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:763)... 10 more

Error Source Code:

Net. SF. cglib. Proxy. enhancer # generateclass (classvisitor V)

... The code is omitted. // the following part of the bytecode will be inserted every time a proxy instance is generated. An error is reported when the JVM verifies the bytecode. If (usefactory | currentdata! = NULL) {int [] keys = getcallbackkeys (); emitnewinstancecallbacks (E); emitnewinstancecallback (E); Round (E, constructorinfo); emitgetcallback (E, keys ); emitsetcallback (E, keys); emitgetcallbacks (E); emitsetcallbacks (E );}

You can view the dump bytecode more intuitively:

In the generated bytecode, The newinstance method is repeated.

Dump method:System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");

What should I do?

There is a bad way to implement multiple proxies, such as using JDK and cglib in combination. Or you can directly use the JDK proxy. However, sometimes the operations on classes do not work.

Based on Spring's practice, I implemented a simple multi-proxy.

In spring, a target method is intercepted by multiple AOP, and multiple proxies are required.

The code used by spring to create a proxy is located at: org. springframework. AOP. Framework. cglibaopproxy # getproxy

Spring AOP interceptor class: org. springframework. AOP. Framework. cglibaopproxy. dynamicadvisedinterceptor

The intercept method of this class is the core of implementing multiple proxies.

Each call to the target method generates a call object based on the target method and multiple interception points of the target method.

// Generate the call object cglibmethodinvocation c = new cglibmethodinvocation (proxy, target, method, argS, targetclass, chain, methodproxy); // call C. Proceed ();

Then calling the proceed method of the parent class is actually a filter mode:

Public object proceed () throws throwable {If (this. currentinterceptorindex = This. interceptorsanddynamicmethodmatchers. size ()-1) {return invokejoinpoint ();} object interceptororinterceptionadvice = This. interceptorsanddynamicmethodmatchers. get (++ this. currentinterceptorindex); If (specified instanceof interceptoranddynamicmethodmatcher) {interceptoranddynamicmethodmatcher dm = (interceptoranddynamicmethodmatcher) interceptororinterceptionadvice; If (DM. methodmatcher. matches (this. method, this.tar getclass, this. arguments) {return DM. interceptor. invoke (this);} else {// skip this interceptor and invoke the next in the chain. recursion. return proceed () ;}} else {return (methodinterceptor) interceptororinterceptionadvice ). invoke (this );}}

Note that the last line calls the invoke method of the interception point. The specific implementation class of this interception point is aspectjaroundadvice.

Let's take a look at his invoke method:

Public object invoke (methodinvocation mi) throws throwable {proxymethodinvocation PMI = (proxymethodinvocation) Mi; // The familiar proceedingjoinpoint parameter in AOP !!!! Proceedingjoinpoint pjp = lazygetproceedingjoinpoint (PMI); joinpointmatch JPM = getjoinpointmatch (PMI); Return invokeadvicemethod (pjp, JPM, null, null );}

Generally, when we compile the AOP interception code in the business, we will be exposed to this proceedingjoinpoint parameter, and then call its proceed method to call the target method.

The proceed method of the proceedingjoinpoint class will eventually call back the proceed method of the dynamicadvisedinterceptor pair. Until all the interception points are completed. The method of the final execution target class.

Therefore, if each method you set is intercepted multiple times, multiple methodinterceptor (not cglib) instances form a call chain. Then pass the proceedingjoinpoint to you to intercept and use it.

With so many preparations, we can implement a simple one by ourselves. It cannot be as complicated as spring !!!!

Simple implementation of multiple cglib proxies

Let's talk about the idea: in fact, it is very simple. You only need to put a filter chain in the interceptor, and the user can intercept multiple calls in the filter. These interceptors are like the method you add @ around annotation, but we are not as convenient as spring here.

Draw a UML diagram:

The Code is as follows:

Test. Java & sayhello. Java

public class Test {    public static void main(String[] args) {        Object proxy = ProxyFactory.create().getProxy(new SayHello());        proxy.toString();    }    static class SayHello {        @Override        public String toString() {            return "hello cglib !";        }    }}

Proxyfactory. Java & Interceptor. Java

public class ProxyFactory {    private ProxyFactory() {}    public static ProxyFactory create() {        return new ProxyFactory();    }    public Object getProxy(Object origin) {        final Enhancer en = new Enhancer();        en.setSuperclass(origin.getClass());        List<Chain.Point> list = new ArrayList<>();        list.add(new Point1());        list.add(new Point2());        en.setCallback(new Interceptor(new Chain(list, origin)));        return en.create();    }    private class Interceptor        implements MethodInterceptor {        Chain chain;        public Interceptor(Chain chain) {            this.chain = chain;        }        @Override        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)            throws Throwable {            return chain.proceed();        }    }}

Chain. Java & point. Java

public class Chain {    private List<Point> list;    private int index = -1;    private Object target;    public Chain(List<Point> list, Object target) {        this.list = list;        this.target = target;    }    public Object proceed() {        Object result;        if (++index == list.size()) {            result = (target.toString());            System.err.println("Target Method invoke result : " + result);        } else {            Point point = list.get(index);            result = point.proceed(this);        }        return result;    }    interface Point {        Object proceed(Chain chain);    }}

Point1.java & point2.java

public class Point1 implements Chain.Point {    @Override    public Object proceed(Chain chain) {        System.out.println("point 1 before");        Sleep.sleep(20);        Object result = chain.proceed();        Sleep.sleep(20);        System.out.println("point 1 after");        return result;    }}public class Point2 implements Chain.Point {    @Override    public Object proceed(Chain chain) {        System.out.println("point 2 before");        Sleep.sleep(20);        Object result = chain.proceed();        Sleep.sleep(20);        System.out.println("point 2 after");        return result;    }}

Run test main:

As expected.

Implement multiple proxies using cglib

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.