今天重新看了下动态代理,也参考了一些网上的文章,自己总结一下动态代理的理解
动态代理离不开java反射机制,动态+代理。动态是指在在运行时生成的代理类,现在很多框架都是利用类似机制来提供灵活性的扩展性,比如用来包装 RPC 调用,面向切面编程(AOP)等。动态代理最大用处就是解耦,对所定义的一些接口做些增加的作用,像事物,统一日志等.
AOP 思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强 !
- JDK 动态代理,被代理对象必须实现接口,利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;
- 字节码实现(比如说cglib/asm等),得用ASM开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
现在举个简单的例子:比如有一个工厂,需要销售,销售的话会找到代理商去销售,这里我把商店定义为代理商,去做销售操作
我们循序渐进来讲,先说静态代理
定义一个工厂类
package com.vincent.JDK.DynamicProxyNew;
public interface Factory {
public void sell(String produceName);
}
定义一个食品处理厂
package com.vincent.JDK.DynamicProxyNew;
public class FoodFactory implements Factory{
@Override
public void sell(String produceName) {
System.out.println("开始销售"+produceName+"咯");
}
}
定义一个商店
package com.vincent.JDK.DynamicProxyNew;
public class Store implements Factory {
private FoodFactory foodFactory;
Store(FoodFactory foodFactory){
this.foodFactory=foodFactory;
}
@Override
public void sell(String produceName) {
leaflet();
this.foodFactory.sell(produceName);
}
public void leaflet(){
System.out.println("开始派发传单,今天新到的西瓜只要4元一斤");
}
}
测试类
package com.vincent.JDK.DynamicProxyNew;
public class SimpleTest {
public static void main(String[] args) {
FoodFactory foodFactory=new FoodFactory();
Store store=new Store(foodFactory);
store.sell("西瓜");
System.out.println("纳入税务局税收统计");
}
}
运行后如图
开始派发传单,今天新到的西瓜只要4元一斤
开始销售西瓜咯
纳入税务局税收统计
因为每次销售后,都要打税,纳入税务局进行统计,所以我如果有N个商店是不是写调用N次打税接口,而且每个商店都有自己的行为,例如打广告,搞活动什么的,那每个商场都要写很多实现,岂不是爆炸了?所以我们现在需要写一个动态代理的操作,来简化这些操作,对于程序来说,代码实现就是强化的作用
我再编写一个handler类实现InvocationHandler接口,重写它的invoke方法
package com.vincent.JDK.DynamicProxyNew;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class StoreProxy implements InvocationHandler {
private Object object;
StoreProxy(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//参数:代理对象,接口
//内部实现原理通过java反射机制找到具体类,然后带入参数,再执行
//里面代码较为复杂,阅读能力强的同志可以自行查看
System.out.println("税务员进驻商场");
method.invoke(this.object,args);
//代理代码增加操作
System.out.println("纳入税务局税收统计");
return null;
}
}
编写一个动态代理测试类
package com.vincent.JDK.DynamicProxyNew;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
FoodFactory foodFactory=new FoodFactory();
StoreProxy store=new StoreProxy(foodFactory);
//传入类读取器,实现类的接口,以及代理对象
Factory factory=(Factory) Proxy.newProxyInstance(
foodFactory.getClass().getClassLoader(), foodFactory.getClass().getInterfaces(), store);
factory.sell("西瓜");
}
}
测试结果如下
税务员进驻商场
开始销售西瓜咯
纳入税务局税收统计
这里顺便更新一下CGLIB动态代理的实现,也很简单,类似jdk动态代理,也是需要做一个对MethodInterceptor拦截器类进行继承重载的操作
package com.vincent.JDK.CGLIB;
public class HelloService{
public HelloService() {
System.out.println("HelloService构造");
}
/**
* 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
package com.vincent.JDK.CGLIB;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.invoke.MethodHandleInfo;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
//1)obj表示增强的对象,即实现这个接口类的一个对象;
//
//2)method表示要被拦截的方法;
//
//3)args表示要被拦截方法的参数;
//
//4)Proxy表示要触发父类的方法对象;
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy Proxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = Proxy.invokeSuper(obj, objects);
System.out.println("======插入后者通知======");
return object;
}
}
public static void main(String[] args) {
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService proxy= (HelloService)enhancer.create();
// 通过代理对象调用目标方法
proxy.sayHello();
}
运行效果
CGLIB debugging enabled, writing to 'D:\code'
HelloService构造
======插入前置通知======
HelloService:sayHello
======插入后者通知======
可以在d盘的code下面找到那个新生成的动态代理类