Spring全家桶面试题(2021优化版) (qq.com)
测试、日志、Thymeleaf、webmvc、servletAPI、Tomcat
<dependencies>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-engineartifactId>
<version>5.7.2version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
<version>3.0.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>9.0.46version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
dependencies>
<configuration debug="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
root>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG" />
<logger name="controller" level="DEBUG" />
configuration>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMVCservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springConfig/springmvc-config.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMVCservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<context:component-scan base-package="controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
bean>
property>
bean>
property>
bean>
beans>
package controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @author 13544
*/
@Controller
public class PortalController {
private Logger logger =LoggerFactory.getLogger(this.getClass().getName());
@RequestMapping(value = "/portal", method = {RequestMethod.GET})
public ModelAndView portalView(ModelAndView mv) {
logger.debug("进入了PortalServlet的indexView....");
mv.addObject("username", "lijiamin");
//封装要跳转的视图,放在ModelAndView中,这里涉及到了逻辑视图的知识点
mv.setViewName("portal");
return mv;
}
@RequestMapping(value = "/", method = {RequestMethod.GET})
public ModelAndView indexView(ModelAndView mv) {
logger.debug("进入了IndexServlet的indexView....");
//封装要跳转的视图,放在ModelAndView中,这里涉及到了逻辑视图的知识点
mv.setViewName("index");
return mv;
}
}
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<body>
hello
<br>
<a th:href="@{/portal}">跳转到portal-2a>
body>
html>
---------------------------------------------------------------------------
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>跳转页title>
head>
<body>
<h1 th:text="${username}">texth1>
body>
html>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>4_SPringMVC_testartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<name>4_SPringMVC_test Maven Webappname>
<url>http://www.example.comurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.8version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-logging-apiartifactId>
<version>1.1version>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>9.0.46version>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>9.0.46version>
dependency>
dependencies>
<build>
<finalName>4_SPringMVC_testfinalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-pluginartifactId>
<version>3.1.0version>
plugin>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
plugin>
<plugin>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.1version>
plugin>
<plugin>
<artifactId>maven-war-pluginartifactId>
<version>3.2.2version>
plugin>
<plugin>
<artifactId>maven-install-pluginartifactId>
<version>2.5.2version>
plugin>
<plugin>
<artifactId>maven-deploy-pluginartifactId>
<version>2.8.2version>
plugin>
plugins>
pluginManagement>
build>
project>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMVC2servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:servlet/springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMVC2servlet-name>
<url-pattern>/hello1url-pattern>
servlet-mapping>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> bean> <bean id="/hello1" class="ssm.controller.HelloController01"/>beans>
package ssm.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @author 13544 */public class HelloController01 implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { System.out.println("handleRequest.."); //ModelAndView 模型和视图 ModelAndView mv = new ModelAndView(); //封装对象,放在ModelAndView中。Model mv.addObject("msg", "HelloSpringMVC!"); //封装要跳转的视图,放在ModelAndView中,这里涉及到了逻辑视图的知识点 mv.setViewName("test_jsp"); return mv; }}
Hello World!
${msg}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0modelVersion> <groupId>org.examplegroupId> <artifactId>4_SPringMVC_testartifactId> <version>1.0-SNAPSHOTversion> <packaging>warpackaging> <name>4_SPringMVC_test Maven Webappname> <url>http://www.example.comurl> <properties> <project.build.sourceEncoding>UTF-8project.build.sourceEncoding> <maven.compiler.source>1.7maven.compiler.source> <maven.compiler.target>1.7maven.compiler.target> properties> <dependencies> <dependency> <groupId>junitgroupId> <artifactId>junitartifactId> <version>4.11version> <scope>testscope> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webmvcartifactId> <version>5.3.8version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-beansartifactId> <version>5.3.8version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webartifactId> <version>5.3.8version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-expressionartifactId> <version>5.3.8version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-coreartifactId> <version>5.3.8version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-contextartifactId> <version>5.3.8version> dependency> <dependency> <groupId>commons-logginggroupId> <artifactId>commons-loggingartifactId> <version>1.2version> dependency> <dependency> <groupId>commons-logginggroupId> <artifactId>commons-logging-apiartifactId> <version>1.1version> dependency> <dependency> <groupId>org.apache.tomcat.embedgroupId> <artifactId>tomcat-embed-coreartifactId> <version>9.0.46version> dependency> <dependency> <groupId>org.apache.tomcat.embedgroupId> <artifactId>tomcat-embed-coreartifactId> <version>9.0.46version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-aopartifactId> <version>5.3.8version> dependency> dependencies> <build> <resources> <resource> <directory>src/main/javadirectory> <includes> <include>**/*.propertiesinclude> <include>**/*.xmlinclude> includes> <filtering>falsefiltering> resource> <resource> <directory>src/main/resourcesdirectory> <includes> <include>**/*.propertiesinclude> <include>**/*.xmlinclude> includes> <filtering>falsefiltering> resource> resources> <finalName>4_SPringMVC_testfinalName> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-pluginartifactId> <version>3.1.0version> plugin> <plugin> <artifactId>maven-resources-pluginartifactId> <version>3.0.2version> plugin> <plugin> <artifactId>maven-compiler-pluginartifactId> <version>3.8.0version> plugin> <plugin> <artifactId>maven-surefire-pluginartifactId> <version>2.22.1version> plugin> <plugin> <artifactId>maven-war-pluginartifactId> <version>3.2.2version> plugin> <plugin> <artifactId>maven-install-pluginartifactId> <version>2.5.2version> plugin> <plugin> <artifactId>maven-deploy-pluginartifactId> <version>2.8.2version> plugin> plugins> pluginManagement> build>project>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>springMVCservlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:springConfig/springmvc-config.xmlparam-value> init-param> <load-on-startup>1load-on-startup> servlet> <servlet-mapping> <servlet-name>springMVCservlet-name> <url-pattern>/url-pattern> servlet-mapping>web-app>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <context:component-scan base-package="controller">context:component-scan> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".html"/> bean> beans>
package controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.servlet.ModelAndView;/** * 首页跳转,主页视图 * * @author 13544 */@Controllerpublic class IndexServlet { @RequestMapping(value = "/",method = {RequestMethod.GET}) public ModelAndView indexView(ModelAndView mv) { System.out.println("进入了IndexServlet的indexView...."); //封装要跳转的视图,放在ModelAndView中,这里涉及到了逻辑视图的知识点 mv.setViewName("index"); return mv; }}
如果方法中添加了Model参数,那么每次调用该请求处理方法时,Spring MVC都会创建Model对象,并将其作为参数传递给方法。
在方法上可以标注
@RestController
,是一个Controller
和ResponseBody
的增强版@RestControllerAdvice
其实是一种用来简化上面代码的一种东西
@GetMapping:匹配Get方式请求
@PostMapping:匹配Post方式请求
@PutMapping:。。。。
@DeleteMapping:。。。。
@PatchMapping:。。。。
@GetMapping(value = “/HelloController01”) 其实相当于 @RequestMapping(value = “/HelloController01”,method = RequestMethod.GET) ,是一种简写
到 web.xml 中配置 CharacterEncodingFilter 即可
<filter> <filter-name>CharacterEncodingFilterfilter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class> <init-param> <param-name>encodingparam-name> <param-value>UTF-8param-value> init-param> <init-param> <param-name>forceRequestEncodingparam-name> <param-value>trueparam-value> init-param> <init-param> <param-name>forceResponseEncodingparam-name> <param-value>trueparam-value> init-param>filter><filter-mapping> <filter-name>CharacterEncodingFilterfilter-name> <url-pattern>/*url-pattern>filter-mapping>
TIP
注1:在较低版本的 SpringMVC 中,forceRequestEncoding 属性、forceResponseEncoding 属性没有分开,它们是一个 forceEncoding 属性。这里需要注意一下。
注2:由于 CharacterEncodingFilter 是通过 request.setCharacterEncoding(encoding); 来设置请求字符集,所以在此操作前不能有任何的 request.getParameter() 操作。在设置字符集之前获取过请求参数,那么设置字符集的操作将无效。
注:如果形参上设置的参数在请求上没有传过来,默认会报错400,需要用required关闭默认必须要有请求参数
有时前端请求中参数名和后台控制器类方法中的形参名不一样,就会导致后台无法正确绑定并接收到前端请求的参数。为此,Spring MVC提供了@RequestParam
注解来进行间接数据绑定
@RequestMapping(value = "/hello") public ModelAndView hello(@RequestParam(value = "helloId") Integer id, ModelAndView mv) { System.out.println(id); mv.setViewName("index"); return mv; }
输入url:http://localhost:8080/07_test/hello?helloId=234,就能看见控制台输出了数字234
@Controller@RequestMapping(value = "/HelloController01")public class HelloController01 { @RequestMapping(value = "/say2", method = {RequestMethod.POST}) public String sayAno3(UserInfo user) { System.out.println(user); return "/WEB-INF/view/test.jsp"; }
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>首页标题title>head><body><h1>第一个首页h1><form action="/HelloController01/say2" method="post"> 用户名<input type="text" name="userName"/>br> 密码<input type="text" name="userPwd"/>br> <input type="submit" name="submit" value="提交">form>body>html>
如果前端是一组名称相同的数据,那么就需要使用数组或者集合进行接收,而不是用JavaBean直接拿过来
@RequestMapping(value = "/hello") public ModelAndView hello(Integer[] ids , ModelAndView mv) { System.out.println(id); mv.setViewName("index"); return mv; }
就类似与接收对象一样,这里放一个同前端传参名的数组就好了
表单
<form th:action="@{/param/one/name/multi/value}" method="post"> 请选择你最喜欢的球队: <input type="checkbox" name="team[0]" value="Brazil"/>巴西 <input type="checkbox" name="team" value="German"/>德国 <input type="checkbox" name="team" value="French"/>法国 <input type="checkbox" name="team" value="Holland"/>荷兰 <input type="checkbox" name="team" value="Italian"/>意大利 <input type="checkbox" name="team" value="China"/>中国 <br/> <input type="submit" value="保存"/>form>
方法
@RequestMapping("/param/one/name/multi/value")public String oneNameMultiValue( // 在服务器端 handler 方法中,使用一个能够存储多个数据的容器就能接收一个名字对应的多个值请求参数 @RequestParam("team") List teamList ) { for (String team : teamList) { logger.debug("team = " + team); } return "target";}
忘记写了
通过这个注解获取请求消息头中的具体数据
@RequestMapping("/request/header")public String getRequestHeader( // 使用 @RequestHeader 注解获取请求消息头信息 // name 或 value 属性:指定请求消息头名称 // defaultValue 属性:设置默认值 @RequestHeader(name = "Accept", defaultValue = "missing") String accept) { logger.debug("accept = " +accept); return "target";}
获取当前请求中的 Cookie 数据
@RequestMapping("/request/cookie")public String getCookie( // 使用 @CookieValue 注解获取指定名称的 Cookie 数据 // name 或 value 属性:指定Cookie 名称 // defaultValue 属性:设置默认值 @CookieValue(value = "JSESSIONID", defaultValue = "missing") String cookieValue, // 形参位置声明 HttpSession 类型的参数即可获取 HttpSession 对象 HttpSession session) { logger.debug("cookieValue = " + cookieValue); return "target";}
如果我们想要获取链接地址中的某个部分的值,就可以使用 @PathVariable 注解
@PathVariable(value = "movieId")
json数据交互依赖包
@RequestBody:方法形参
@ResponseBody:类或方法
<dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-annotationsartifactId> <version>2.9.8version> dependency> <dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-databindartifactId> <version>2.9.8version> dependency> <dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-coreartifactId> <version>2.9.8version> dependency>
转发
@RequestMapping("/test/forward/command")public String forwardCommand() { // 需求:要转发前往的目标地址不在视图前缀指定的范围内, // 通过返回逻辑视图、拼接前缀后缀得到的物理视图无法达到目标地址 // 走视图解析器,但是不会自动添加前后缀了 // 这个是WEB-INF之外的转发 return "forward:/outter.html"; // 转发到指定的地址: return "forward:/WEB-INF/outter.html";}
重定向
@RequestMapping("/test/redirect/command")public String redirectCommand() { // 重定向到指定的地址: // 这个地址由 SpringMVC 框架负责在前面附加 contextPath,所以我们不能加,我们加了就加多了 // 框架增加 contextPath 后:/demo/outter.html // 我们多加一个:/demo/demo/outter.html return "redirect:/outter.html";}
@Controllerpublic class PortalController { @RequestMapping(value = "/", method = {RequestMethod.GET}) public ModelAndView indexView(ModelAndView mv) { //封装要跳转的视图,放在ModelAndView中 mv.setViewName("index"); return mv; }}
在形参上把东西写上去就完了
@RequestMapping("/original/api/direct")public String getOriginalAPIDirect( // 有需要使用的 Servlet API 直接在形参位置声明即可。 // 需要使用就写上,不用就不写,开发体验很好,这里给 SpringMVC 点赞 HttpServletRequest request, HttpServletResponse response, HttpSession session) { logger.debug(request.toString()); logger.debug(response.toString()); logger.debug(session.toString()); return "target";}
或者通过容器注入
// 获取ServletContext对象的方法二:从 IOC 容器中直接注入@Autowiredprivate ServletContext servletContext; @RequestMapping("/original/servlet/context/second/way")public String originalServletContextSecondWay() { logger.debug(this.servletContext.toString()); return "target";}
@RequestMapping("/attr/request/model")public String testAttrRequestModel( // 在形参位置声明Model类型变量,用于存储模型数据 Model model) { // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域 // 存入请求域这个动作也被称为暴露到请求域 model.addAttribute("requestScopeMessageModel","i am very happy[model]"); return "target";}
@RequestMapping("/attr/request/model/map")public String testAttrRequestModelMap( // 在形参位置声明ModelMap类型变量,用于存储模型数据 ModelMap modelMap) { // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域 // 存入请求域这个动作也被称为暴露到请求域 modelMap.addAttribute("requestScopeMessageModelMap","i am very happy[model map]"); return "target";}
@RequestMapping("/attr/request/map")public String testAttrRequestMap( // 在形参位置声明Map类型变量,用于存储模型数据 Map map) { // 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域 // 存入请求域这个动作也被称为暴露到请求域 map.put("requestScopeMessageMap", "i am very happy[map]"); return "target";}
@RequestMapping("/attr/request/original")public String testAttrOriginalRequest( // 拿到原生对象,就可以调用原生方法执行各种操作 HttpServletRequest request) { request.setAttribute("requestScopeMessageOriginal", "i am very happy[original]"); return "target";}
@RequestMapping("/attr/request/mav")public ModelAndView testAttrByModelAndView() { // 1.创建ModelAndView对象 ModelAndView modelAndView = new ModelAndView(); // 2.存入模型数据 modelAndView.addObject("requestScopeMessageMAV", "i am very happy[mav]"); // 3.设置视图名称 modelAndView.setViewName("target"); return modelAndView;}
使用会话域最简单直接的办法就是使用原生的 HttpSession 对象
@RequestMapping("/attr/session")public String attrSession( // 使用会话域最简单直接的办法就是使用原生的 HttpSession 对象 HttpSession session) { session.setAttribute("sessionScopeMessage", "i am haha ..."); return "target";}
应用域同样是使用原生对象来操作:
@Autowiredprivate ServletContext servletContext;@RequestMapping("/attr/application")public String attrApplication() { servletContext.setAttribute("appScopeMsg", "i am hungry..."); return "target";}
REST:Representational State Transfer,表现层资源状态转移
REST 风格主张在项目设计、开发过程中,具体的操作符合 HTTP 协议定义的请求方式的语义
操作 | 请求方式 |
---|---|
查询操作 | GET |
保存操作 | POST |
删除操作 | DELETE |
更新操作 | PUT |
过去做增删改查操作需要设计4个不同的URL,现在一个就够了。
操作 | 传统风格 | REST 风格 |
---|---|---|
保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST |
删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE |
更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT |
查询(表单回显) | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET |
<filter> <filter-name>hiddenHttpMethodFilterfilter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class> filter> <filter-mapping> <filter-name>hiddenHttpMethodFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <filter> <filter-name>EncodingFilterfilter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class> <init-param> <param-name>encodingparam-name> <param-value>utf-8param-value> init-param> filter> <filter-mapping> <filter-name>EncodingFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping>
控制器
@RequestMapping(value = "/getRestful/{movieId}", method = {RequestMethod.GET}) public ModelAndView getRestful(@PathVariable(value = "movieId", required = false) Integer id, ModelAndView mv) { System.out.println("id" + "是:" + id); return new ModelAndView("redirect:/"); }
html
<form th:action="@{/getRestful/3}" method="GET"> <button type="submit">点击开始查询button>form>
html
<td> <a th:href="@{/getRestful/} + ${movie.movieId} +'/'+ ${movie.movieName}"> <button>GETidbutton> a>td>
Java
@RequestMapping(value = "/getRestful/{movieId}/{movieName}", method = {RequestMethod.GET}) public ModelAndView getRestful(@PathVariable(value = "movieId", required = false) Integer id, @PathVariable(value = "movieName", required = false) String movieName, ModelAndView mv) { System.out.println(id + " and " + movieName); return new ModelAndView("redirect:/"); }
跟以前一样没变化,就from表单用post提交
表单
<form th:action="@{/putRestful}" method="post"> <input type="hidden" name="_method" value="put"/> id:<input type="text" name="id"> name:<input type="text" name="name"> <button type="submit">更新button>form>
控制器
// PUT @RequestMapping(value = "/putRestful", method = {RequestMethod.PUT}) public ModelAndView putRestful(ModelAndView mv, Integer id, String name) { System.out.println(id + " and " + name); return new ModelAndView("redirect:/"); }
1、from通用表单要放在div外,可以放在body内的最下面
2、js引入的逻辑方法放在body内的最下面
通用表单
<form id="form1" action="" method="post"> <input type="hidden" name="_method" value="delete">form>
删除
<a th:href="@{|/de/${movie.movieId}|}" @click="confirmRemoveMovie"> 删除 a>
JS
new Vue({ el: "#tbody1", methods: { "reomveMovie": function (event) { //获取通用表单 var formElem = document.getElementById("form1"); //修改通用表单的action formElem.action = event.target.href; //提交表单 formElem.submit(); //禁用目标对象的默认行为 event.preventDefault(); }, "confirmRemoveMovie": function (event) { var flag = window.confirm("您确认要删除该影片信息吗?") if (flag) { //获取通用表单 var formElem = document.getElementById("form1"); //修改通用表单的action formElem.action = event.target.href; //提交表单 formElem.submit(); } //禁用目标对象的默认行为 event.preventDefault(); } }});
Java
// 删除 @RequestMapping(value = "/de/{movieId}", method = {RequestMethod.DELETE}) public ModelAndView deleteId(ModelAndView mv, @PathVariable(value = "movieId") Integer movieId) { System.out.println("id是:" + movieId); String str = Integer.toString(movieId); movieServiceImpl.removeMovieById(str); return new ModelAndView("redirect:/"); }
主要的两个注解
@RequestBody:方法形参
@ResponseBody:类或方法
下面的示例是用JSON格式进行传输
注意:异步请求的返回值参数好像不能用模型,如果用模型返回前端页面好像无法读取
1、可以在形参上填写类对象,那么从前端传送过去的数据会根据set方法对这个形参对象进行相应的注入
2、也可以在码方法的时候,返回值选择对象而不是字符串,那么返回给页面的时候他也能自动toString
html
<head> <script type="text/javascript" src="/04_SpringMVC/static/vue.js">script> <script type="text/javascript" src="/04_SpringMVC/static/axios.js">script> <meta charset="UTF-8"> <title>跳转页title>head>--------------------------------------<div id="test"> <form th:action="@{/}"> 请输入:<input type="text" placeholder="请输入用户名" name="userName" v-model="userName" @blur="blurFocus" > <button type="submit">提交button> form>div>
javascript
let vue1 = new Vue( { el: "#test", data: { userName: "" }, methods: { blurFocus() { axios({ method: "POST", // Thymeleaf语法,加上项目前缀 url: "[[@{/asioxTest}]]", data: { userName: this.userName } // 这个data就是传json的意思了,以前穿普参用params } ).then(function (resp) { if (resp.data == "-1") { alert("你有毛病吧") } else if (resp.data == "1") { alert("希望人没事") } else { console.info(resp.data) } }).catch(function (error) { console.error(error) }) } } } )
java
@RestControllerpublic class AxiosTest { @RequestMapping(value = "/asioxTest") public String asioxTest(@RequestBody String userName) { System.out.println(userName + ":是你!!"); String x = "Oh.......No!"; return "1"; }}
MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
(13条消息) 过滤器(Filter)和拦截器(Interceptor)的区别_无知人生,记录点滴-CSDN博客
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义:
1、通过实现 HandlerInterceptor
接口或者继承 HandlerInterceptor
接口的实现类(如HandlerInterceptorAdapter)来定义。
2、通过实现 WebRequestInterceptor
接口或继承 WebRequestInterceptor
接口的实现类来定义。
以实现HandlerInterceptor接口的定义方式为例,自定义拦截器类的代码如下所示。
提示:在拦截器中,将数据存入请求域、转发或重定向请求都需要使用原生对象来完成,SpringMVC 并没有提供 Model、ModelMap 等 API 供我们使用。
<mvc:interceptors> <bean class="Interceptor.Process01Interceptor"/> <mvc:interceptor> <mvc:mapping path="/portal"/> <bean class="Interceptor.Process01Interceptor"/> mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/portal/*"/> <bean class="Interceptor.Process01Interceptor"/> mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/portal/**"/> <bean class="Interceptor.Process01Interceptor"/> mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/portal/**"/> <mvc:exclude-mapping path="/portal/request/bbb"/> <bean class="Interceptor.Process01Interceptor"/> mvc:interceptor>mvc:interceptors>
package Interceptor;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Process01Interceptor implements HandlerInterceptor { /* * preHandle()方法: * 该方法会在控制器方法前执行,其返回值表示是否中断后续操作。 * 当其返回值为true时,表示继续向下执行; * 当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。 * * postHandle()方法: * 该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。 * * afterCompletion()方法: * 该方法在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些异常处理、资源清理、记录日志信息等工作。 * */ /* * 当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行 * 而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行 * */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle---"); /* * 如果返回false,需要提供拦截后的跳转页面 * request.getRequestDispatcher("error").forward(request,response); * * 同时,在这个方法中可以获取请求 request 的参数进行判断是否放行 * */ return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle---"); /* * 在目标资源后执行,可以篡改跳转后的资源数据 * modelAndView.addObject("username","我被postHandle拐走了"); * * 也可以篡改目标跳转走向 * modelAndView.setViewName("error"); * */ } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion---"); }}
SpringMVC 将『把请求参数注入到 POJO 对象』这个操作称为『数据绑定』
,SpringMVC 对基本数据类型提供了自动的类型转换。例如:请求参数传入“100”字符串,我们实体类中需要的是 Integer 类型,那么 SpringMVC 会自动将字符串转换为 Integer 类型注入实体类。
通过注解设定格式
public class Product { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date productDate; @NumberFormat(pattern = "###,###,###.###") private Double productPrice;
表单
<form th:action="@{/save/product}" method="post"> 生产日期:<input type="text" name="productDate" value="1992-10-15 17:15:06" /><br/> 产品价格:<input type="text" name="productPrice" value="111,222,333.444" /><br/> <button type="submit">保存button>form>
BindingResult bindingResult对象可以获取到相关错误信息
400
页面BindingResult
接口和它的父接口 Errors
中定义了很多和数据绑定相关的方法,如果在数据绑定过程中发生了错误,那么通过这个接口类型的对象就可以获取到相关错误信息
分控制器方法
@RequestMapping("/save/product")public String saveProduct( Product product, // 在实体类参数和 BindingResult 之间不能有任何其他参数 // 封装数据绑定结果的对象 BindingResult bindingResult) { // 判断数据绑定过程中是否发生了错误 if (bindingResult.hasErrors()) { // 如果发生了错误,则跳转到专门显示错误信息的页面 // 相关错误信息会自动被放到请求域 return "error"; } logger.debug(product.toString()); return "target";}
页面错误信息
<p th:errors="${product.productDate}">这里显示具体错误信息p>
在实际开发过程中,难免会有某些情况需要使用自定义类型转换器。因为我们自己自定义的类型在 SpringMVC 中没有对应的内置类型转换器。此时需要我们提供自定义类型来执行转换
public class Student { private Address address; …… ----------------------------------------------public class Address { private String province; private String city; private String street; ……
<h3>自定义类型转换器h3><form th:action="@{/save/student}" method="post">地址:<input type="text" name="address" value="aaa,bbb,ccc" /><br/>form>
@RequestMapping("/save/student")public String saveStudent(Student student) { logger.debug(student.getAddress().toString()); return "target";}
public class AddressConverter implements Converter<String, Address> { // String转Address @Override public Address convert(String source) { // 1.按照约定的规则拆分源字符串 String[] split = source.split(","); String province = split[0]; String city = split[1]; String street = split[2]; // 2.根据拆分结果创建 Address 对象 Address address = new Address(province, city, street); // 3.返回转换得到的对象 return address; }}
<mvc:annotation-driven conversion-service="formattingConversionService"/> <bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.atguigu.mvc.converter.AddressConverter"/> set> property> bean>
表述层数据检查
JSR 303标准
注解 | 规则 |
---|---|
@Null | 标注值必须为 null |
@NotNull | 标注值不可为 null |
@AssertTrue | 标注值必须为 true |
@AssertFalse | 标注值必须为 false |
@Min(value) | 标注值必须大于或等于 value |
@Max(value) | 标注值必须小于或等于 value |
@DecimalMin(value) | 标注值必须大于或等于 value |
@DecimalMax(value) | 标注值必须小于或等于 value |
@Size(max,min) | 标注值大小必须在 max 和 min 限定的范围内 |
@Digits(integer,fratction) | 标注值值必须是一个数字,且必须在可接受的范围内 |
@Past | 标注值只能用于日期型,且必须是过去的日期 |
@Future | 标注值只能用于日期型,且必须是将来的日期 |
@Pattern(value) | 标注值必须符合指定的正则表达式 |
其余扩展注解
注解 | 规则 |
---|---|
标注值必须是格式正确的 Email 地址 | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
实体信息
// 字符串必须满足Email格式 @Email private String email;
分控制器方法(注意注解@Validated
)
@RequestMapping("/save/president")public String savePresident( // 在实体类参数和 BindingResult 之间不能有任何其他参数 @Validated President president, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "error"; } logger.debug(president.getEmail()); return "target";}
如果出现问题,则会在我们指定跳转的页面抛出400异常
<h1>系统信息h1> <p th:errors="${president.email}">这里显示系统提示消息p>
①微观
将异常类型和某个具体的视图关联起来,建立映射关系。好处是可以通过 SpringMVC 框架来帮助我们管理异常。
②宏观
整个项目从架构这个层面设计的异常处理的统一机制和规范。
一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱
懒得看
异常处理器类加入IOC容器
<context:component-scan base-package="com.atguigu.mvc.exception"/>
声明处理异常的方法
// 异常处理器类需要使用 @ControllerAdvice 注解标记@ControllerAdvicepublic class MyExceptionHandler { // @ExceptionHandler注解:标记异常处理方法 // value属性:指定匹配的异常类型,可以是RunTimeException // 异常类型的形参:SpringMVC 捕获到的异常对象 @ExceptionHandler(value = NullPointerException.class) public String resolveNullPointerException(Exception e, Model model) { // 我们可以自己手动将异常对象存入模型 model.addAttribute("Exception", e); // 返回逻辑视图名称,在页面就可以利用Thymeleaf显示异常信息 return "error-nullpointer"; }}
查看请求消息头中是否包含 Ajax 请求独有的特征:
所以可以写一个判断来辨别
Boolean bo = request.getHeader("Accept").contains("application/json");System.out.println("他是JSON吗" + bo);
<dependency> <groupId>commons-fileuploadgroupId> <artifactId>commons-fileuploadartifactId> <version>1.4version> dependency>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"/>bean>
package controller.download;import java.io.File;import java.math.BigInteger;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.UUID;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.servlet.ModelAndView;import javax.servlet.ServletContext;import java.io.IOException;@Controllerpublic class UploadDemo01 { @Autowired private ServletContext servletContext; @RequestMapping(value = "/simple/upload", method = {RequestMethod.POST}) public ModelAndView upload( @RequestParam(value = "pic") MultipartFile pic ) throws IOException, NoSuchAlgorithmException { // 1、虚拟路径 - 需求根因:解决多操作系统下的路径问题 String destFileFolderVirtualPath = "/DownloadDemo01"; // 2、调用 ServletContext 对象的方法将虚拟路径转换为真实物理路径 String destFileFolderRealPath = servletContext.getRealPath(destFileFolderVirtualPath); // 判断文件是否为空 pic.isEmpty(); // 文件大小(有缺陷,已经上传到了服务器的临时文件夹) pic.getSize(); // 当前上传文件的二进制内容组成的字节数组 pic.getBytes(); //使用UUID随机产生文件名称,防止同名文件覆盖 String fileName = UUID.randomUUID().toString() + "文件名后面带后缀的那个"; // 3、获取文件完整名称 String originalFilename = pic.getOriginalFilename(); // 4、根据文件完整名称获取文件扩展名 String fileExtname = originalFilename.substring(originalFilename.lastIndexOf(".")); // 5、获取MD5转换器 MessageDigest md5 = MessageDigest.getInstance("MD5"); // 6、获取文件的byte信息 byte[] uploadBytes = pic.getBytes(); // 7、对文件进行MD5加密 byte[] digest = md5.digest(uploadBytes); // 8、转换为16进制 String fileFirstname = new BigInteger(1, digest).toString(16); // 9、拼装起来就是我们生成的整体文件名 String destFileName = fileFirstname + "" + fileExtname; // 10、路径 + 文件名 的拼接 String destFilePath = destFileFolderRealPath + "/" + destFileName; // 11、数据转入File对象 File file = new File(destFilePath); // 12、写入硬盘 pic.transferTo(file); return new ModelAndView("redirect:/portal"); }}
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8">property> <property name="maxUploadSize" value="1000000">property> <property name="maxUploadSizePerFile" value="100000">property>bean>
@Autowiredprivate ServletContext servletContext;@RequestMapping("/download/file")public ResponseEntity<byte[]> downloadFile() { // 1.获取要下载的文件的输入流对象 // 这里指定的路径以 Web 应用根目录为基准 InputStream inputStream = servletContext.getResourceAsStream("/images/mi.jpg"); try { // 2.将要下载的文件读取到字节数组中 // ①获取目标文件的长度 int len = inputStream.available(); // ②根据目标文件长度创建字节数组 byte[] buffer = new byte[len]; // ③将目标文件读取到字节数组中 inputStream.read(buffer); // 3.封装响应消息头 // ①创建MultiValueMap接口类型的对象,实现类是HttpHeaders MultiValueMap responseHeaderMap = new HttpHeaders(); // ②存入下载文件所需要的响应消息头 responseHeaderMap.add("Content-Disposition", "attachment; filename=mi.jpg"); // ③创建ResponseEntity对象 ResponseEntity responseEntity = new ResponseEntity<>(buffer, responseHeaderMap, HttpStatus.OK); // 4.返回responseEntity对象 return responseEntity; } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null;}
DispatcherServlet:前端总控制器
获取前端请求并与后端模块进行交互
HandlerMapping:处理映射器
建立了请求路径和分控制器方法之间的映射
HandlerExecutionChain:处理器执行链
总控制器调用HandlerMapping组件的返回值是一个执行链,不仅有要执行的分控制器方法,还有相应的多个拦截器,组成一个执行链。
此类下包含了分控制器方法和列表类型的拦截器,所以它是按顺序链式执行的
HandlerAdapter:处理适配器
调用分控制器的方法,不是由总控制器之间调用的,而是由HandlerAdapter来调用
因为会有XML方式、注解方式等处理器形式,具体执行会有不同,通过不同的HandlerAdapter来实现。这里用到了适配器设计模式
ViewResolver:视图解析器
将前端控制器交付的视图进行解析
逻辑视图(result)----->物理视图(/WEB-INF/templates/result.html)
SSM整合后,配置文件内容过多,可以分到两个配置文件中。这两个配置文件夹如何加载
方法1:DispatcherServlet加载所有的配置文件(只有一个Ioc容器,存放所有的Bean)
<servlet> <servlet-name>dispatcherServletservlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring*.xmlparam-value> init-param> <load-on-startup>1load-on-startup>servlet>
方法2:DispatcherServlet加载springmvc的配置文件,使用ContextLoaderListener加载另外一个配置文件(会有两个Ioc容器)
会有两个Ioc容器
使用ContextLoaderListener加载另外一个配置文件创建的IoC容器是父容器。
DispatcherServlet加载springmvc的配置文件创建的IoC容器是子容器。
注意:Servlet、Filter、Listener的加载顺序:Listener、Filter、Servlet
我们一般都会在MVC中的web.xml进行如下配置
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/spring.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
ContextLoader
类ContextLoaderListener可以指定在Web应用程序启动时载入Ioc容器
ServletContextListener
接口用来监听Servlet容器的初始化,当tomcat启动时会初始化一个Servlet容器,这样ContextLoaderListener会监听到Servlet容器的初始化,这样在Servlet容器初始化之后我们就可以在ContextLoaderListener中也进行一些初始化操作
总结
在启动Web容器时(继承了
ContextLoader
类,可以指定在Web应用程序启动时载入Ioc容器),开始初始化spring的web应用上下文(因为实现了ServletContextListener
接口,启动容器时,就会默认执行它实现的方法),读取ContextConfigLocation中定义的xml文件,自动装配ApplicationContext的配置信息并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里。这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean
一句话概括:为项目提供了spring支持,初始化了Ioc容器
如果
如何查看:将logback的总的日志级别改为DEBUG
缺点:
重复的bean会多占用资源
SpringMVC创建的Controller肯定是调用SpringMVC自己创建的Service和Dao,但是在SpringMVC的配置文件中并没有关于事务的设置,所以调用SpringMVC自己创建的Service和Dao,将无法使用到事务。这绝对不可以。
解决方案1:两个配置文件中扫描不同的包
<context:component-scan base-package="com.atguigu.service,com.atguigu.dao">context:component-scan>
<context:component-scan base-package="com.atguigu.controller">context:component-scan>
结果:SpringMVC中创建了Controller,Listener中创建了Service并应用了事务。当SpringMVC在自己的IoC容器中找不到Service的时候,就会到父容器中去找Service。问题解决。
解决方案2:两个配置文件中扫描不同的包
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
整个请求处理过程都是 DispatcherServlet 的 doDispatch() 方法在宏观上协调和调度
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查请求是否是multipart(即文件上传),若是进行相关处理
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 确定当前请求的处理程序
mappedHandler = getHandler(processedRequest);
// 如果找不到对应的处理器的话,就像这样
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 确定当前请求的处理程序适配器(例如是注解适配器还是配置文件适配器)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果处理程序支持,则处理最后修改的头文件
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行chain中拦截器附加的预处理方法,即preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 适配器统一执行handle方法(适配器统一接口的作用),此处是真正处理业务逻辑的地方
// 执行分控制器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理分发结果:包括解析视图并进行视图渲染,执行chain中拦截器附加的后处理方法,即afterCompletion方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
具体看源码吧
简单囊括
服务器解析客户端的URL请求获取Controller对象和对应的接口方法
,然后运用Java的反射运行对应的接口方法。
不简单囊括
获取的方式依赖@Controller和@RequestMapping注解,由URL解析出接口方法上的注解@RequestMapping的值,再根据这个值映射对应的接口方法。
这种映射方式需要把注解@RequestMapping的值放入Map容器中缓存起来,Map中的key为注解@RequestMapping的值、value为对应的接口方法的Method对象。当读取URL时,就相当于有了key,此时就可以从容器中获取接口方法的Method对象了。
类型 | 过滤器Filter | 拦截器interceptor |
---|---|---|
规范 | Filter是在Servlet规范中定义的,是Servlet容器支持的 | 拦截器是在Spring容器内的,是Spring框架支持的 |
使用范围 | 过滤器只能用于Web程序 中 |
拦截器既可以用于Web程序 ,也可以用于Application、Swing程序 中 |
原理 | 过滤器是基于函数回调 | 拦截器是基于java的反射机制 |
使用的资源 | 过滤器不能使用Spring资源 | 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,可以通过loC注入到拦截器 |
深度 | Filter在只在Servlet前后起作用 | 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性 |