Java进阶之代理
Java中内置了java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来支持动态代理。
看之前我们先明白代理是什么?
代理就是被代理方授权给第三方来做被代理方的事情,这里的第三代理方就是被代理方的代理,哈哈哈。
代理方可以控制被代理方的访问,比如打官司,你要有事情,找我的代理律师,不要直接找我,但是代理律师和你谈的还是我的事情。
代理是一种设计模式,它为其他对象提供一种代理,以控制对这个对象的访问。
代码示例一下:
首先定义一个接口,接口可以为它的子类定义一种规范。
public interface IService {
void doSomething(int num);
}
然后实现类,这个类就是具体的业务操作,也就是我的事情
public class RealService implements IService {
public void doSomething(int num) {
System.out.println("解决我piao的事情,喜提【"+num+"】年缝纫机");
}
}
不用代理的情况下,我们就是直接被处理:
RealService me = new RealService();
me.doSomething(6);
这样的话,直接就上山干6年,么办法。
你肯定不想吧,你找个代理律师,这个piao的事情不直接找我RealService,我一句话不说,有事找我法务。
/**
* 建一个法务,代理律师,我可继承了IService,我也可以doSomething(num)
*/
public class ProxyService implements IService {
private IService realService;
public ProxyService(IService realService) {
this.realService = realService;
}
public void doSomething(int num) {
System.out.println("我是被代理方的代理律师,现在开始解决当事人piao问题");
System.out.println("哎,我的当事人是不满十八周岁,且初次违法,恳请法律减轻处罚! 减1年:"+num--);
System.out.println("哎,我的当事人对于自己的违法行为表现出深刻的悔过态度,承诺不再犯类似错误,恳请法律减轻处罚! 减1年:"+num--);
realService.doSomething(num);
System.out.println("事情解决,收取被代理方当事人报酬22万");
}
}
我们看通过上述代理类来解决这个事情:
// 新建业务类对象
IService realService = new RealService();
// 新建代理类对象,将业务类对象交给代理类代理
IService proxyService = new ProxyService(realService);
// 通过代理类去调用业务类的方法
proxyService.doSomething();
最后,本来要是直接面向业务类本身去doSomething(6)的话,体验卡有6年,但是使用代理后,减掉了2年。当然事后代理类还收取了报酬。
可以看到调用方法的前后都可以加一些业务类本身没有的骚操作,以达到业务类本身所不具有的功能。
以上是一个静态代理,静态代理在编译时就已经确定代理类和原始类的关系。通常,代理类和原始类实现相同的接口。代理类持有原始类的实例,并在其方法中调用原始类的方法,这样可以在调用前后添加额外的逻辑。
静态代理是有缺点的:
代码冗余:每个代理类都需要手动编写,如果接口方法很多,代理类会变得非常庞大。
灵活性差:一旦接口增加方法,所有代理类都需要修改,比如上面如果不只是piao了,还是强制piao,那就需要增加更多的骚操作方法来处理。
那么有没有一种更好的解决方案呢?
有,那就是动态代理!
动态代理中的代理类并不要求在编译期就确定,而且此时可能也不知道要代理哪些,可以在运行期动态生成,从而实现对目标对象的代理功能。
听起来好熟悉,是不是反射的时候就是这么说的: 编译期不确定,在运行期动态去操作类的实例,没错,Java动态代理底层也是用的反射机制。
使用Java动态代理:
要用java.lang.reflect.InvocationHandler接口来代替代理类继承的接口,这个接口只有一个invoke方法来支持传入要代理的method方法,并且使用反射去调用它。
要使用java.lang.reflect.Proxy类的newProxyInstance方法来创建代理类,三个参数分别是被代理业务类加载器、被代理业务类接口列表、代理类本身。
这样Java底层机制就会通过反射来完成代理。
class ProxyDong implements InvocationHandler {
private Object realService;
public Object proxy(Object obj) {
this.realService = obj;
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(), // 被代理业务类加载器
obj.getClass().getInterfaces(), // 被代理业务类接口列表
this // 代理类本身
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int num = (int) args[0];
System.out.println("我是被代理方的代理律师,现在开始解决当事人piao问题,当前体验卡年数:"+num);
num--;
System.out.println("哎,我的当事人是不满十八周岁,且初次违法,恳请法律减轻处罚! 减1年后年数:"+num);
num--;
System.out.println("哎,我的当事人对于自己的违法行为表现出深刻的悔过态度,承诺不再犯类似错误,恳请法律减轻处罚! 减1年后年数:"+num);
method.invoke(this.realService, num);
System.out.println("事情解决,收取被代理方当事人报酬22万");
return null;
}
}
观察发现不管是动态代理还是静态代理,肯定是要让代理去持有被代理的业务类。
使用动态代理解决:
RealService me = new RealService();
IService proxyService = (IService) new ProxyDong().proxy(me);
proxyService.doSomething(6);
这就是Java的动态代理,动态代理的优势:
无需手动编写代理类:动态代理自动生成代理类,减少了代码量。
高度灵活:代理类可以在运行时动态创建,不受接口方法数量限制。
也就是说,真实的被代理的业务类发生改变了,比如我们业务类中新加一个qiang()方法,动态代理类也不用修改,直接使用的时候继续调用就可以执行那一套减年套路:
proxyService.qinag(10);
这就是动态代理的强大之处,它能够自动适应接口的变化,无需手动更新代理类。
这里的减年骚操作只是其中的一种,我们还有很多的应用场景:
访问控制:如安全代理确保只有特定用户可以访问敏感操作。
日志记录:记录方法调用的时间和参数,便于监控和调试。
事务管理:在方法调用前后开启和提交事务。
性能优化:如缓存代理缓存结果,减少数据库访问次数。
等等...web开发中的spring框架中的拦截器(Interceptors)、面向切面编程(AOP)等等都是代理模式。
END