实在不想写了,把以前的动态代理翻出来贴这了
1. 代理模式
想了解动态代理首先要知道代理模式:
代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题。
先看下代理模式结构:

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。
其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
按照代理的创建时期,代理类可以分为两种:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
2. 静态代理实现
首先定义一个接口:老王
public interface ILaoWang {
void say();
}
委托类(被代理类),实现老王接口
public class LaoWangImpl implements ILaoWang {
@Override
public void say() {
System.out.println("lao wang is a salted fish");
}
}
代理类,同样实现老王接口,并为老王添加逻辑
public class LaoWangProxy implements ILaoWang {
@Override
public void say() {
LaoWangImpl laoWang = new LaoWangImpl();
System.out.println("lao wang is hungry");
laoWang.say();
System.out.println("lao wang is sleeping");
System.out.println("Zzz~~~");
}
}
测试老王代理
public class TestLaoWang {
public static void main(String[] args) {
LaoWangProxy laoWangProxy = new LaoWangProxy();
laoWangProxy.say();
}
}
输出:
lao wang is hungry
lao wang is a salted fish
lao wang is sleeping
Zzz~~~
通过以上代码我们总结下静态代理的优缺点
优点
静态代理实现了初步的解耦,应用方不用知道具体实现类(被代理类)的具体实现逻辑,只需知道代理类即可。
缺点
- 代理类和委托类都实现了相同的接口,意味着他们要实现接口所有的方法,即使不需要代理的方法,这就意味着出现大量的荣誉代码,增加了代码维护难度。
- 如果需要代码的类很多,那就要增加大量的代理类,这将是灾难性的。
3. 动态代理
为了避免静态代理的缺点,我们就会想办法通过一个代理类完成全部的代理功能,那么我们就需要用动态代理。
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。
动态代理有两种实现形式:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
java中如何实现动态代理
代理类需要实现 java.lang.reflect.InvocationHandler 并通过反射 java.lang.reflect.Proxy 调用代理类中的方法。
2. 实例
1. JDK动态代理实现步骤
- 通过实现 InvocationHandler 接口创建自己的调用处理器
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入 接口及委托类和上边代码一样
我们来看一下代理类的实现,创建一个LaoWangHandler并实现InvocationHandler
public class LaoWangHandler implements InvocationHandler {
private Object target;
public Object instance(Object target) {
// 被代理对象
this.target = target;
// 通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler
// 为被代理类生成动态代理实例
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* @param proxy 被代理的对象
* @param method 被代理的方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke " + method.getName());
method.invoke(target, args);
System.out.println("after invoke " + method.getName());
return null;
}
}
接下来看一下测试类
public class TestLaoWang {
public static void main(String[] args) {
LaoWangHandler invocationHandler = new LaoWangHandler();
ILaoWang laoWang = (ILaoWang) invocationHandler.instance(new LaoWangImpl());
laoWang.say();
}
}
输出
before invoke say
lao wang is a salted fish
after invoke say
可以直接使用lambda形式, 省略handler
public class Demo {
public static void main(String[] args2) {
LaoWangImpl laoWangImpl = new LaoWangImpl();
ILaoWang laoWang = (ILaoWang) Proxy.newProxyInstance(laoWangImpl.getClass().getClassLoader(),
laoWangImpl.getClass().getInterfaces(), (proxy, method, args) -> {
if (method.getName().equals("say")) {
System.out.println("lambda pattern before method ");
method.invoke(laoWangImpl, args);
}
return null;
});
laoWang.say();
}
}
2. Cglib实现动态代理
使用Cglib实现动态代理,完全不受代理类必须实现接口的限制,而且Cglib底层采用asm字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,Cglib不能对声明为final的方法进行代理,因为Cglib原理是动态生成被代理类的子类。
被代理类
public class LaoWangService {
public void say() {
System.out.println("lao wang is a salted fish");
}
}
定义一个方法拦截器
public class LaoWangInterceptor implements MethodInterceptor {
private Object object;
public Object instance(Object target) {
this.object = target;
// 1. Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
Enhancer enhancer = new Enhancer();
// 2. 将被代理类设置成父类
enhancer.setSuperclass(this.object.getClass());
// 3. 设置拦截器
enhancer.setCallback(this);
// 4. 动态生成代理类
Object proxy = enhancer.create();
return proxy;
}
/**
* @param o cglib生成的代理对象
* @param method 被代理对象的方法
* @param objects 参数
* @param methodProxy 代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("this is cglib before ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
// 调用被代理的方法
//调用proxy.invoke()方法,会报java.lang.StackOverflowError错误,原因是invoke()内部会一直被反复调用
//Object object = proxy.invoke(obj, args);
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("this is cglib after ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
return object;
}
}
测试
public class TestCglib {
public static void main(String[] args) {
// 把代理类的class文件存入本地,方便查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:\\CODE\\Java\\Demo\\demo3");
LaoWangInterceptor laoWangInterceptor = new LaoWangInterceptor();
// 动态生成代理类
LaoWangService instance = (LaoWangService) laoWangInterceptor.instance(new LaoWangService());
instance.say();
}
}
输出
CGLIB debugging enabled, writing to 'G:\CODE\Java\Demo\demo3'
this is cglib before ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
lao wang is a salted fish
this is cglib after ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