1 简介
Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。
MVC结构:
M 代表 模型(Model):数据,就是 dao,bean
V 代表 视图(View) :网页, JSP,用来展示模型中的数据
C 代表 控制器(controller) :控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。
Spring MVC结构
2 Demo案例
2.1 注解详解
@Controller 注解,添加在类上,表示这是控制器 Controller对象。
- name 属性:该 Controller 对象的 Bean 名字。允许空。
@RequestMapping 注解,添加在类或方法上,标记该类/方法对应接口的配置信息。
- path 属性:接口路径。[] 数组,可以填写多个接口路径。
- values 属性:和 path 属性相同,是它的别名。
- method 属性:请求方法 RequestMethod ,可以填写 GET、POST、POST、DELETE 等等。[] 数组, 可以填写多个请求方法。如果为空,表示匹配所有请求方法。
- params 属性:请求参数需要包含值的参数名,可为空。
- consumes 属性:提交内容类型( Content-Type )
@RequestParam注解,添加在方法参数上,标记该方法参数对应的请求参数的信息。
- name 属性:对应的请求参数名。如果为空,则直接使用方法上的参数变量名。
- value 属性:和 name 属性相同,是它的别名。
- required 属性:参数是否必须传。默认为 true ,表示必传。
- defaultValue 属性:参数默认值。
2.2 传统MVC的实现
1)配置
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/log4j2.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--最新的MVC方式 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--传统的MVC方式 -->
<servlet>
<servlet-name>CheckIn</servlet-name>
<servlet-class>Servlet.CheckIn</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CheckIn</servlet-name>
<url-pattern>/CheckIn</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UserRegister</servlet-name>
<servlet-class>Servlet.UserRegister</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>UserRegister</servlet-name>
<url-pattern>/UserRegister</url-pattern>
</servlet-mapping>
</web-app>
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- /hello 路径的请求交给 id 为 helloController 的控制器处理-->
<prop key="/hello">hello</prop>
</props>
</property>
</bean>
<bean id="hello" class="control.hello"></bean>
<!--配置静态资源允许访问 -->
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/assets/**" location="/assets/"/>
</beans>
2)Bean
package Bean;
public class User {
private String id;
private String number;
private String password;
private String name;
private String sex;
private String address;
public User(String id ,String number,String password) {
super();
this.id = id;
this.number = number;
this.password = password;
}
public User(String number, String password) {
super();
this.number = number;
this.password = password;
}
public User(String number, String password,String name,String sex,String address) {
super();
this.number = number;
this.password = password;
this.name = name;
this.sex = sex;
this.address = address;
}
省略get / set
/*
create table student(
id int(3) primary key auto_increment,
number char(11) not null,
password char(20) not null,
name varchar(20) not null,
sex char(2) not null,
address varchar(40)
)
*/
}
3)DButil
public class DBUtil {
private static Logger logger= LogManager.getLogger();
private static final String url = "jdbc:mysql://localhost:3306/iast1" + "?useSSL=true&characterEncoding=UTF-8";
private static final String USERNAME = "root";
private static final String PWD = "root";
public static Connection con = null;
public static PreparedStatement pstmt = null;
public static ResultSet res = null;
public static ResultSet executeQuery(String sql, Object...params) {
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, USERNAME, PWD);
pstmt = con.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
res = pstmt.executeQuery();
logger.info("执行的SQL语句:"+pstmt.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
public static boolean executeUpdate(String sql, Object...params) {
int result = -1;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, USERNAME, PWD);
pstmt = con.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
result = pstmt.executeUpdate();
logger.info("执行的SQL语句:"+pstmt.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
Close();
}
return result > 0 ? true : false;
}
public static void Close() {
try {
if (res != null)
res.close();
if (pstmt != null)
pstmt.close();
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4)LoginDAO
public class LoginDao {
public int Login(User user) {
int flag = -1;
String sql = "select * from student where number = ? and password = ? ";
ResultSet res = DBUtil.executeQuery(sql, user.getNumber(), user.getPassword());
try {
if (res.next()){
flag = 1;
}
} catch (SQLException e) {
e.printStackTrace();
}
return flag;
}
public int register(User user) {
int flag = -1;
String sql = "insert into student(id,number,password,name,sex,address) values(null,?,?,?,?,?)";
Boolean res = DBUtil.executeUpdate(sql, user.getNumber(), user.getPassword(),
user.getName(),user.getSex(), user.getAddress());
if (res){
flag = 1;
}
return flag;
}
public int Check(User user) {
int flag = -1;
String sql = "select * from student where number = ? ";
ResultSet res = DBUtil.executeQuery(sql, user.getNumber());
try {
if (res.next()) {
flag = 1;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return flag;
}
}
5)CheckIn
public class CheckIn extends HttpServlet {
private static Logger logger= LogManager.getLogger();
public CheckIn() {
super();
}
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String number=request.getParameter("number");
String password=request.getParameter("password");
User user=new User(number,password);
LoginDao dao=new LoginDao();
int result=dao.Login(user);
if(result > 0) {
request.setAttribute("number", number);
logger.info("学号:"+number+" 尝试登陆成功");
request.getRequestDispatcher("welcome.jsp").forward(request,response);
}else {
logger.error("学号:"+number+" 尝试登录失败");
response.sendRedirect("error/loginFail.jsp");
}
}
public void init() throws ServletException {
// Put your code here
}
}
6)jsp
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" type="text/css" href="css/login.css">
</head>
<body>
<div id="content">
<div class="login-header">
<img src="assets/images/icon.png"/>
</div>
<form action="CheckIn" method="post">
<div class="login-input-box">
<label for="number">学号:</label> <input type="text" name="number" id="number" placeholder="请输入学号!" required>
</div>
<div class="login-input-box">
<label for="password">密码:</label> <input type="password" name="password" id="password" placeholder="请输入密码!" required>
</div>
<div class="remember-box">
<label>
<input type="checkbox"> Remember Me
</label>
</div>
<div class="login-button-box">
<input type="submit" name="登录" value="登录">
</div>
</form>
<div class="logon-box">
<a href = "http://127.0.0.1:8080/Spring_MVC_Test_war_exploded/register.jsp">点击注册</a>
<a href="">Forgot</a>
</div>
</div>
</body>
</html>
Welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功界面</title>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
String number = (String) request.getAttribute("number");
out.println(number);
%>
</body>
</html>
2.3 SpringMVC实现
1 HandlerInterceptor
在使用 SpringMVC 的时候,我们可以使用 HandlerInterceptor ,拦截 SpringMVC 处理请求的过程,自定义前置和处理的逻辑。
- 日志拦截器,记录请求与响应
- 认证拦截器,解析前端传入的用户标识,例如access_token 访问令牌,记录到 ThreadLocal 中。
- 授权拦截器,我们可以通过每个 API 接口需要的授权信息,进行判断,当前请求是否允许访问。
- 限流拦截器,我们可以通过每个 API 接口的限流配置,进行判断,当前请求是否超过允许的请求频率。
多个 HandlerInterceptor 可以组成一个 Chain 拦截器链。那么,整个正常执行的过程,就变成:
- 首先,按照 HandlerInterceptor 链的正序,执行 #preHandle(...) 方法。
- 然后,执行 handler 的逻辑处理。
- 之后,按照 HandlerInterceptor 链的倒序,执行 #postHandle(...) 方法。
- 最后,按照 HandlerInterceptor 链的倒序,执行 #afterCompletion(...) 方法。
执行流程如下:
// 首先,按照 HandlerInterceptor 链的**正序**,执行 `preHandle(...)` 方法。
INFO Interceptors.FirstInterceptor : [preHandle]
INFO Interceptors.ThirdInterceptor : [preHandle]
// 然后,执行 `handler` 的逻辑处理。
INFO springdockfiledemo.Control.DemoTest :
[ineceptorTest start to do something ...]
// 之后,按照 HandlerInterceptor 链的**倒序**,执行 `postHandle(...)` 方法。
INFO Interceptors.ThirdInterceptor : [postHandle]
INFO Interceptors.FirstInterceptor : [postHandle]
// 最后,按照 HandlerInterceptor 链的**倒序**,执行 `afterCompletion(...)` 方法。
INFO Interceptors.ThirdInterceptor : [afterCompletion]
ERROR servlet.HandlerExecutionChain : HandlerInterceptor.afterCompletion threw exception
java.lang.RuntimeException: 故意抛个错误
INFO Interceptors.FirstInterceptor : [afterCompletion]
2 核心代码
1)User类
@Data
public class User {
private Integer id;
private String name;
}
2)Servlet、Filter、Listener 配置
(1)通过 Bean 的方式
@Bean
public ServletRegistrationBean testServlet01() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("[doGet][uri: {}]", req.getRequestURI());
}
});
servletRegistrationBean.setUrlMappings(Collections.singleton("/test/01"));
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean testFilter01() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new Filter() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("[doFilter]");
filterChain.doFilter(servletRequest, servletResponse);
}
});
filterRegistrationBean.setUrlPatterns(Collections.singleton("/test/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean<?> testListener01() {
return new ServletListenerRegistrationBean<>(new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("[contextInitialized]");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
});
}
(2)通过注解的方式
@WebServlet(urlPatterns = "/test/02")
public class TestServlet02 extends HttpServlet {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("[doGet][uri: {}]", req.getRequestURI());
}
}
// TestFilter02.java
@WebFilter("/test/*")
public class TestFilter02 implements Filter {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("[doFilter]");
filterChain.doFilter(servletRequest, servletResponse);
}
}
// TestServletContextListener02.java
@WebListener
public class TestServletContextListener02 implements ServletContextListener {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("[contextInitialized]");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}