代理模式
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:不用随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
代理模式的关键点是:代理对象是对目标对象的扩展,并会调用目标对象
demo:租房问题
1. 静态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现 (租房接口)
- 真实角色 : 被代理的角色 (房东)
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .(中介,中介类要有真实角色对象)
- 客户 : 使用代理角色来进行一些操作 (租客)
接口:租房接口
//租房接口
public interface Rent {
void rent();
}
真实对象:房东
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东要出房子");
}
}
代理角色:中介
中介可以添油加醋
public class Proxy {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent(){
seeHouse();
host.rent();
commission();
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void commission(){
System.out.println("中介费");
}
}
客户
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮忙租房子,但有附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东,直接和中介
proxy.rent();
}
}
优点:真实角色更加纯粹 . 不再去关注一些公共的事情 ,实现了业务的分工 。公共业务发生扩展时变得更加集中和方便
缺点:类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
2. 动态代理
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理
2.1 JDK动态代理
- 利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
- 动态代理也叫做:JDK代理,接口代理
- 代理类所在包:java.lang.reflect.Proxy
代理类,两种写法
方法一
//为目标对象自动生成代理对象
public class ProxyUtil{
//目标对象
private Rent rent;
public ProxyUtil(Host host) {
this.rent = rent;
}
public Object getProxy(){
//第一个参数:类加载器,用来设置当前代理对象所属类的类加载器,只有他代理对象所属类才能被加载,让他们共用一个类加载器
//第二个参数:类的接口,目标对象要实现的功能
//第三个参数:执行处理器,用来设置代理对象如何实现目标对象的功能,两种方法给参数
//方法一:该类实现InvocationHandler,参数传this,此方法不好理解
//方法二:直接通过匿名内部类创建对象
ClassLoader loader = this.getClass().getClassLoader();
Class[] interfaces = this.rent.getClass().getInterfaces();
//三参数:代理对象,方法,参数
return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//设置代理对象如何实现目标对象的功能
//参数一为要代理的对象,参数二为参数列表
Mylogger.before(method.getName(), Arrays.toString(args));
return method.invoke(rent,args); //动态代理实现功能
}
});
}
}
方法二
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 {
//动态代理的本质就是反射
Object result = method.invoke(rent, args);
return result;
}
}
客户端
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置代理对象
pih.setRent(host);
//动态获取代理类
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理