问:拦截器只能拦截部分Web请求,意思是说,拦截器的拦截是基于Java反射机制实现的,拦截的对象只能是实现了接口的类,而不能拦截URL这种链接。这个是什么意思,举个例子说明一下
答:
- “拦截器只能拦截部分 Web 请求”:
- 这意味着拦截器不能对所有的 Web 请求进行处理。在 Java Web 开发中,拦截器通常用于特定场景下对请求进行预处理、后处理或控制请求的流程。但不是所有类型的请求都能被拦截器处理到。
- “拦截器的拦截是基于 Java 反射机制实现的”:
- 拦截器通常利用反射机制在请求处理的特定阶段进行介入。例如,通过反射可以获取被拦截方法的信息,执行一些额外的操作后再决定是否继续执行被拦截的方法。
- “拦截的对象只能是实现了接口的类,而不能拦截 URL 这种链接”:
- 这表明拦截器通常是针对实现了特定接口的 Java 类中的方法进行拦截。而对于直接通过 URL 访问的资源(如静态资源文件),拦截器可能无法生效。因为拦截器的设计目的主要是处理特定的业务逻辑方法调用,而不是直接处理 URL 请求
public interface ServiceInterface {
void doSomething();
}
public class ServiceImpl implements ServiceInterface {
@Override
public void doSomething() {
System.out.println("执行具体业务逻辑");
}
}
拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof ServiceInterface) {
System.out.println("拦截到了实现 ServiceInterface 接口的方法调用");
return true;
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
配置类加上拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
问:那这个只能拦截实现了接口的类,是什么意思。registry.addInterceptor(new LoggingInterceptor());这个不是注册了吗,不就可以拦截了吗
在registry.addInterceptor(new LoggingInterceptor());
这行代码中,确实是将LoggingInterceptor
这个拦截器注册到了框架中。
然而,并不是注册了拦截器就一定能拦截所有的请求。正如前面所说,如果被拦截的对象是一个没有实现接口的类,并且框架在这种情况下难以创建合适的代理对象,那么拦截器可能就无法生效。
以 Spring 框架为例,Spring 在某些情况下会根据目标对象的类型来决定如何应用拦截器。如果目标对象实现了接口,Spring 可以比较容易地使用 JDK 动态代理来创建一个代理对象,在这个代理对象的方法调用前后插入拦截器的逻辑。但如果目标对象没有实现接口,Spring 可能会尝试使用 CGLIB 来创建子类代理,但这可能会有一些限制,并且不是在所有情况下都能成功应用拦截器
// 实现了接口的类
public interface MyServiceInterface {
void doSomething();
}
public class MyServiceImpl implements MyServiceInterface {
@Override
public void doSomething() {
System.out.println("执行实现接口的类的方法");
}
}
// 没有实现接口的类
public class MyOtherClass {
public void doAnotherThing() {
System.out.println("执行没有实现接口的类的方法");
}
}
@RestController
public class MyController {
private final MyServiceInterface myService;
private final MyOtherClass myOtherClass;
public MyController(MyServiceInterface myService, MyOtherClass myOtherClass) {
this.myService = myService;
this.myOtherClass = myOtherClass;
}
@GetMapping("/test1")
public void test1() {
myService.doSomething();
}
@GetMapping("/test2")
public void test2() {
myOtherClass.doAnotherThing();
}
}
在这种情况下,当访问/test1
路径时,拦截器可能会生效,因为MyServiceImpl
实现了接口,Spring 可以创建代理对象并应用拦截器逻辑。但当访问/test2
路径时,拦截器可能不会生效,因为MyOtherClass
没有实现接口,并且在这种情况下 Spring 可能无法以同样的方式应用拦截器
问:那为什么实现了接口就可以拦截器生效
实现了接口的类能使拦截器生效主要有以下几个原因:
一、动态代理机制
- 易于创建代理对象:
- 很多框架(如 Spring)利用 Java 的动态代理机制来实现拦截功能。当一个类实现了接口时,框架可以相对容易地使用 JDK 动态代理来创建一个代理对象。这个代理对象会包装原始的实现类对象,并在方法调用前后插入拦截器的逻辑。
- 例如,假设有一个接口
MyInterface
和一个实现该接口的类MyClass
。框架可以创建一个代理对象,使得在调用MyClass
的方法时,拦截器可以在方法执行前后进行一些额外的操作。
- 方法调用的拦截:
- 通过代理对象,框架可以在对接口方法的调用进行拦截。当客户端代码调用接口方法时,实际上是调用了代理对象的方法。代理对象可以在调用实际的实现类方法之前和之后执行拦截器中的代码,从而实现对业务逻辑的增强和控制。
二、框架设计与约定
- 一致性和可预测性:
- 框架通常会遵循一定的设计模式和约定。当一个类实现了接口,框架可以预期这个类的行为会遵循接口定义的规范。这使得框架能够更可靠地应用拦截器,因为它知道接口方法的签名和行为是固定的。
- 例如,在 Spring 框架中,对于实现了特定接口的 bean,框架可以更容易地进行依赖注入、事务管理和拦截器的应用等操作,因为这些 bean 的行为可以通过接口进行一定程度的预测。
- 便于扩展和维护:
- 实现接口使得代码更易于扩展和维护。如果需要添加新的功能或修改现有功能,可以通过创建新的接口实现类或者修改现有实现类来实现,而不会影响到其他部分的代码。同时,拦截器可以在不修改实现类代码的情况下,对实现了接口的类的方法调用进行统一的处理,提高了代码的可维护性。
最后自己总结:
为什么在写东西的时候要写一个接口去,再写实现类,因为有了一个接口之后,jdk或者其他的动态代理,可以创建一个代理对象,那么在spring框架中aop,就可以在调用方法的时候进行拦截,处理逻辑。这个接口不是拦截器的接口,是实现类的接口。有了这个接口就可以拦截了。这些配置类,拦截器类,这些都是有关拦截器本身的东西。为什么可以在调用方法前后,就是实现了一个接口,让jdk可以为这个类代理了,来实现这个逻辑。