Java动态代理

以前做动态代理都是通过Java的动态代理,今天看公司代码,公司的是使用Javassist来实现的代理,于是又去了解了一下动态代理的东西。

动态代理主要有三种实现方式:

1、 Jdk原生的动态代理

 Jdk动态代理要求被代理的对象必须实现一个接口,没有接口的条件下可以使用cglib

参考代码:

 

package cn.edu.knowledge.proxy;

public interface Person {
    
    void sayHello();
}
package cn.edu.knowledge.proxy;

public class Student implements Person{

    @Override
    public void sayHello() {
        
        System.out.println("I am student");
    }

}

 

package cn.edu.knowledge.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy implements InvocationHandler {
    private Object target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("Before say hello");

        Object result = method.invoke(target, args);

        System.out.println("After say hello");

        return result;
    }
    
    public Object getProxy(Object target){
        this.target = target;
        
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    
    public static void main(String[] args) {
        Person p = new Student();
        JDKProxy proxy = new JDKProxy();
        Person p2 = (Person) proxy.getProxy(p);
        p2.sayHello();
    }    

}

 

2、 动态字节码生成

使用动态字节码生成技术实现 AOP 原理是在运行期间目标字节码加载后,生成目标类 的子类,将切面逻辑加入到子类中,所以使用 Cglib 实现 AOP 不需要基于接口。

本节介绍如何使用 Cglib 来实现动态字节码技术。Cglib 是一个强大的,高性能的 Code 生 成类库,它可以在运行期间扩展 Java 类和实现 Java 接口,它封装了 Asm,所以使用 Cglib 前 需要引入 Asm 的 jar。 清单七:使用CGLib实现AOP

 

package my.test;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class CglibProxyTest implements MethodInterceptor {

    private CglibProxyTest() {

    }

    public static <T extends Test> Test newProxyInstance(Class<T> targetInstanceClazz){

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(targetInstanceClazz);

        enhancer.setCallback(new CglibProxyTest());

        return (Test) enhancer.create();

    }

    public Object intercept(Object obj, Method method, Object[] args,

            MethodProxy proxy) throws Throwable {

        return proxy.invokeSuper(obj, args);

    }

}

 

3、 自定义类加载器

如果我们实现了一个自定义类加载器,在类加载到 JVM 之前直接修改某些类的方法, 并将切入逻辑织入到这个方法里,然后将修改后的字节码文件交给虚拟机运行,那岂不是更 直接。 

Javassist 是一个编辑字节码的框架,可以让你很简单地操作字节码。它可以 在运行期定义或修改 Class。使用 Javassist 实现 AOP 的原理是在字节码加载前直 接修改需要切入的方法。这比使用 Cglib 实现 AOP 更加高效,并且没太多限制

package cn.edu.knowledge.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtField.Initializer;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;

public class JavassistTest {

    public static void main(String[] args) throws CannotCompileException,
            NotFoundException, SecurityException, NoSuchMethodException,
            InstantiationException, IllegalAccessException,
            ClassNotFoundException, IllegalArgumentException,
            InvocationTargetException {
        ClassPool pool = ClassPool.getDefault();
        CtClass cls = pool.makeClass("cn.edu.knowledge.TestClass");

        // 添加私有成员name及其getter、setter方法
        CtField param = new CtField(pool.get("java.lang.String"), "name", cls);
        param.setModifiers(Modifier.PRIVATE);
        cls.addMethod(CtNewMethod.setter("setName", param));
        cls.addMethod(CtNewMethod.getter("getName", param));
        cls.addField(param, Initializer.constant(""));

        // 添加无参的构造体
        CtConstructor cons = new CtConstructor(new CtClass[] {}, cls);
        cons.setBody("{name = \"Brant\";}");
        cls.addConstructor(cons);

        // 添加有参的构造体
        cons = new CtConstructor(
                new CtClass[] { pool.get("java.lang.String") }, cls);
        cons.setBody("{$0.name = $1;}");
        cls.addConstructor(cons);

        // 打印创建类的类名
        System.out.println("类名:"+cls.toClass());

        // 通过反射创建无参的实例,并调用getName方法
        Object o = Class.forName("cn.edu.knowledge.TestClass").newInstance();
        Method getter = o.getClass().getMethod("getName");
        System.out.println("name: "+getter.invoke(o));

        // 调用其setName方法
        Method setter = o.getClass().getMethod("setName",
                new Class[] { String.class });
        setter.invoke(o, "Adam");
        System.out.println("name: " + getter.invoke(o));

        // 通过反射创建有参的实例,并调用getName方法
        o = Class.forName("cn.edu.knowledge.TestClass")
                .getConstructor(String.class).newInstance("Liu Jian");
        getter = o.getClass().getMethod("getName");
        System.out.println("name: "+ getter.invoke(o));
    }

}

 

posted @ 2016-07-28 17:08  googlemeoften  阅读(422)  评论(0编辑  收藏  举报