代理模式就是当客户想访问一个对象的时候,找一个中介(代理对象)来代替真实对象。用户通过中介来实现真实对象的方法,并且在方法执行前后可以加上自己需要的其他附加操作。
1.1 静态代理
首先定义一个接口,这个接口表示一个动作,代理对象和真实对象都实现这个接口的动作。比如真实对象(房东)和代理对象(房屋中介)都实现出租房子Rent()这个接口。
接口
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(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();//调用了真实对象的方法
seeHouse();//附加方法
agencyFee();//附加方法
}
//带人看房
public void seeHouse() {
System.out.println("中介带人看房");
}
//收中介费
public void agencyFee(){
System.out.println("收中介费");
}
}
想租房的人(Client)
public class Client {
public static void main(String[] args) {
Host host= new Host();
//创建代理对象,传入真实对象
Proxy proxy=new Proxy(host);
proxy.rent();
}
}
运行结果
这里没有通过真实对象(房东)来执行租房这一行为,而是由中介代理执行,这就是代理模式
代理模式最主要的就是有一个公共接口(Rent),一个具体的类(Host),一个代理类(Proxy),代理类持有具体类的实例,代为执行具体类实例方法。
1.2 静态代理的缺点
-
静态代理类需要实现目标类(被代理类)的接口,并实现其方法,造成了代码的大量冗余。比如说,想要在每个代理的方法前都加上一个处理方法,这样就需要在每一个代理类的每个代理方法前加入beforeMethod()方法
-
静态代理只能对某个固定接口的实现类进行代理服务,一个接口的实现类对应一个代理类,其灵活性不强。故一般大项目不会选择静态代理。而动态代理一个代理类对应一个接口的所有实现类
2.动态代理
在静态代理里,代理类持有具体类的实例,代为执行具体类实例方法。
动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 动态代理是代理一个接口下的多个实现类
动态代理的步骤
- 创建InvocationHandler对象(传入真实对象),这个对象里invoke方法实现真实对象的方法和一些附加的方法
InvocationHandler handler = new MyInvocationHandler(host);
public class MyInvocationHandler implements InvocationHandler {
//被代理的对象
private Rent rent;
public MyInvocationHandler(Rent rent) {
this.rent =rent;
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里可以加一些其他公共操作
//args为调用方法对应的参数
Object result=method.invoke(rent,args);
//在这里可以加一些其他公共操作
return result;
}
}
- 创建一个新的Class对象(代理类的Class对象,用它生成代理对象proxy),拷贝接口的所有信息,这个对象中含有真实对象实现的接口中的所有方法和类结构信息,并且含有构造器(接口没有构造器,不能产生实例)
Class<?> ProxyClass = Proxy.getProxyClass(Rent.class.getClassLoader(), new Class<?>[] {Rent.class});
所以,一旦我们明确接口,完全可以通过接口的Class对象,创建一个代理Class(这个类和接口结构一样,只是多了一个构造器),通过代理Class即可创建代理对象
- 获得新的Class对象的带InvocationHandler参数Constructor构造器
Constructor<?> constructor = ProxyClass.getConstructor(InvocationHandler.class);
- 通过构造器,构造代理对象proxy
Rent proxy = (Rent) cons.newInstance(handler);
- 通过代理对象.方法来调用handler.invoke()方法。
代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
再联系到InvacationHandler中的invoke方法
我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了真实对象的相应方法
。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
proxy.rent();