代理模式应用场景十分广泛,随便一个
框架
都会用到,因此学好代理模式对后续框架学习是最基本的要素!!今天我们就来讲讲代理模式
!
代理模式
的定位,它是
结构性模式
Proxy pattern
通过代理,控制对对象的访问
可以详细的访问某个(某类)对象的实现,在调用这个方法前做前置处理,调用这个方法后做后置处理(AOP的微观实现)
代理模式是SpringAOP
的核心实现机制!
抽象角色
公共对外方法
,一般会使用接口或者抽象类来解决真实角色
业务
,供代理角色调用代理角色
客户
安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
static proxy
接下来以房东租房这件实例,来讲讲代理模式
房东
要租房
,如果房东
懒得管太多,这时候就需要一个中介
,来帮助房东
租房并打理一切事情,这时候租房者
就不需要直接和房东
打交道了,而是通过中介
间接和房东打交道,中介
就是中间者,代理了房东
,且可以在租房前后附加其他操作,比如:签合同,看房子等
这时候对象上述的四个角色就有四个对象:
抽象角色
:租房业务真实角色
:房东代理角色
:中介,可能还有带客户看房子等业务客户
:租房者接下来,我们通过代码还原上述四个角色
抽象角色:表示租房
这个业务,用接口实现
//租房
public interface Rent {
public void rent();
}
真实角色:代表房东
,实现租房业务接口
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子了");
}
}
代理角色:中介
,实现租房业务接口
因为代理了房东
,所以私有属性是房东对象,除了租房子业务外,可能还有看房、签合同、收中介费等业务
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//代理租房子
public void rent() {
seeHouse();
host.rent();
contract();
fare();
}
//看房
public void seeHouse() {
System.out.println("中介带你看房");
}
//签合同
public void contract() {
System.out.println("租赁合同");
}
//收中介费
public void fare() {
System.out.println("收中介费");
}
}
客户:租房者
,访问中介
public class Client {
public static void main(String[] args) {
//房东租房子
Host host = new Host();
//代理,中介帮房东租房子,并且有一些附属操作
Proxy proxy = new Proxy(host);
//不需要找房东,直接找中介租房即可
proxy.rent();
}
}
好处:
职责清晰
使真实角色
更加的简单专一,不管具体的业务
智能化
客户只需访问代理角色,减少了直接访问真实角色带来的问题
高拓展性
业务发展拓展的时候,方便集中管理
缺点:
可能上述例子过于简单,不能直观的感受到代理模式的好处,我们再举个例子加深理解
首先创建一个业务实现接口
npublic interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
然后再来一个业务实现类
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
再来个测试类
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.add();
userService.delete();
userService.update();
userService.query();
}
}
如果此时,我们需要增加一个日志业务,需要打印每个方法的执行
业务实现类
中的每个方法中都要修改public class UserServiceImpl implements UserService {
public void add() {
System.out.println("使用了add方法");
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("使用了delete方法");
System.out.println("删除一个用户");
}
public void update() {
System.out.println("使用了update方法");
System.out.println("修改了一个用户");
}
public void query() {
System.out.println("使用了query方法");
System.out.println("查询了一个用户");
}
}
业务很多的情况下,修改量十分大,这时候用代理模式
就能很好的解决我们的问题
我们新建一个业务代理类
,在业务实现类里只需增加一个方法就可以实现上述新增业务
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("使用了add方法");
userService.add();
}
public void delete() {
log("使用了delete方法");
userService.delete();
}
public void update() {
log("使用了update方法");
userService.update();
}
public void query() {
log("使用了query方法");
userService.query();
}
public void log(String msg) {
System.out.println("使用了" + msg + "方法");
}
}
然后修改测试类,进行测试
package demo2;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
由此可见:
高拓展性
dynamic proxy
动态生成
的,不是我们直接写好的接下来我们讲述
JDK自带的动态代理
需要了解:
Proxy
(代理)、InvocationHandler
(调用处理程序)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
每次通过Proxy生成的代理类对象都要指定对应的处理器对象,就是第三个参数
三个参数:
这里还是以上述租房子为实例,我们用动态代理的方式实现
Rent接口
(抽象角色) 和 Host类
(真实角色)不变
定义一个处理器接口实现类,继承InvocationHandler
其中的invoke方法实现对真实角色方法的调用,因此该类中有真实角色私有属性,为传参使用
package demo3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//定义真实角色
private Rent host;
//真实角色set方法
public void setHost(Rent host) {
this.host = host;
}
/**
生成代理类方法
1. 类加载器,为当前类即可
2. 代理类实现的接口
3. 处理器接口对象
**/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
host.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
//方法在此调用
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用真实角色方法
Object result = method.invoke(host, args);
//附加方法
seeHouse();
contract();
fare();
return result;
}
//看房
public void seeHouse() {
System.out.println("中介带你看房");
}
//签合同
public void contract() {
System.out.println("租赁合同");
}
//收中介费
public void fare() {
System.out.println("收中介费");
}
}
测试类
package demo3;
public class Client {
public static void main(String[] args) {
//真实角色:房东
Host host = new Host();
//处理器接口对象
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//设置要代理的真实角色
handler.setHost(host);
//动态生成代理类
Rent proxy = (Rent) handler.getProxy();
//调用方法
proxy.rent();
}
}
其他好处同静态代理