Java 动态代理实现

2017-10-07  本文已影响35人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

JDK 动态代理

代理模式是常用的 JAVA 设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 JDK 动态代理是 java.lang.reflect.* 包提供的方式,但是必须借助一个或多个接口才能产生代理对象。

说明
java.lang.reflect.Proxy 用来获取动态代理对象
java.lang.reflect.InvocationHandler 用来约束调用者实现

定义接口 IHelloWorld

public interface IHelloWorld {
    public void sayHelloWorld();
}

定义实现类 HelloWorldImpl

public class HelloWorldlmpl implements IHelloWorld {
    @Override
    public void sayHelloWorld() {
        System.out.println("Hello World !");
    }
}

定义代理类 JdkProxy

public class JdkProxy implements InvocationHandler {

    public Object target = null;

    public Object build(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("先处理~!!");
        Object obj = method.invoke(target, args);
        System.out.println("后处理~!!");
        return obj;
    }
  1. 第一步,建立代理对象和真实对象的关系。
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
参数 说明
target.getClass().getClassLoader() 类加载器
target.getClass().getInterfaces() 动态代理的下挂接口,即 IHelloWorld
this 实现方法逻辑的动态代理类,即 JdkProxy 本身
  1. 第二步,实现代理的逻辑方法。
invoke(Object proxy, Method method, Object[] args) 
参数 说明
proxy 代理对象,build 方法生成的对象
method 当前正在调度的方法
args 调度方法的参数
Object obj = method.invoke(target, args);

实现和输出结果

JdkProxy example = new JdkProxy();
IHelloWorld helloWorld = (IHelloWorld) example.build(new HelloWorldImpl());
helloWorld.sayHelloWorld();
先处理~!!
Hello World !!
后处理~!!

CGLIB 动态代理

CGLIB 是一个强大的,高性能,高质量的 Code 生成类库。它可以在运行期扩展 Java 类与实现 Java 接口。其底层是通过小而快的字节码处理框架 ASM(http://forge.ow2.org/projects/asm,使用 BSD License)来转换字节码并生成新的类。大部分功能实际上是 asm 所提供的,CGLIB 只是封装了 asm,简化了 asm 的操作,实现了在运行期动态生成新的 class。

说明
net.sf.cglib.core 底层字节码处理类,他们大部分与 ASM 有关系
net.sf.cglib.transform 编译期或运行期类和类文件的转换
net.sf.cglib.proxy 实现创建代理和方法拦截器的类
net.sf.cglib.reflect 实现快速反射和C#风格代理的类
net.sf.cglib.util 集合排序工具类
net.sf.cglib.beans JavaBean 相关的工具类

直接定义类 HelloWorldImpl

public class HelloWorldlmpl {

    public void sayHelloWorld() {
        System.out.println("Hello World !");
    }
}

定义代理类 CglibProxy

public class CglibProxy implements MethodInterceptor {

    public Object getProxy(Class cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(this);
        enhancer.setClassLoader(cls.getClassLoader());  
        return enhancer.create();
    }

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("先处理~!!");
        Object obj = methodProxy.invokeSuper(proxy, args);
        System.out.println("后处理~!!");
        return obj;
    }
}
  1. 第一步,建立代理对象和真实对象的关系。
enhancer.setSuperclass(cls);
enhancer.setCallback(this);
enhancer.setClassLoader(cls.getClassLoader());
return enhancer.create();
参数 说明
enhancer.setSuperclass(cls) 设置要代理的目标类,以扩展它的功能
enhancer.setCallback(this) 设置单一回调对象,在回调中拦截对目标方法的调用
enhancer.setClassLoader(cls.getClassLoader()) 设置类装载器
enhancer.create() 创建代理对象
类型 说明
net.sf.cglib.proxy.FixedValue 为提高性能,FixedValue 回调对强制某一特别方法返回固定值是有用的。
net.sf.cglib.proxy.NoOp 把对方法调用直接委派到这个方法在父类中的实现。
net.sf.cglib.proxy.LazyLoader 当实际的对象需要延迟装载时,可以使用 LazyLoader 回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
net.sf.cglib.proxy.Dispatcher Dispathcer 回调和 LazyLoader 回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
net.sf.cglib.proxy.ProxyRefDispatcher ProxyRefDispatcher 回调和 Dispatcher 一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。
  1. 第二步,实现代理的逻辑方法。
intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
参数 说明
proxy 代理对象
method 被代理的方法
args 该方法的参数数组
methodProxy 不是原始的 method,以提高性能

实现和输出结果

CglibProxy cglibProxy = new CglibProxy();  
HelloWorldImpl hw = (HelloWorldImpl) cglibProxy.getProxy(HelloWorldImpl.class);
hw.sayHelloWorld();
先处理~!!
Hello World !!
后处理~!!

参考资料

http://blog.csdn.net/zhoudaxia/article/details/30591941

上一篇 下一篇

猜你喜欢

热点阅读