在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
代理模式分为静态代理和动态代理
1. 静态代理模式
在静态代理中,我们需要一个依赖现有对象的代理对象来执行现有对象的方法,代理对象和现有对象都要实现同一个接口,就好像我们租房子一样,我们可能直接找到房东,也可能通过中介租房,那么通过中介就是一种代理,他们之间的关系如下图
public interface Rent {
public void rent();
}
// 房东
public class Landlord implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
// 中介
public class LandlordProxy implements Rent {
private Rent landlord;
@Override
public void rent() {
System.out.println("中介帮房东出租房子");
if (landlord == null) {
landlord = new Landlord();
}
landlord.rent();
// 可以对方法进行增强等
System.out.println("中介收取手续费");
}
}
// 租客
public class Tenant {
public static void main(String[] args) {
new Tenant().rentHouse();
}
public void rentHouse() {
Rent proxy = new LandlordProxy();
proxy.rent();
}
}
运行效果如下
2. 动态代理
动态代理与静态代理最重要的区别便是代理对象的生成方式,静态代理中每一个代理类只为一个类服务,但是如果类比较多的时候,便要编写大量的代理类,而动态代理则很好的解决了这一个问题,动态代理是利用反射机制在程序运行时动态生成对象,在jdk动态代理中主要使用的是Proxy类和InvocationHandle接口
Rent接口和Landlord类不变,创建一个RentHandle实现InvocationHandle接口
public class RentHandle implements InvocationHandler {
// 被代理对象
private Object target;
public RentHandle(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 可以对方法进行前置和后置增强,相当于Spring中的环绕通知
// 比如给某个方法增加日志等
System.out.println(method.getName() + "方法执行时间" + new SimpleDateFormat("yyyy-MM-dd hh:mm").format(new Date()));
// 实际调用Landlord的rent方法
method.invoke(target, args);
System.out.println(method.getName() + "方法执行后");
return null;
}
}
// 租客
public class Tenant {
public static void main(String[] args) {
new Tenant().rentHouse();
}
public void rentHouse() {
Rent landlord = new Landlord();
InvocationHandler handler = new RentHandle(landlord);
Rent rentProxy = (Rent) Proxy.newProxyInstance(landlord.getClass().getClassLoader(), landlord.getClass().getInterfaces(), handler);
System.out.println("代理对象->" + rentProxy.getClass().getName());
rentProxy.rent();
}
}
运行效果如下
实现动态代理的步骤
- 首先需要一个实现了接口的被代理类
- 创建一个Handle类实现InvocationHandle接口
- 在Handle的invoke方法中编写相应代码,如增加日志等
- 获取代理对象,通过Proxy类的newProxyInstance方法获取代理对象,然后转化为相应类型
- 使用代理对象,调用相关方法即可得到一个增强后的方法
3. 静态代理和动态代理的区别和联系
- 静态代理代理类需要对被代理类的每个方法都编写相应的代码,而动态代理只需在invoke方法中编写一次
- 静态代理中一个代理类只为一个类服务,而动态代理可以根据传入的目标类对象和接口为多个类服务,实现了代码的复用
- 静态代理针对性较强,可以针对被代理类的某个方法进行特殊处理,而动态代理一般只能做一些通用的的处理,如打印日志
- 静态代理和动态代理都需要被代理类实现某个接口
4. JDK动态代理和CGLIB动态代理
- JDK动态代理:使用JDK的Proxy代理类,JDK的动态代理要求目标对象必须实现接口,这是Java设计上的要求
- CGLIB动态代理:CGLIB动态代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以使用CGLIB生成动态代理,要求目标类必须能够被继承,即不能是final修饰的类,方法也不能是final的