1、基本介绍
代理设计模式是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。代理模式分为静态代理和动态代理。静态代理是指代理类在编译时就已经确定,而动态代理的代理类在运行时动态生成。
代理模式的两大作用:
屏蔽被代理对象
对被代理对象的方法做增强
2、静态代理
2.1、案例
1、定义接口,供代理类和被代理类实现
public interface DataQueryService {
String query(String param);
}
2、定义被代理类
public class DataQuery implements DataQueryService{
@Override
public String query(String param) {
// 查询数据库并返回结果
return "查询结果";
}
}
3、定义代理类,并给被代理类做增强
/**
* DataQuery代理类
*/
public class DataQueryProxy implements DataQueryService{
// 代理谁就应该持有谁
private DataQuery dataQuery;
// 缓存
private Map<String, String> queryCaChe = new HashMap<>();
public DataQueryProxy(DataQuery dataQuery) {
// 1、屏蔽被代理对象
this.dataQuery = dataQuery;
}
@Override
public String query(String param) {
// 2、对原方法做增强
String data = queryCaChe.get(param);
if (StringUtils.isEmpty(data)) {
return dataQuery.query(param);
}
return data;
}
}
2.2、静态代理步骤总结
1、创建一个接口,定义代理类和被代理类共同实现的方法
2、创建被代理类,定义这个接口,并且在其中定义实现方法
3、创建代理类,也要实现这个接口,同时定义一个属性为被代理类对象
4、在代理类中实现接口中的方法,方法中调用被代理类中的对应方法
5、在使用代理类时,首先创建被代理类的对象和代理类的对象,然后通过代理类调用方法
这样就实现对被代理类方法的增强或修改。当然也可以通过继承来实现代理。步骤与接口一致,但是通过继承实现代理类和被代理类的耦合度较高,不够灵活
3、动态代理
3.1、介绍
动态代理是在程序运行时动态生成代理类,无需手动编写代理类,大大的降低了代码的复杂度。动态代理一般使用Java提供的反射机制实现,可以对任意实现了接口的类进行代理
3.2、基于JDK实现动态代理
1、基于代理类和被代理类基础上,使用Proxy.newProxyInstance()方法创建代理对象,执行query方法
public class ProxyApplication {
public static void main(String[] args) {
// jdk动态代理主要使用Proxy类来完成
// 1、定义被代理类的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 2、代理类需要实现的接口
Class[] interfaces = new Class[] {DataQueryService.class};
InvocationHandler invocationHandler = new CacheInvocationHandler();
// 3、获取代理对象
DataQueryService dataQuery = (DataQueryService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
// 4、调用代理对象增强的方法,其实就是调用invocationHandler的invoke方法
dataQuery.query("key1");
}
}
2、代理类会实现接口的所有方法并转发到InvocationHandler的invoke方法中对被代理类做增强
public class CacheInvocationHandler implements InvocationHandler {
private static final Map<String, String> cacheMap = new HashMap<>();
// 持有被代理类
private DataQuery dataQuery;
public CacheInvocationHandler() {
this.dataQuery = new DataQuery();
}
/**
* @param proxy 代理类
* @param method 方法
* @param args 方法参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法名称
String name = method.getName();
// 对代理类的query方法做增强
if ("query".equals(name)) {
String key = args[0].toString();
String value = cacheMap.get(key);
if (value == null) {
String queryResult = dataQuery.query(key);
if (queryResult != null) {
cacheMap.put(key, queryResult);
return queryResult;
}
} else {
return value;
}
}
// 如果是其他方法则不作增强,正常执行被代理类方法
return method.invoke(dataQuery, args);
}
}
3.3、基于CGLib实现动态代理
1、定义被代理类,注意:该被代理类并没有实现接口
public class DataQuery {
public String query(String param) {
// 查询数据库并返回结果
return "查询结果";
}
}
2、创建代理类并调用方法
public class ProxyApplication {
public static void main(String[] args) {
// CGlib通过Enhancer类来实现动态代理
Enhancer enhancer = new Enhancer();
// 设置代理类的父类(注意此处没有设置接口,CGLib动态代理会动态的创建被代理类的子类)
enhancer.setSuperclass(DataQuery.class);
// 设置一个方法拦截器来拦截方法,对方法进行增强
enhancer.setCallback(new CacheMethodInterceptor());
// 创建代理类
DataQuery dataQuery = (DataQuery) enhancer.create();
dataQuery.query("key");
}
}
3、在MethodInterceptor(方法拦截器)中执行增强逻辑
public class CacheMethodInterceptor implements MethodInterceptor {
private static final Map<String, String> cacheMap = new HashMap<>();
// 持有被代理类
private DataQuery dataQuery;
public CacheMethodInterceptor() {
this.dataQuery = new DataQuery();
}
/**
*
* @param o 代理类
* @param method 方法
* @param args 方法参数
* @param methodProxy 用于调用未被拦截的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 方法名称
String name = method.getName();
// 对代理类的query方法做增强
if ("query".equals(name)) {
String key = args[0].toString();
String value = cacheMap.get(key);
if (value == null) {
String queryResult = dataQuery.query(key);
if (queryResult != null) {
cacheMap.put(key, queryResult);
return queryResult;
}
} else {
return value;
}
}
// 如果是其他方法则不作增强,正常执行被代理类方法
return method.invoke(dataQuery, args);
}
}
3.4、动态代理总结
3.4.1、JDK动态代理
JDK动态代理是基于Java反射机制实现的。
在使用JDK动态代理时,被代理的对象必须实现一个或多个接口,代理对象的类型由接口类型决定。
在运行时,JDK动态代理会动态地创建一个实现指定接口的代理类,并且重写该接口的所有方法,使得这些方法的调用都被转发到InvocationHandler对象中的invoke方法中进行处理。
3.4.2、CGLib动态代理
CGLib动态代理则是通过继承被代理类来实现的。
在使用CGLib动态代理时,被代理的类不必实现任何接口,代理对象的类型由被代理类的类型决定。
在运行时,CGLib动态代理会动态地创建一个被代理类的子类,并且重写该类的所有非final方法
3.4.3、两者的区别
JDK动态代理只能代理实现了接口的类,而CGLib动态代理可以代理任何类
JDK动态代理在运行时会动态生成代理类;而CGLib动态代理在运行时会动态生成被代理类的子类
3.5、使用方式
如果被代理的类实现了接口,建议使用JDK动态代理;如果被代理的类没有实现接口,建议使用CGLib动态代理