从‘’动态代理‘’到‘’拦截器‘’的一系列学习
java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心
1. InvocationHandler接口
InvocationHandler
中包含invoke()
方法我们通过动态代理对象调用方法时,这个方法的调用就会被转发到实现了
InvocationHandler
接口类的invoke()
方法来调用
下面是在评论区一个大佬举得例子,我觉得很形象
public class DynamicProxy {
public static void main(String[] args) {
// 小韭菜学生类
Student ordinaryStudents = new OrdinaryStudents();
ordinaryStudents.eat();
ordinaryStudents.write();
// 现在有一位特殊的学生,他是区长的儿子,我们自然要对他额外照顾,要给他加一下功能。
// 一种思路是定义一个类:区长的儿子类,他继承自学生类,但世上儿子千千万,有区长的儿子,也有市长的儿子,更有省长的儿子,不能把他们挨个定义出来,
// 现在就可以使用动态代理机制,动态的给区长的儿子加上功能,以后碰到市长、省长的儿子也同样处理。
//--------------------------------------------------------------------------------
//下面的思路很重要
// InvocationHandler作用就是,当代理对象的原本方法被调用的时候,会重定向到一个方法,
// 这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。
// InvocationHandler接收三个参数:
// proxy,代理后的实例对象
// method,对象被调用方法
// args,调用时的参数
InvocationHandler handler = (proxy, method, handlerArgs) -> {
// 定义eat方法
if ("eat".equals(method.getName())) {
System.out.println("我可以吃香喝辣!");
return null;
}
// 定义write方法
if ("write".equals(method.getName())) {
System.out.println("我的作文题目是《我的区长父亲》。");
//调用普通学生类的write方法,流程还是要走的,还是要交一篇作文上去,不能太明目张胆
method.invoke(ordinaryStudents, handlerArgs);
System.out.println("我的作文拿了区作文竞赛一等奖!so easy!");
return null;
}
return null;
};
// 对这个实例对象代理生成一个代理对象。
// 被代理后生成的对象,是通过People接口的字节码增强方式创建的类而构造出来的。它是一个临时构造的实现类的对象。
// loder和interfaces基本就是决定了这个类到底是个怎么样的类。而h是InvocationHandler,决定了这个代理类到底是多了什么功能.
// 通过这些接口和类加载器,拿到这个代理类class。然后通过反射的技术复制拿到代理类的构造函数,
// 最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。
// 最终实现可以在运行的时候才切入改变类的方法,而不需要预先定义它。
Student sonOfDistrict = (Student) Proxy.newProxyInstance(ordinaryStudents.getClass().getClassLoader(), ordinaryStudents.getClass().getInterfaces(), handler);
//这里的Proxy.newProxyInstance在下文有介绍
sonOfDistrict.eat();
sonOfDistrict.write();
}
}
/*最终输出:
我在吃饭!
我在写作文!
我可以吃香喝辣!
我的作文题目是《我的区长父亲》。
我在写作文!
我的作文拿了区作文竞赛一等奖!so easy! */
getInterfaces()方法和Java的反射机制有关。它能够获得这个对象所实现的接口
2. 用 Java 实现拦截器 Interceptor 的拦截功能
理解拦截器的实现
3. JDK动态代理类Proxy.newProxyInstance()
动态代理是在运行时生成代理对象的技术,允许你拦截对目标对象方法的调用,并在调用前后执行自定义的逻辑
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法
newProxyInstance() 方法的签名如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用
在上面的例子:
Student sonOfDistrict = (Student) Proxy.newProxyInstance(ordinaryStudents.getClass().getClassLoader(), ordinaryStudents.getClass().getInterfaces(), handler);
4. Spring MVC 拦截器
原理:拦截器Interceptor的拦截功能是基于 Java 的动态代理来实现的,参考第3点
实现:通过两种途径,第一种是实现HandlerInterceptor接口,第二种是实现
WebRequestInterceptor
接口
4.1 HandlerInterceptor 接口
在HandlerInterceptor
接口中,定义了 3 个方法,分别为preHandle()
、postHandle()
和afterCompletion()
,我们就是通过复写这 3 个方法来对用户的请求进行拦截处理的
preHandle:处理器执行之前执行,如果返回 false 将跳过处理器、拦截器 postHandle 方法、视图渲染等,直接执行拦截器 afterCompletion 方法。
postHandle:处理器执行后,视图渲染前执行,如果处理器抛出异常,将跳过该方法直接执行拦截器 afterCompletion 方法。
afterCompletion:视图渲染后执行,不管处理器是否抛出异常,该方法都将执行,清理资源
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("请求来了");
return true;
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("已登录");
return true;
}
}
API配置
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor());
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login");
}
}