代理模式(Proxy)
有两种代理模式:静态代理、动态代理
动态代理分为两大类:基于接口的动态代理(例如JDK动态代理)
基于类的动态代理(例如Cglib代理)
可以去了解一下JAVAssist(字节码类库)
实际上想到中介就简单多了,代理就相当于中介
静态代理
角色分析:
抽象角色:一般会使用接口或者类来解决(租房)
真实角色:被代理的角色(房东)
代理角色:代理真实的角色,代理真实角色后,一般会做一些附属操作,比如中介有收取中介费的操作(中介)
客户:访问代理对象的人(租房的人)
一个房东可能只出租一套房,但是中介可以代理n套房子,也就是说可以接进来n多个接口
UML类图
代码实现:
首先创建一个抽象的租房接口
//租房
public interface Rent {
public void rent();
}
创建真实角色(房东实现租房接口)
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
创建代理角色(中介实现租房接口)
//中介
public class Proxy implements Rent {
//使用组合
private Host host;//房东
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
}
//中介这里不仅可以租房,还可以看房
public void seeHouse() {
System.out.println("中介带你看房");
}
//收中介费
public void fare() {
System.out.println("中介收中介费");
}
//签租赁合同
public void hetong() {
System.out.println("中介签合同");
}
}
创建客户访问代理角色
//客户端:要租房的人
public class Client {
public static void main(String[] args) {
Host host = new Host();
//代理,代理角色一般会有一 些附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房子即可
proxy.rent();
}
}
静态代理模式的优缺点
优点:
1)可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
2)公共业务就交给了代理角色,实现了业务的分工
3)公共业务发生扩展的时候,方便集中管理
缺点:(可以使用动态代理解决)
一个真实角色就会产生一个代理角色,代码量会翻倍(例如租房的房东,卖车的车主等)
静态代理模式实现2
在公司里,修改原来的代码是大忌
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
//真实对象
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
//代理角色
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String msg) {
System.out.println("[info]使用了"+msg+"方法");
}
}
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.query();
}
}
动态代理模式(这里以JDK动态代理为例)
角色分析(与静态代理角色一样)
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理的本质就是反射机制实现的
首先要了解两个类:Proxy:代理;InvocationHandler:调用处理程序
InvocationHandler:是反射包下的接口,每个代理实例都有一个关联的调用处理程序。利用代理实例自动生成,InvocationHandler接口里仅仅只有一个方法invoke(proxy,method,args)处理代理实例上的方法调用,并返回结果。
Proxy:提供了创建动态代理类和实例的静态方法,也是由这些方法创建的所有动态代理的超类。里面有四个静态方法,这里用到了newProxyInstance(loader,interfaces,h)生成一个代理对象其参数为(类加载器来定义代理类,代理类实现的接口列表,调度方法调用的调用处理函数)
UML类图:
代码如下:
首先我们还是要有一个接口Rent
//租房
public interface Rent {
public void rent();
}
创建真实对象
//房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
创建代理处理程序类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//这是一个处理程序,并不是一个真实的代理类
//反射包下的,不要导错包
public class ProxyInvocationHandler implements InvocationHandler{
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy() {
//(代理人,被代理对象)
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质,就是使用反射机制实现
Object result = method.invoke(rent, args);
hetong();
fare();
return result;
}
//中介这里不仅可以租房,还可以看房
public void seeHouse() {
System.out.println("中介带你看房");
}
//收中介费
public void fare() {
System.out.println("中介收中介费");
}
//签租赁合同
public void hetong() {
System.out.println("中介签合同");
}
}
测试
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色,现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
//这里的proxy就是动态生成的 ,我们并没有写
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
动态代理模式的优化
我们可以将动态代理处理程序类弄成一个工具类
public class ProxyInvocationHandler implements InvocationHandler{
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy() {
//(代理人,被代理对象)
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
然后在客户端中:
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,现在不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置要代理的对象
pih.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService)pih.getProxy();
proxy.delete();
}
}
动态代理模式的优缺点
优点:
1)可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
2)公共业务就交给了代理角色,实现了业务的分工
3)公共业务发生扩展的时候,方便集中管理
4)一个动态代理类代理的是一个接口,一般就是对应的一类业务
5)一个动态代理类可以代理多个类,只要实现了同一个接口即可
动态代理模式和静态代理模式的区别说一个例子就很好理解了,现在是一个房东一个中介,那如果有两个房东呢?我们就需要一个与之对应的中介了,否则不符合开闭原则
代理模式的应用场景
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了开闭原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
代理模式与适配器模式
都是借助第三方去访问目标对象
适配器模式的重点在于衔接不同类型的类或接口
代理模式的重点在于处理不同类之间的逻辑实现