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));
    }

}

优质内容筛选与推荐>>
1、volatile 用法
2、python0.16------构造函数/析构函数/self详解/重写/访问限制/对象属性和类属性/@property/运算符重载
3、字符串的方法实例
4、如何实现兼容多浏览器的“设为首页”功能?
5、Zabbix 3.0 部署监控 [二]


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn