学习路径:https://coding.imooc.com/learn/list/270.html
前言
Spring 的AOP实质就是动态代理模式
相关设计模式
静态代理
静态代理模式下的被代理类没有使用多态。动态代理借助JDK的InvocationHandler接口实现多态,从而完成动态绑定实例化的类型
应用场景
1. 三层架构中代理ServiceImpl —— 新增代理类实现
/**
* 只展示应用层代码, 下面会具体分析
*/
public static void main(String[] args) {
IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(
new OrderServiceImpl() ).bind();
// 数据准备
Order order = new Order();order.setUserId(2);
// 使用动态代理存数据
orderServiceDynamicProxy.saveOrder(order);
}
- 实现
实现InvacationHandler改写invoke方法,引入Proxy类的newProxyInstance方法,下面面会分析这个案例
2. Filter拦截敏感词 —— 匿名内部类实现
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// todo 需要增强 request 的 getParameter()方法
// 代理的是request对象
HttpServletRequest proxyInstance = (HttpServletRequest) Proxy.newProxyInstance(
req.getClass().getClassLoader(),
new Class[]{HttpServletRequest.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getParameter")) {
String value = (String) method.invoke(req, args); // request是这个接口的实现类对象
// 去除所有空格, 注意返回值
value = value.replace(" ", "");
// 将脏话按照长度替换成*号, list是存储脏话的集合
for (String s : list) {
if (value.contains(s)) {
StringBuilder temp = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
temp.append("*");
}
value = value.replace(s, temp.toString());
}
}
return value;
}
return method.invoke(req, args);
}
});
// todo 一定要注意将代理类传进来
chain.doFilter(proxyInstance, resp);
}
3. 增强自定义数据库连接池Connection的close() —— 普通内部类实现
/**
* 只保留动态代理相关的代码
*/
public class ConnectionPool {
// 创建Connection的方法
public Connection createConnection() {
Connection proxyConnection = null;
try {
// todo 与JDBCUtil的唯一耦合,分离了JDBCUtil的职责
Connection connection = JDBCUtil.getConnection();
proxyConnection = (Connection) Proxy.newProxyInstance(
ConnectionPool.class.getClassLoader(),
new Class[]{Connection.class}, // 具体要代理的是哪个接口的实现类
new ConnectionHandler(connection) // 传入代理类
);
} catch (SQLException e) {
e.printStackTrace();
}
curCount++;
return proxyConnection;
}
/**
* 内置被代理对象的封装类
*/
class ConnectionHandler implements InvocationHandler {
// 在内部维护一个需要被增强的对象(被代理对象)
private Connection connection;
public ConnectionHandler(Connection connection) {
this.connection = connection;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 先得到当前调用的方法的方法名
String methodName = method.getName();
// 需要增强的方法,进行增强
if ("close".equalsIgnoreCase(methodName)) {
// todo 可以强转的内在原因
// todo proxy 通过 newInstance() 传入的 interfaces[] 拼接处 Proxy implements Connection
pool.addLast((Connection) proxy);
} else { // 其他方法正常调用
return method.invoke(connection, args);
}
return null;
}
}
}
三层架构中代理ServiceImpl 原理解析
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
新增代理类
/**
* 两个关键方法
* @see InvocationHandler#invoke(Object, Method, Object[])
* @see Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)
*/
public class OrderServiceDynamicProxy implements InvocationHandler {
// 被代理的对象,抽象成Object类
private Object target;
/**
* 构造方法初始化成员变量
* @param target 需要代理的类(接口的实现类)
*/
public OrderServiceDynamicProxy(Object target) {
this.target = target;
}
/**
* 核心1:
* 动态代理使用ClassLoader和反射生成一个class文件
* 该class文件是Proxy类型,存在JVM中,可以在实例化过程中动态绑定类型
* 也就是动态实现的核心
*
* 【动态的实现】cls.getInterfaces() 获取被代理类的接口而不是直接获取该代理类
* 接口 -> 能约束返回值的取值范围,但是这里不能写死,要交给上层强转
*
* @return Object 类型 -- 让上层决定
*/
public Object bind(){
// 获取代理类的class文件
Class cls = target.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
}
/**
* 核心2:
* 当Proxy类型被实例化动态绑定后,调用绑定完成的类的方法时,就触发invoke()
* 通过反射最终调用的是被代理类的方法
* @param proxy 代理
* @param method 代理的方法
* @param args 代理的方法所要传入的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod(argObject);
// 使用反射得到的方法和参数调用目标方法
Object object = method.invoke(target, args);
afterMethod();
return object;
}
/**
* 以下跟静态不同的是,可以设置不同类的实例进该方法的行为
* @param obj
*/
private void beforeMethod(Object obj){
int userId = 0;
System.out.println("动态代理 before core");
// 这边还可以设置很多if判断,或者switch语句对入参类进行判断,使该前置方法不再单一处理一个类
if(obj instanceof Order){
Order order =(Order)obj;
userId = order.getUserId();
}
int dbRouter = userId % 2;
System.out.println("静态代理分配到【db" + dbRouter +"】处理数据");
/**
* 负载均衡的逻辑,与代理模式的设计模式无关
* todo 设置dataSource;
*/
DataSourceContextHolder.setDBType("db" + String.valueOf(dbRouter));
}
private void afterMethod() {
System.out.println("动态代理 after code");
}
}
方法细节: InvocationHandler#invoke 与 Proxy#newProxyInstance
// debug 看看Proxy#newProxyInstance的返回值,是个很诡异的$Proxy0,源码上来看这个$Proxy0是动态生成的,这个类的作用看下文
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
// 代理类声明
public class OrderServiceDynamicProxy implements InvocationHandler
// 代理类中的成员变量 -- 代理类的声明
private Object target;
// 有声明可知,InvocationHandler用作生成$Proxy0的参数,其invoke方法作用见下文
public Object bind(){
// 获取代理类的class文件
Class cls = target.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),this);
}
关于invoke为什么在saveOrder()后自动触发,与$Proxy0有关。 相关的内容参考:
https://www.jianshu.com/p/89546eff3cd0
以上参考内容在$Proxy0这个类中找到了答案,必须实现invoke方法是为了 $Proxy0顺利解析被代理方法的参数和方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod(argObject);
// 使用反射得到的方法和参数调用目标方法
Object object = method.invoke(target, args);
afterMethod();
return object;
}