JAVA代理:静态代理和动态代理
在java编程中代理是一种很常见的设计模式,它允许我们通过创建一个中间层来间接的访问某个对象,从而实现对该对象的控制和增加。它主要分为静态代理和动态代理,其中Spring AOP就是动态代理的典型例子。
举一个例子比如点外卖,我们只要通过外卖app进行下单,不需要我们自去取餐吧,是外卖骑手取完餐后送到我们手上,而外面骑手就是代理,帮我们取餐的。代理可以简单理解为一个对象不想干的事情,让其他对象帮你干。
静待代理
代理类实现与目标类相同的接口,使用时实例化代理类而不是目标类。
拿外卖取餐作为例子 :
接口:
public interface DineService {
void eat();
}
目标类:
public class DineServiceImpl implements DineService {
@Override
public void eat() {
System.out.println("点餐");
}
}
代理类:
public class DineServiceProxy implements DineService {
/**
* 接受目标类的对象 可以理解为dineService 就是目标类的引用
*/
DineService dineService;
public DineServiceProxy(DineService dineService) {
this.dineService = dineService;
}
/**
* 加入横切逻辑 调用目标方法 横切逻辑可以理解为你想让代理类帮你完成的事
*/
@Override
public void eat() {
dineService.eat();
System.out.println("取餐");
}
}
测试:
@Test
public void testEat() {
//目标类
DineServiceImpl dineService1Impl = new DineServiceImpl();
//拿到外卖 实例化代理类
DineService dineService = new DineServiceProxy(dineService1Impl);
//调用代理类方法
dineService.eat();
}
运行结果:
通过实例可以看到目标类(也就是我们自己)只负责点餐,而代理类(也就时外卖骑手)帮我们取餐。
在实际的应用中,静态代理常常用于已有的类进行加强或扩展,但是静态代理的缺点就是代理类无法做到通用,每增加一个目标类类,都需要手动创建一个代理类,这样会导致代理类的数量快速增加,增加了维护难度。我们可以使用动态代理来解决这个问题。
动态代理
1.代理对象不需要实现接口,但是目标对象要实现接口,否则不用动态代理
2.动态代理分为:JDK代理和CGLib代理
3.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
JDK代理(接口代理)
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:static Object newProxyInstance(ClassLOader, Class<?>[] interfaces,InvocationHandler h)
要求:目标类必须实现接口
实现:通过代理工厂类来创建目标代理对象
接口:
public interface OrderService {
void save();
}
目标类:
public class OrderServiceImpl implements OrderService {
@Override
public void save() {
System.out.println("-----------构建新订单----------");
System.out.println("订单生成中.....");
System.out.println("-----------订单生成完成---------");
}
}
代理工厂:
public class ProxyFactory<T> {
/**
* 代理的目标对象
*/
T target;
/**
* 创建代理实例时 传递目标对象
* @param target
*/
public ProxyFactory(T target) {
this.target = target;
}
/**
* 创建目标对象的代理类对象
* @return
*/
public T createProxy() {
/**
* newProxyInstance方法参数说明
* ClassLoader loader 类加载器
* Class<?>[] interfaces 代理类要实现的接口,即目标类实现的接口就是代理类要实现订单接口
* InvocationHandler h 代理类要做的工作
*/
return (T) Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
/**
*
* @param proxy 代理实例
* @param method 要执行的目标方法
* @param args 目标方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增加横切逻辑的地方
//添加日志
System.out.println("开始订单日志");
//添加事务
System.out.println("开启事务");
//调用目标方法 target目标对象
Object obj = method.invoke(target, args);
//提交事务
System.out.println("提交事务");
System.out.println("保存订单成功");
return obj; //将执行目标方法的返回值 返回
}
});
}
}
测试:
@Test
public void testProxy() {
ProxyFactory<OrderService> proxyFactory = new ProxyFactory<>(new OrderServiceImpl());
OrderService orderService = proxyFactory.createProxy();
orderService.save();
}
运行结果:
由此可见我们已经实现了目标类动态动态代理,这时候我们想换一个接口和目标类,看代理工厂还能不能实现
接口:
public interface GoodsService {
void structure();
}
目标类:
public class GoodsServiceImpl implements GoodsService {
@Override
public void structure() {
System.out.println("-------生成商品列表----------");
}
}
测试:
@Test
public void testProxy1() {
ProxyFactory<GoodsService> proxyFactory = new ProxyFactory<>(new GoodsServiceImpl());
GoodsService goodsService = proxyFactory.createProxy();
goodsService.structure();
}
运行结果:
我们发现换一个接口和目标类,代理工厂仍然能够运行,说明动态代理成功。但是代理工厂类代码多不够简洁,并且每次使用工厂类时还要进行实例化,这是我们可以对代理工厂类进行一个改造:
public class ProxyFactory2 {
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), target.getClass().getInterfaces(),
(proxy, method, args) -> {
//增加横切逻辑的地方
//添加日志
System.out.println("开始订单日志");
//添加事务
System.out.println("开启事务");
//调用目标方法 target目标对象
Object obj = method.invoke(target, args);
//提交事务
System.out.println("提交事务");
System.out.println("保存订单成功");
return obj; //将执行目标方法的返回值 返回
});
}
}
改造完成,测试一下:
@Test
public void testFactory2() {
GoodsService goodsService = ProxyFactory2.createProxy(new GoodsServiceImpl());
goodsService.structure();
}
运行结果:
结果显示正常表示工厂类改造成功,我们发现测试时,将创建代理工厂改成静态方法后,代码比之前少了几行,使用时也不需要在实例化工厂了。
CGLib代理
CGLib代理也被称为子代理,不要目标类实现接口。
使用前先导入CGLib的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
目标类:
public class OrderServiceImpl {
public void save() {
System.out.println("-----------构建新订单----------");
System.out.println("订单生成中.....");
System.out.println("-----------订单生成完成---------");
}
}
代理类:
public class ProxyFactory {
public static <T> T createProxy(T target) {
//1.创建工具类的实例
Enhancer enhancer = new Enhancer();
//2.设置代理类的父类 目标类类型
enhancer.setSuperclass(target.getClass());
//3.增加横切逻辑 设置方法拦截 拦截目标方法 增加逻辑
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 代理类实例
* @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("开始订单日志");
//添加事务
System.out.println("开启事务");
//调用目标方法 执行目标对象target中的method方法
Object obj = method.invoke(target, objects);
//提交事务
System.out.println("提交事务");
System.out.println("保存订单成功");
return obj; //将执行目标方法的返回值 返回
}
});
//4.返回创建代理类实例
return (T) enhancer.create();
}
}
测试:
@Test
public void testCGLib() {
OrderServiceImpl orderService = ProxyFactory.createProxy(new OrderServiceImpl());
orderService.save();
}
运行结果:
注意:
1.CGLib代理是在内存中动态构建子类,所以代理的类不能为final
2.目标对象的方法如果是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
测试:
~~~java
@Test
public void testCGLib() {
OrderServiceImpl orderService = ProxyFactory.createProxy(new OrderServiceImpl());
orderService.save();
}
运行结果:
[外链图片转存中…(img-xMiZOYF6-1697723037836)]
注意:
1.CGLib代理是在内存中动态构建子类,所以代理的类不能为final
2.目标对象的方法如果是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法