什么是 Servlet
想要深刻理解两者的区别,需要有一个知识储备,对 servlet 和 Spring MVC 的理解。
简单的说,Spring MVC 是对 servlet 的封装和解藕。
广义上的 servlet 是 Java 中用于支持处理浏览器的请求,并生成响应的一组类和接口,位于 javax.servlet.* 包下。
狭义上的 servlet 是继承 HttpServlet 的类,并重写 doGet()、doPost() 等方法来处理浏览器的请求并生成响应的 Java 对象,常用的 Tomcat 服务器用来管理 Servelt 的生命周期,称为 Servlet 容器。一般来说,一个功能模块对应一个 servlet ,对于同一个路径下,不同子路径的 get 请求,可以在 doGet 方法的进行条件判断。例如:
xml
<!-- web.xml 配置 -->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.example.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>OrderServlet</servlet-name>
<servlet-class>com.example.OrderServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OrderServlet</servlet-name>
<url-pattern>/order/*</url-pattern>
</servlet-mapping>
scala
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if ("GET".equals(req.getMethod())) {
doGet(req, resp);
} else if ("POST".equals(req.getMethod())) {
doPost(req, resp);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String action = req.getParameter("action");
if ("list".equals(action)) {
// 处理用户列表请求
} else if ("detail".equals(action)) {
// 处理用户详情请求
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
String operation = req.getParameter("operation");
if ("create".equals(operation)) {
// 处理用户创建请求
} else if ("update".equals(operation)) {
// 处理用户更新请求
}
}
}
从使用示例中,我们可以很清晰的看出 Servlet 编程的优点,1、职责分离:不同模块的 Servlet 独立维护,代码边界清晰。2、配置简单:通过简单的配置就可以让 Servlet 对象与 URL 绑定。
同时,缺点也不言而喻,1、代码冗余:每个 Servlet 模块需要重复编写请求解析、响应处理等公共逻辑。2、扩展性差:每当新增加功能都需要增加一个 Servlet ,同时修改配置文件,可能会导致配置文件的臃肿。3、路由不灵活:无法通过统一匹配逻辑,需要手动解析路径参数。
什么是过滤器
在Servlet中,过滤器是指实现 javax.servlet.Filter
接口,实现 init 、doFilter、destory 方法的类。对客户端请求和响应进行预处理和后处理的组件,在请求到达 Servlet 之前对请求进行过滤和修改,在响应返回给客户端之前对响应进行处理,例如:
typescript
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.example.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从过滤器配置中获取字符编码参数
encoding = filterConfig.getInitParameter("encoding");
if (encoding == null) {
encoding = "UTF-8";
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置请求的字符编码
request.setCharacterEncoding(encoding);
// 设置响应的字符编码
response.setCharacterEncoding(encoding);
// 将请求传递给下一个过滤器或目标 Servlet
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 释放过滤器占用的资源
}
}
java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "CharacterEncodingFilter", urlPatterns = "/*", initParams = {
@WebInitParam(name = "encoding", value = "UTF-8")
})
public class CharacterEncodingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
encoding = filterConfig.getInitParameter("encoding");
if (encoding == null) {
encoding = "UTF-8";
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
过滤器的生命周期
- 初始化 :当 Web 应用启动时,Servlet 容器会创建过滤器实例,并调用
init()
方法进行初始化。init()
方法只会被调用一次。 - 过滤处理 :每次请求匹配到过滤器的
url-pattern
时,Servlet 容器会调用doFilter()
方法对请求进行过滤处理。 - 销毁 :当 Web 应用停止时,Servlet 容器会调用
destroy()
方法销毁过滤器实例,释放过滤器占用的资源。
什么是 Spring MVC?
Spring MVC 是对 Servlet 的封装和扩展:
1、在传统 Servlet 开发中,每个请求可能需要对应一个具体的 Servlet 类来处理。而在 Spring MVC 里,DispatcherServlet
作为前端控制器,接收所有客户端请求,统一调度和分发。感觉有点面向对象的味道在里面,客户端相当于接口,各种客户端的请求相当于各种实现类的实例,对于后端来说,只有一个功能,那就是接收客户端的请求,所以只需要一个 Servlet ,那就是 DispatcherServlet。
2、MVC 架构和注解驱动:基于 '@Controller' 、'@RequestMapping' 、'@GetMapping'等注解实现请求映射,以及业务逻辑和视图渲染的解藕。
工作流程
1、DispatcherServlet接受Http请求,根据URL去HandlerMapping中寻找对于的handler(controller)。
2、HandlerAdapter去调用对应的handler(controller)。
3、调用controller返回ModelAndView给中央处理器。
4、ViewResolver解析ModelAndView中的逻辑view找到真实的view。
5、中央处理器将model传给view。
6、将view作为结果返回给浏览器。
什么是拦截器?
Spring MVC 中的重要组件,对处理器进行预处理和后处理。实现HandlerInterceptor
接口,实现三个方法:preHandle
、postHandle
和 afterCompletion
,例如:
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor {
// 在处理器处理请求之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre-handle method is called");
// 返回 true 表示继续执行后续的处理器和拦截器
return true;
}
// 在处理器处理请求之后,视图渲染之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Post-handle method is called");
}
// 在整个请求处理完成之后执行,包括视图渲染之后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("After-completion method is called");
}
}
typescript
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
registry.addInterceptor(new MyInterceptor())
// 指定拦截的路径
.addPathPatterns("/**")
// 排除不拦截的路径
.excludePathPatterns("/login");
}
}
过滤器和拦截器的区别?
- 层次不同:过滤器作用在 Servlet 层面,拦截器作用在控制器层面。
- 功能侧重不同:过滤器侧重于全局的预处理和后处理,例如字符编码设置。拦截器侧重于对请求处理过程进行增强和控制,例如日志记录、数据预处理。