三種代理框架使用簡介

因系統架構變動,需要使用到代理技術。研究了一下java中常用的3種代理方式,這裏做一下使用上的總結。下面分別對jdk、cglib、javaassit做一個簡單介紹。這裏漏了asm,是因爲asm使用起來較複雜,必須使用的場景極少。

jdk

使用jdk proxy的好處是,它是java自帶的,不需要再依賴任何第三方jar包。壞處是,它只能對接口做代理。

class InjectProxy implements InvocationHandler {

    private Object target;

    private InjectProxy(Object target){
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{

        return method.invoke(target, args);

    }

}

InvocationHandler ih = new InjectProxy( target );
ClassLoader classLoader = InjectProxy.class.getClassLoader();
return Proxy.newProxyInstance( classLoader, target.getClass().getInterfaces(), ih );

cglib

cglib大家都很熟,它封裝了asm的複雜接口,使用起來比較簡單。
cglib主要包含4個概念,BeanGenerator、Enhancer、MethodInterceptor、LazyLoader、Dispatcher。

BeanGenerator

beangenerator主要用於動態生成一個類的子類,可以給子類動態添加一些成員變量,自動生成Getter、Setter方法。缺點是它只能生成含默認構造函數的子類。

BeanGenerator gen = BeanGenerator();
gen.setSuperclass(SuperClass.class);
gen.addProperty("name", String.class);
Class subClazz = (Class)gen.createClass();
SuperClass obj = (SuperClass)gen.create();

Enhancer

enhancer用於實現某個方法調用的aop功能。enhancer生成對象會包含很多cglib自動添加進去的屬性,所以最後生成的對象會比較大。

MethodInterceptor

public class MethodInterceptorImpl implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            return proxy.invokeSuper(obj, args);
    }
}

//代理invoke方法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, new MethodInterceptorImpl()});
enhancer.setCallbackFilter(new CallbackFilter() {
    @Override
    public int accept(Method method) {
        //只攔截Algorithm接口定義的invoke方法
        if(method.getName().equals("invoke"))
            return 1;
        return 0;
    }
});
enhancer.setUseFactory(false);
//new 出對象
Object proxy = enhancer.create();

LazyLoader

lazyLoader是cglib的代理延遲初始化接口。當接口方法被第一次調用時,才確定實際要訪問的對象。什麼意思?看代碼

public class ConcreteClassLazyLoader implements LazyLoader{

    public class PropertyBean {
        private String propertyName;  
        public String getPropertyName() {
            return propertyName;
        }
        public void setPropertyName(String propertyName) {
            this.propertyName = propertyName;
        }
    }

    @Override
    public Object loadObject() throws Exception {  
        System.out.println("LazyLoader loadObject() ...");  
        PropertyBean bean=new PropertyBean();  
        bean.setPropertyName("lazy-load object propertyName!");  
        return bean;  
    }

    public static void main(String[] args){
        Enhancer enhancer=new Enhancer();  
        enhancer.setSuperclass(PropertyBean.class);  
        PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());  

        //此處會回調loadObject
        System.out.println(propertyBean.getPropertyName());

        System.out.println("after...");  
        //之後不再回調loadObejct,直接訪問第一次返回的對象 
        System.out.println(propertyBean.getPropertyName());  
    }
}

Dispatcher

Dispatcher功能與LazyLoader相同,只是dispatcher每次都會被回調。

public class ConcreteDispatcher implements Dispatcher{

    public class PropertyBean {
        private String propertyName;  
        public String getPropertyName() {
            return propertyName;
        }
        public void setPropertyName(String propertyName) {
            this.propertyName = propertyName;
        }
    }

    @Override
    public Object loadObject() throws Exception {  
        System.out.println("Dispatcher loadObject() ...");  
        PropertyBean bean=new PropertyBean();  
        bean.setPropertyName("Dispatcher object propertyName!");  
        return bean;  
    }

