一、什么是代理模式?
代理模式给一个对象提供一个代理对象,并由代理对象控制对原对象的访问。
类比生活,代理相当于我们常说的中介。假设这么一种情境,如果我想租房,直接找到房东来租房是很不方便的,我们一般选择通过房屋中介来进行租房。在这里房东相当于是被代理的对象,而房屋中介就相当于代理。
二、有哪些代理模式?
代理模式可以分为两类:
- 静态代理:由程序员通过代码自己创建代理类,在程序运行之前,代理类的字节码文件就已经被创建了,即代理类在程序运行之前就已经确定了。
- 动态代理:动态代理可以在程序运行时,通过反射的方式动态创建代理类。
三、静态代理
采用前面租房的例子。
(1)我们需要有一个租房的接口,该接口定义了一个租房的方法:
public interface Rent {
void rentHouse();
}
(2)创建一个被代理的类,即房东,房东需要实现租房的方法,即实现租房接口:
public class Host implements Rent{
@Override
public void rentHouse() {
System.out.println("我要租房");
}
}
(3)创建一个代理类,即中介,也需要实现租房接口,并且把被代理的对象通过含参构造方法传递进来,并对对象的方法进行增强:
public class Proxy implements Rent {
private Rent rent;
public Proxy(Rent rent) {
this.rent = rent;
}
@Override
public void rentHouse() {
System.out.println("租房之前");
rent.rentHouse();
System.out.println("租房之后");
}
}
(4)测试类:
public class Test {
public static void main(String[] args) {
//采用原对象
Rent rent = new Host();
rent.rentHouse();
System.out.println("------------------");
//采用代理对象
Rent rentProxy = new Proxy(rent);
rentProxy.rentHouse();
}
}
(5)执行结果:
我要租房
------------------
租房之前
我要租房
租房之后
静态代理的优缺点:
1.优点: 可以在符合开闭原则的情况下对原对象进行功能扩展
2.缺点: 相对于动态代理,我们需要对每一个对象都创建一个对应的代理类,工作量太大,不便于管理。同时接口一旦发生改变,代理类也得相应修改。
四、动态代理(JDK动态代理)
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。
还是采用上面的例子:
(1)被代理类和租房接口都不变:
public interface Rent {
void rentHouse();
}
public class Host implements Rent{
@Override
public void rentHouse() {
System.out.println("我要租房");
}
}
(2)创建一个动态处理器,通过含参构造方法传入一个Object对象,形成一个通用的构造方法,可以传入不同类型的原对象:
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("租房前");
Object result = method.invoke(object, args); //通过反射的方式调用被代理对象的方法
System.out.println("租房后");
return result;
}
}
(3)测试类:
public class Test {
public static void main(String[] args) {
Rent rent = new Host();
Rent rentProxy = (Rent) Proxy.newProxyInstance(rent.getClass().getClassLoader(),
rent.getClass().getInterfaces(), new DynamicProxyHandler(rent));
rentProxy.rentHouse();
}
}
我们通过反射包(java.lang.reflect)中的Proxy类的静态方法newProxyInstance来动态地创建代理类,需要注意的是,newProxyInstance()方法有三个参数:
- ClassLoader loader:指定被代理类的类加载器
- Class<?>[] interfaces:指定被代理对象实现的接口
- InvocationHandler h:指定动态处理器
(4)执行结果:
租房前
我要租房
租房后
动态代理的优缺点:
1.优点: 相对于静态代理,动态代理支持动态地创建代理对象,可以大大减少我们的代码量,同时减少了对业务接口的依赖,降低了耦合度。
2.缺点: JDK动态代理的限制条件是被代理对象和代理对象都需要实现相同的接口,对于没有接口的类,就不能采用JDK动态代理的方式对原对象进行控制。(可以采用CGLib动态代理,这里不做介绍)