代理模式
通过代理,控制对对象的访问。
可以详细控制某个或某类对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。
举个例子:用户邀请明星唱歌,可以通过经纪人作为代理。那么经纪人完成面谈合同安排等等,明星只负责前置处理技术后唱歌,如果经纪人负责多个明星,它可以为多个明星提供前置服务。
核心角色
抽象角色:定义代理角色和真实角色的公共对外方法。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。(关注真正的业务逻辑。)
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法实现抽象方法,并可以附加自己的操作。(将统一流程代码放在代理角色中处理。)
应用场景
安全代理:屏蔽对真实角色的直接访问
远程代理:RMI
延迟加载:先加载轻量级代理对象,真正需要再加载真实对象。
分类
静态代理
// 抽象角色
public interface Star {
void signContract(); //签合同
void bookTicket(); // 订票
void sing(); //唱歌
void collectMoney(); //收钱
}
// 真实角色实现抽象角色接口
public class RealStar implements Star {
@Override
public void signContract() {
System.out.println("realstar.sign");
}
@Override
public void bookTicket() {
System.out.println("realstar.book");
}
@Override
public void sing() {
System.out.println("realstar(singer1).sing");
}
@Override
public void collectMoney() {
System.out.println("realstar.collect");
}
}
// 代理角色实现抽象角色接口
// 代理角色可以实现签合同订票收钱等一系列功能,但不唯独不能唱歌
// 因此必须传入真实对象,调用真实对象的唱歌的方法
public class ProxyStar implements Star{
private Star star;
public ProxyStar(Star star) {
super();
this.star = star;
}
@Override
public void signContract() {
System.out.println("proxystar.sign");
}
@Override
public void bookTicket() {
System.out.println("proxystar.book");
}
@Override
public void sing() {
star.sing();
}
@Override
public void collectMoney() {
System.out.println("proxystar.collect");
}
}
// 客户端调用
// 客户端不需要在意每一项操作到底是谁执行的,只需要把要执行的操作告诉代理即可。
public class Client {
public static void main(String[] args) {
Star real = new RealStar();
Star proxy = new ProxyStar(real);
proxy.signContract();
proxy.bookTicket();
proxy.sing();
proxy.collectMoney();
}
}
动态代理
代理类由工具生成
JDK自带的动态代理
javaassist字节码操作实现
CGLIB
ASM
JDK自带的动态代理
java.lang.reflect.Proxy
动态生成代理类和对象
java.lang.reflect.InvocationHandler(处理器接口)
通过invoke方法实现对真实角色的代理访问。
每次通过Proxy生成代理类对象时都要指定对应的处理器对象。
定义一个Handler,传入realStar做参数,
public class starHandler implements InvocationHandler {
Star realStar;
public starHandler(Star realStar) {
super();
this.realStar = realStar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(realStar, args);
return null;
}
}
客户端:newProxyInstance
newProxyInstance方法用来返回一个代理对象,这个方法有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。
public class Client {
public static void main(String[] args) {
Star realStar = new RealStar();
starHandler handler = new starHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] {Star.class}, handler);
proxy.bookTicket();
proxy.sing();
}
}
我们发现proxy调用所有的所有方法实际上都是在通过invoke调用realStar的方法,invoke中的语句都会执行。那么我们可以在invoke中执行一些判断以及统一的业务逻辑,也就是把核心方法之外的进行同意流程控制,写法简单。
我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法。
比如,在客户端可以通过proxy代理调用sing,那么一定会首先签合同,再调用真实对象的sing,最后完成收款。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("签合同");
if(method.getName().equals("sing")) {
method.invoke(realStar, args);
}
System.out.println("收款");
return null;
}
其他:
AOP:通过预编译方式和运行期动态代理实现在不修改源码的情况下给程序统一添加代码。