    public static void main(String[] args){
        Enhancer enhancer=new Enhancer();  
        enhancer.setSuperclass(PropertyBean.class);  
        PropertyBean propertyBean = (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteDispatcher());  

        //此處會回調loadObject
        System.out.println(propertyBean.getPropertyName());

        System.out.println("after...");  
        //每次都回調loadObejct
        System.out.println(propertyBean.getPropertyName());  
    }
}

cglib總結

BeanGenerator適合給子類加成員變量
MethodInterceptor 適合做方法攔截
LazyLoader、Dispatcher適合做對象路由

javaassist

javaassist的使用頻率少於cglib,因爲它使用起來比cglib要稍微麻煩些,可以通過動態拼接源碼來生成對象是它的強項。

生成子類

protected CtClass createSubClass(Class<?> clazz) throws CannotCompileException, NotFoundException, ClassNotFoundException{
    /*javassist生成具有needLog屬性的子類*/
    ClassPool pool = ClassPool.getDefault();

    String proxyClassName = clazz.getName() + "$LogAble";
    CtClass cc = pool.getOrNull(proxyClassName);
    if(cc == null){
        cc = pool.makeClass(proxyClassName);
        cc.setModifiers(Modifier.PUBLIC);

        //父類
        CtClass superClass = pool.getOrNull(clazz.getName());
        if(superClass == null)
            superClass = pool.makeClass(clazz.getName());
        cc.setSuperclass(superClass);

        //構造函數
        CtClass stringClass = pool.get("java.lang.String");
        CtClass[] constructorParamClassArr = new CtClass[]{stringClass};
        //生成一個同父類的構造函數
        CtConstructor ctc = CtNewConstructor.make(constructorParamClassArr,null,CtNewConstructor.PASS_PARAMS,null,null, cc); 
        cc.addConstructor(ctc);

        //接口
        addInterface(cc, LogAopFilter.class);

        //添加needLog屬性
        addClassProperty(cc, "needLog", Boolean.class);
    }
    /*javassist方法結束*/
    return cc;
}

/*
 * 子類添加接口
 */
protected void addInterface(CtClass cc, Class<?> interfaceClass){
    ClassPool pool = ClassPool.getDefault();
    CtClass ccInterface = pool.getOrNull(interfaceClass.getName());
    if(ccInterface == null)
        ccInterface = pool.makeInterface(interfaceClass.getName());
    cc.addInterface(ccInterface);
}

/*
 * 子類添加屬性
 */
protected void addClassProperty(CtClass cc, String name, Class<?> type) throws CannotCompileException{
    addClassProperty(cc, name, type, "");
}
protected void addClassProperty(CtClass cc, String name, Class<?> type, String decorate) throws CannotCompileException{
    CtField field = CtField.make(String.format("private %s %s %s;", decorate, type.getName(), name), cc);
    cc.addField(field);
    CtMethod getMethod = CtMethod.make(String.format("public %s get%s(){return %s;}", type.getName(), name.substring(0, 1).toUpperCase() + name.substring(1), name), cc);  
    cc.addMethod(getMethod);
    CtMethod setMethod = CtMethod.make(String.format("public void set%s(%s value) {this.%s = $1;return;}",name.substring(0, 1).toUpperCase() + name.substring(1), type.getName(), name), cc);  
    cc.addMethod(setMethod);
}

生成代理

javaassit生成代理對象的過程和cglib差不多。

private class MethodHandlerImpl implements MethodHandler {

    final Object delegate;

    MethodHandlerImpl(Object delegate) {
        this.delegate = delegate;
    }

    public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Throwable {
        return m.invoke(delegate, args);
    }
}

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setSuperclass(SuperClass.class);
proxyFactory.setInterfaces(new Class[] { SuperInterface.class });
Class<?> proxyClass = proxyFactory.createClass();
SuperInterface proxy = (SuperInterface) proxyClass.newInstance();
((ProxyObject) proxy).setHandler(new MethodHandlerImpl(delegate));

性能比較

對三種框架生成的代理對象的性能測試網上有很多,可以參考這篇

基本上的順序是
javaassit拼接源碼生成的方法 (5倍於) > cglib MethodInterceptor生成的方法 (2倍於) > jdk proxy生成的方法 > javaassist MethodInterceptor生成的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章