第一章 初识SpringMVC
1.1 SpringMVC概述
- SpringMVC是Spring子框架,是Spring 为展现层提供的基于 MVC 设计理念的优秀的 Web 框架
- SpringMVC通过一套 MVC 注解,让 POJO 成为处理请求的控制器【请求处理器】,而无须实现任何接口。
- SpringMVC是非侵入式
- @Component、@Repository、@Service、@Controller
1.2 SpringMVC基本工作原理
1.3 搭建SpringMVC步骤
-
创建web工程,打包方式设置war
-
导入jar包
<!--spring-webmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.1</version> </dependency> <!-- 导入thymeleaf与spring5的整合包 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>
-
在web.xml中注册DispatcherServlet【前端控制器】
- url设置为【/】
- 设置springmvc.xml配置文件的路径,从而管理SpringMVC容器对象
- 设置DispatcherServlet的优先级【启动服务器,创建当前DispatcherServlet】
-
创建springmvc.xml配置文件
- 名称:springmvc.xml
- 位置:src/main/resouces
- 内容
- 开启组件扫描
- 配置视图解析器
-
创建Controller【请求处理器】
- @Controller:标识当前类是一个请求处理器类
- @RequestMapping:将指定URL映射到指定方法或类上
-
创建相关页面,进行测试
第二章 @RequestMapping注解详解
2.1 @RequestMapping概述
- 描述:请求映射器
- 作用:将指定URL映射到指定方法或类上
2.2 @RequestMapping注解位置
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping {}
- 方法上
- 为当前方法映射URL【常用:可以单独使用】
- 类上
- 为当前类映射URL【不能单独使用,需要配置方法使用】
2.3 @RequestMapping注解属性【重点】
-
value属性
- 类型:String[]
- 作用:为方法或类映射URL
-
path属性
- 类型:String[]
- 作用:与value属性作用一致
-
method属性
-
类型:RequestMethod[]
public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }
-
作用:设置当前URL【方法或类】的请求方式【GET、POST、DELETE、PUT】
-
注意:
- 如不指定请求方式时,默认支持8种请求方式
- 如请求方式不被支持,会报如下错误【405】
-
-
params属性
-
类型:String[]
-
作用:设置当前URL【方法或类】的请求参数
-
注意:如未携带指定参数,会报如下错误【400】
-
-
headers属性
-
类型:String[]
-
作用:设置当前URL【方法或类】的请求头
-
注意:如未携带指定的请求头,会报错
-
2.4 @RequestMapping注解【扩展注解】
-
@GetMapping
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = RequestMethod.GET) public @interface GetMapping {}
-
@PostMapping
@RequestMapping(method = RequestMethod.POST) public @interface PostMapping {}
-
@DeleteMapping
@RequestMapping(method = RequestMethod.DELETE) public @interface DeleteMapping {}
-
@PutMapping
@RequestMapping(method = RequestMethod.PUT) public @interface PutMapping {}
2.5 @RequestMapping注解支持Ant风格URL
-
Ant 风格资源地址支持 3 种匹配符
?:匹配任意一个字符
*:匹配任意数量任意字符
**:匹配多层路径任意数量任意字符
-
语法
/** * 测试Ant风格URL */ @RequestMapping(value = "/testAnt/?/*/**") public String testAnt(){ System.out.println("==>testAnt!"); return "success"; }
2.6 @RequestMapping中占位符及@PathVariable注解【重点】
-
语法
- 定义占位符:{参数名称}
- 获取占位符:@PathVariable注解
-
示例代码
<a th:href="@{/helloController/testPathVariable/1001/zs}">测试@RequestMapping获取占位符数据</a><br>
@RequestMapping("testPathVariable/{userId}/{userName}") public String testPathVariable(@PathVariable("userId")Integer userId, @PathVariable("userName")String userName){ System.out.println("==>testPathVariable/1001/zs"); System.out.println("userId = " + userId); System.out.println("userName = " + userName); return "success"; }
第三章 SpringMVC中REST风格的CRUD
3.1 REST基本概述
- REST全称:RESTful
- REST风格的URL将HTTP协议中的四种请求方式GET、POST、PUT、DELETE分别对应四种基本操作:
- GET 用来获取资源【查询】
- POST 用来新建资源【增加】
- PUT 用来更新资源【修改】
- DELETE 用来删除资源【删除】
3.2 REST风格与传统风格CRUD对比
-
传统风格
功能 URL 请求方式 增加 /saveEmp POST 删除 /deleteEmpById?id=1001 GET 修改 /updateEmp POST 查询 /getEmpById?id=1001 GET -
REST风格
功能 URL 请求方式 增加 /emp POST 删除 /emp/1001 DELETE 修改 /emp PUT 查询 /emp/1001 GET -
总结:REST风格优势
- 提高网站排名
- 竞价排名
- 技术优化
- 便于第三方平台对接
- 提高网站排名
3.3 SpringMVC中使用HiddenHttpMethodFilter
-
HiddenHttpMethodFilter作用:将POST提交方式,转换为PUT或DELETE
-
HiddenHttpMethodFilter源码
public static final String DEFAULT_METHOD_PARAM = "_method"; private String methodParam = DEFAULT_METHOD_PARAM; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { String paramValue = request.getParameter(this.methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) { requestToUse = new HttpMethodRequestWrapper(request, method); } } } filterChain.doFilter(requestToUse, response); } /** * Simple {@link HttpServletRequest} wrapper that returns the supplied method for * {@link HttpServletRequest#getMethod()}. */ private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper(HttpServletRequest request, String method) { super(request); this.method = method; } @Override public String getMethod() { return this.method; } }
-
使用步骤
- 注册HiddenHttpMethodFilter
- 表单
- 表单必须以POST方式提交
- 表单中必须携带_method参数【PUT或DELETE(不区分大小写)】
-
示例代码
<h2>增加POST</h2> <form th:action="@{/emp}" method="post"> <input type="submit" value="添加员工信息"> </form> <h2>查询GET</h2> <a th:href="@{/emp/1001}">查询员工信息</a> <h2>删除DELETE</h2> <form th:action="@{/emp/1001}" method="post"> <input type="hidden" name="_method" value="DELETE"> <input type="submit" value="删除员工信息"> </form> <h2>修改PUT</h2> <form th:action="@{/emp}" method="post"> <input type="hidden" name="_method" value="PUT"> <input type="submit" value="修改员工信息"> </form>
@Controller public class EmployeeController { private static final String SUCCESS = "success"; /** * 添加员工信息 * @return */ // @RequestMapping(value = "/emp",method = RequestMethod.POST) @PostMapping("/emp") public String saveEmp(){ System.out.println("==>添加员工信息!"); return SUCCESS; } /** * 查询员工信息 * @return */ // @RequestMapping(value = "/emp",method = RequestMethod.GET) @GetMapping("/emp/{empId}") public String getEmpById(@PathVariable("empId")Integer empId){ System.out.println("==>查询员工信息!empId:"+empId); return SUCCESS; } /** * 删除员工信息 * @return */ // @RequestMapping(value = "/emp",method = RequestMethod.DELETE) @DeleteMapping("/emp/{empId}") public String deltEmpById(@PathVariable("empId")Integer empId){ System.out.println("==>删除员工信息!empId:"+empId); return SUCCESS; } /** * 修改员工信息 * @return */ // @RequestMapping(value = "/emp",method = RequestMethod.PUT) @PutMapping("/emp") public String updateEmp(){ System.out.println("==>修改员工信息"); return SUCCESS; } }
<?xml version="1.0" encoding="UTF-8"?> <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"> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <!--所有请求【PUT|DELEET】--> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 设置springmvc.xml配置文件的路径,从而管理SpringMVC容器对象--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 设置DispatcherServlet的优先级【启动服务器,创建当前DispatcherServlet】--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <!-- URL设置为/--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
第四章 SpringMVC处理请求数据【重点】
Servlet获取请求数据
- 获取请求参数:request.getParameter()...
- 请求体【参数】
- url【参数】
- 请求头
- request.getHeader()
- Cookie信息
- request.getCookies();
4.1 SpringMVC支持自动入参
-
概述:请求参数名与入参参数名一致,即可自动入参
-
注意:
- SpringMVC自带类型转换器【直接将参数转换为我们需要的类型】
- 如请求参数与入参的参数名不一致,不会报错。会将null值注入【所以建议用包装类型】
-
示例代码
<a th:href="@{/testRequestData(username='zs',age=18)}">测试自动入参</a>
/** * 测试自动入参 * @return */ @GetMapping("/testRequestData") public String testRequestData(String username,Integer age){ System.out.println("username = " + username); System.out.println("age = " + age); return SUCCESS; }
4.2 @RequestParam注解详解
-
作用:将指定的参数名,设置处理请求方法的入参参数中
-
属性
-
value:设置需要入参的参数名
-
name:与value作用一致
-
required:表示被@RequestParam注解标识的参数,是否必须入参
-
true:表示必须入参,如未入参,会报错400
-
false:表示不必须入参,如未入参,不会报错,会将null装配
-
-
defaultValue:设置当前入参的默认值【当参数为null时,不显示null显示默认值】
-
-
示例代码
<a th:href="@{/testRequestParam(eId=1001,eName='empChai')}">测试@RequestParam</a>
/** * 测试@RequestParam注解 * @return */ @GetMapping("/testRequestParam") public String testRequestParam(@RequestParam(value = "eId",required = false,defaultValue = "100001") Integer empId, @RequestParam("eName") String empName){ System.out.println("empId = " + empId); System.out.println("empName = " + empName); return SUCCESS; }
4.3 SpringMVC支持POJO入参
-
概述:入参时,参数名与POJO中的属性一致,SpringMVC支持POJO自动入参
-
支持级联属性赋值
-
示例代码
<h3>处理入参为POJO</h3> <form th:action="@{/testPOJO}" method="post"> 员工编号:<input type="text" name="id"><br> 员工姓名:<input type="text" name="lastName"><br> 员工邮箱:<input type="text" name="email"><br> 员工薪资:<input type="text" name="salary"><br> 员工部门编号:<input type="text" name="dept.deptId"><br> 员工部门名称:<input type="text" name="dept.deptName"><br> <input type="submit" value="Test_pojo"> </form>
/** * 测试POJO入参 * @return */ @PostMapping("/testPOJO") public String testPOJO(Employee employee){ System.out.println("employee = " + employee); return SUCCESS; }
4.4 SpringMVC处理请求头信息
-
语法:@RequestHeader
-
作用:获取请求头信息
-
位置:书写在参数前
-
属性
- value:设置请求头名称
- name:作用域value属性的作用是一致
- required:设置当前请求头是否必须入参
- true【默认值】:表示当前请求头必须入参,如未入参,会报错
- false:表示当前请求头不必须入参,如未入参,不会报错【会装配默认值:null】
- defaultValue:设置当前请求头默认数值【请求头信息为null时,显示默认值】
-
示例代码
/** * 获取请求头信息 */ @GetMapping("/testRequestHeader") public String testRequestHeader(@RequestHeader("User-Agent") String userAgent, @RequestHeader("Referer") String referer){ System.out.println("userAgent = " + userAgent); System.out.println("referer = " + referer); return SUCCESS; }
4.5 SpringMVC处理Cookie信息
-
语法:@CookieValue
-
作用:通过Cookie的key获取Cookie的value
-
位置:参数前
-
属性
- value:设置Cookie的key
- name:与value作用一致
- required:设置当前Cookie是否必须入参
- true【默认值】:表示当前Cookie必须入参,如未入参,会报错
- false:表示当前Cookie不必须入参,如未入参,不会报错【null】
- defaultValue:设置当前Cookie的默认值【Cookie信息为null时,显示默认值】
-
示例代码
/** * 获取Cookie */ @GetMapping("/getRequestCookie") public String getRequestCookie(@CookieValue(value = "JSESSIONID",required = false) String jsessionId){ System.out.println("jsessionId = " + jsessionId); return SUCCESS; }
4.6 使用原生Servlet-API
-
如需使用Servlet原生API,怎么办?
- 答:直接将原生对象入参即可
-
示例代码
@GetMapping("/setRequestCookie") public String setRequestCookie(HttpSession session, HttpServletRequest request, HttpServletResponse response){ //使用session对象时,就会默认创建特殊Cookie【cookie的key是session的Id】 System.out.println("session = " + session); System.out.println("request = " + request); // request.getParameter() System.out.println("response = " + response); return SUCCESS; }
第五章 SpringMVC处理响应数据【重点】
5.1 SpringMVC处理响应数据概述
-
SpringMVC提供了以下两种种途径输出模型数据
- 使用ModelAndView作为方法的返回值类型,处理响应数据
- 使用Model、Map、ModelMap作为方法入参,处理响应数据
-
SpringMVC中处理方法无论返回String还是ModelAndView或其他,最终底层都会封装为ModelAndView对象
5.2 源码解析ModelAndView对象
public class ModelAndView {
/** View instance or view name String. */
//理解:视图对象或视图名称【推荐使用视图名称】
@Nullable
private Object view;
/** Model Map. */
//public class ModelMap extends LinkedHashMap<String, Object> {
@Nullable
private ModelMap model;
/**
* Return the model map. May return {@code null}.
* Called by DispatcherServlet for evaluation of the model.
*/
@Nullable
protected Map<String, Object> getModelInternal() {
return this.model;
}
/**
* Return the underlying {@code ModelMap} instance (never {@code null}).
*/
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();
}
return this.model;
}
/**
* Return the model map. Never returns {@code null}.
* To be called by application code for modifying the model.
*/
public Map<String, Object> getModel() {
return getModelMap();
}
/**
设置模型数据
*/
public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
/**
* Set a view name for this ModelAndView, to be resolved by the
* DispatcherServlet via a ViewResolver. Will override any
* pre-existing view name or View.
*/
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
/**
* Return the view name to be resolved by the DispatcherServlet
* via a ViewResolver, or {@code null} if we are using a View object.
*/
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
}
5.3 session域使用
-
SpringMVC响应数据默认存储request域中
-
如使用其他域对象,通用方法是使用原生ServletAPI对象
- HttpServletRequest
- HttpSession
- ServletContext
-
使用session域常用方式如下
- 原生HttpSession对象
- 使用注解@SessionAttributes,可以将request域数据同步session域中
-
示例代码
@SessionAttributes(value = "lastName")//将request域中lastName同步到session域中 public class DoResponseDataController { @GetMapping("/testModelAndViewAndSession") public ModelAndView testSession(/*HttpSession session*/){ ModelAndView mv = new ModelAndView(); //设置视图名称 mv.setViewName("res_success"); //将数据存储session域 // session.setAttribute("lastName","empChai"); //使用注解 mv.addObject("lastName","empChai"); return mv; } }
5.4 使用Model、Map、ModelMap作为方法入参,处理响应数据
/**
* 使用Map、Model、ModelMap处理响应数据
* @return
*/
@GetMapping("/testModelOrMap")
public String testModelOrMap(/*Map<String,Object> map*/
/*ModelMap modelMap*/
Model model){
//设置数据
// map.put("username","lisi");
//ModelMap
// modelMap.addAttribute("username","lisi_mp");
//Model
model.addAttribute("username","lisi_model");
return "res_success";
}
5.5 源码解析SpringMVC工作原理【1】
-
DispatcherServlet底层
-
DispatcherServlet的1061行代码【触发Controller中相应方法】
ModelAndView mv = null; //1061行代码【触发Controller中相应方法】 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- 1061行代码【触发Controller中相应方法】
- 验证【无论返回String还是ModelAndView或其他,最终底层都会封装为ModelAndView对象】
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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //1061行代码 Actually invoke the handler. 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); } 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); } } } }
-
-
断点
第六章 SpringMVC处理请求与响应乱码
6.1 发现问题
- SpringMVC默认
- 请求参数是乱码
- 响应数据不乱码
6.2 解决请求乱码
遇到问题:解决思路如下
- 第一方【JDK】
- 第三方【jar包】
- 自己
-
使用SpringMVC提供CharacterEncodingFilter解决请求乱码
@Nullable private String encoding; private boolean forceRequestEncoding = false; @Nullable public String getEncoding() { return this.encoding; } /** * Return whether the encoding should be forced on requests. * @since 4.3 */ public boolean isForceRequestEncoding() { return this.forceRequestEncoding; } @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String encoding = getEncoding(); if (encoding != null) { if (isForceRequestEncoding() || request.getCharacterEncoding() == null) { request.setCharacterEncoding(encoding); } if (isForceResponseEncoding()) { response.setCharacterEncoding(encoding); } } filterChain.doFilter(request, response); }
-
使用CharacterEncodingFilter步骤
- 注册CharacterEncodingFilter
- URL:配置/*
- 注意:注册在第一过滤器的位置
- 设置初始化参数:encoding=UTF-8
- 设置初始化参数:forceRequestEncoding=true
- 注册CharacterEncodingFilter
-
示例代码
<!-- 解决请求乱码--> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
第七章 View【视图】与ViewResolver【视图解析器】
7.1 ViewResolver对象
-
作用:将View从ViewResolver中解析出来
-
ViewResolver常用实现类
- ThymeleafViewResolver
- 解析出ThymeleafView对象&RedirectView对象
- ThymeleafViewResolver
-
源码解析
7.2 View对象
JavaWeb中响应数据
- 将数据共享到域中,使用Thymeleaf渲染
- 使用响应流,直接响应数据
- 作用:渲染模型数据,将模型里的数据以某种形式呈现给客户。
- 将数据共享到域中
- 消息转换器作用:使用响应流,直接响应数据【response.getWriter().write("")】
- View常用实现类
- ThymeleafView对象
- 默认存储request域
- RedirectView【重定向视图对象】
- 需要将数据共享session域
- ThymeleafView对象
- 扩展【实战中有两种数据渲染方式】
- 在后的渲染,类似Thymeleaf
- 前端渲染,类似vue
7.3 源码解析View及ViewResolver
-
ViewResolver
-
View
- ThymeleafView中,默认处理响应乱码
- Thymeleaf中,默认将数据共享到request域
第八章 SpringMVC中三个常用实战功能
8.1 视图控制器【mvc:view-controller】
-
语法,mvc:view-controller
-
作用:将指定URL映射到指定页面
-
注意:使用视图控制器【ViewController】会导致其他Controller无法正常使用,必须添加如下标签
<mvc:annotation-driven>
-
示例代码
<?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: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"> <!-- 装配视图控制器--> <mvc:view-controller path="/" view-name="index"></mvc:view-controller> <!-- 解决使用视图控制器后续问题 --> <mvc:annotation-driven></mvc:annotation-driven> </beans>
8.2 SpringMVC中重定向
-
语法:return "redirect:/xxx.html";
- 【redirect:】是重定向语法要求
- 【redirect:/】使用上下文路径(绝对路径)
-
注意:
-
重定向使用的RedirectView对象,改对象由ThymeleafViewResolver解析
-
重定向视图对象源码
- 【redirect:/】使用上下文路径(绝对路径)【原理】
-
8.3 SpringMVC中静态资源加载问题
-
SpringMVC中默认无法加载静态资源【html、css、js、image等】
-
无法加载静态资源【原因】:
-
在Tomcat服务器下,本质上使用DefaultServlet【Tomcat服务器提供的】加载静态资源
-
使用SpringMVC后,导致DefaultServlet失效,所以无法加载静态资源
-
原理:前端控制器【DispatcherServlet】的URL覆盖了DefaultServlet中的URL
- DefaultServlet在tomcat中配置【conf->web.xml中】
-
-
-
解决方案
-
装配default-servlet-handler,解决上述问题
-
语法:<mvc:default-servlet-handler>
-
原理:DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
-
注意:如配置<mvc:default-servlet-handler>标签,也会导致Controller无法使用,需要添加如下标签解决
<mvc:annotation-driven></mvc:annotation-driven>
-
-
示例代码
<!-- 解决静态资源加载问题--> <mvc:default-servlet-handler></mvc:default-servlet-handler> <!-- 1. 解决view-controller的后续问题 2. 解决default-servlet-handler的后续问题 --> <mvc:annotation-driven></mvc:annotation-driven>
第九章 REST风格练习【无持久化层CRUD】
9.1 实现【添加员工信息】功能步骤
-
先跳转到添加页面
-
URL:/toAddPage
-
请求方式:GET
-
-
实现添加功能
-
URL:/emps
-
请求方式:POST
-
添加成功之后重定向到查询所有员工的URL
-
显示效果
-
9.2 实现【修改员工信息】功能
-
跳转修改员工信息页面
-
URL:/toUpdatePage/{id}
-
请求方式:GET
-
根据员工的id从数据库中查询员工信息
-
员工id不能修改,设置为只读
-
显示效果
-
-
实现修改员工信息功能
-
URL:/emps
-
请求方式:PUT
-
更新成功之后同样重定向到查询所有员工的URL
-
显示效果:对应的员工信息被更新
-
9.3 实现【删除员工信息】功能
实战项目研发中,删除有两种
- 物理删除【真实删除数据库中数据】
- 逻辑删除【修改某个字段,从而达到不显示的效果(推挤使用)】
-
方式一
-
URL:/emps/{id}
-
请求方式:DELETE
-
点击删除按钮弹出提示框
-
删除成功之后同样重定向到查询所有员工的URL
-
显示效果:对应的员工信息从数据库中删除
-
-
方式二【不用按钮样式】
- 单击超链接提交请求
- 将超链接中href属性值,赋值给表单的action属性
- 取消超链接请求
- 提交DELETE方式表单
第十章 SpringMVC消息转换器
10.1 消息转换器概述
- 概念:HttpMessageConverter
是 Spring3.0 新添加的一个接口 - 作用:负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息。
10.2 使用消息转换器处理请求数据【请求报文】
-
@RequestBody注解
-
位置:参数前
-
作用:获取请求体
-
注意:如请求中未携带请求体,会报错400【required=true】
- GET请求方式没有请求体
- POST请求方式,未携带参数,也没有请求体
-
示例代码
<form th:action="@{/testMcRequestBody}" method="post"> <input name="username" value="zs"><br> <input type="submit" value="提交"> </form>
@RequestMapping("/testMcRequestBody") public String testMcRequestBody(@RequestBody String requestBody){ System.out.println("requestBody = " + requestBody); return SUCCESS; }
-
-
HttpEntity对象
-
作用:HttpEntity对象获取请头&请求体
-
示例代码
<form th:action="@{/testHttpEntity}" method="post"> <input name="username" value="zs"><br> <input type="submit" value="提交"> </form>
/** * 测试HTTPEntity对象 */ @RequestMapping("/testHttpEntity") public String testHttpEntity(HttpEntity<String> he){ //获取请求头 HttpHeaders headers = he.getHeaders(); System.out.println("headers = " + headers); //获取请求体 String body = he.getBody(); System.out.println("body = " + body); return SUCCESS; }
-
10.3 使用消息转换器处理响应数据【响应报文】【重点】
-
@ResponseBody注解
-
位置:方法或类上
-
作用:
-
使用响应流响应数据【类似response.getWriter().write()】
<a th:href="@{/testResponseBody}">测试响应数据【@ResponseBody】</a>
/** * 测试testResponseBody */ @ResponseBody @RequestMapping("/testResponseBody") public String testResponseBody(){ System.out.println("==>testResponseBody!!!"); return "responseBody success!!!"; //逻辑视图名 }
-
响应Json格式数据【非常重要】
- 准备工作
- 导入jar包
<!-- jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.3</version> </dependency>
- 装配<mvc:annotation-driven>
-
示例代码
<a th:href="@{/testResponseBodyJson}">处理Json格式数据【@ResponseBody】</a>
@ResponseBody @RequestMapping("/testResponseBodyJson") public List<Employee> testResponseBodyJson(){ List<Employee> empList = new ArrayList<>(); empList.add(new Employee(101,"zs","[email protected]")); empList.add(new Employee(102,"zs2","[email protected]")); empList.add(new Employee(103,"zs3","[email protected]")); return empList; }
-
-
-
ResponseEntity实现文件下载
第十一章 文件上传与文件下载
11.1 文件上传与文件下载底层IO逻辑
- 文件上传:读本地文件,写到服务器
- 文件下载:读服务器文件,写到本地
11.2 文件下载
-
实现【文件下载】步骤
- 准备下载资源
- 位置:【/WEB-INF/download/】
- 名称:download
- 使用ResponseEntity
对象作为方法返回值,实现文件下载 - bytes[]:设置当前下载文件的所有字节
- headers:设置响应头参数【插件&字符集问题】
- statusCode:设置响应状态码【200:响应成功】
- 准备下载资源
-
示例代码
package com.atguigu.controller; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import javax.servlet.http.HttpServletRequest; import java.io.FileInputStream; import java.io.InputStream; /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/5/21 16:30 */ @Controller public class FileDownController { @RequestMapping("/doFileDownController") public ResponseEntity<byte[]> doFileDownController(String fileName, HttpServletRequest request) throws Exception{ //获取【目标文件】真实路径 String realPath = request.getServletContext().getRealPath("/WEB-INF/download/" + fileName); // - bytes[]:设置当前下载文件的所有字节 InputStream is = new FileInputStream(realPath); byte[] bytes = new byte[is.available()]; is.read(bytes); // - headers:设置响应头参数【插件&字符集问题】 HttpHeaders headers = new HttpHeaders(); //设置要下载的文件为附件格式【下载不打开】 headers.add("Content-Disposition", "attachment;filename="+fileName); //处理中文文件名问题 headers.setContentDispositionFormData("attachment", new String(fileName.getBytes("utf-8"), "ISO-8859-1")); // - statusCode:设置响应状态码【200:响应成功】 ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,HttpStatus.OK); return responseEntity; } }
11.3 文件上传
-
实现【文件上传功能】思路
- 导入jar包
- 装配CommonsMultipartResolver
- id必须是multipartResolver
- 表单
- 表单提交方式必须是POST
- 设置表单enctype="multipart/form-data"
- 表单中必须包含文件域【type="file"】
- Controller中
- 将文件域自动装配MultipartFile类型
- 调用transferTo()方法实现文件上传
-
示例代码
- 导包
<!-- commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
- 装配CommonsMultipartResolver
<!-- 装配CommonsMultipartResolve实现文件上传 id必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"></property> </bean>
- 表单
<div align="center"> <h2>文件上传</h2> <form th:action="@{/doUploadFile}" method="post" enctype="multipart/form-data"> 用户名称:<input name="username" type="text"><br> 上传文件:<input type="file" name="uploadFile"><br> <input type="submit" value="文件上传"> </form> </div>
- Controller
/** * 处理文件上传 * @param username * @param uploadFile * @return */ @PostMapping("/doUploadFile") public String doUploadFile(String username, MultipartFile uploadFile, HttpSession session){ try { //获取上传文件路径 String realPath = session.getServletContext().getRealPath("/WEB-INF/uploaddir/"); //如上传文件目录不存在【新建目录】 //获取上传文件名称 String filename = uploadFile.getOriginalFilename(); //File.separator:获取当前系统目录分隔符 File uFile = new File(realPath+File.separator+filename); //实现文件上传 uploadFile.transferTo(uFile); } catch (IOException e) { e.printStackTrace(); } return "success"; }
11.4 优化文件上传
-
上传目录不存在
-
解决方案:新建目录
File rpFile = new File(realPath); if(rpFile.exists()==false){ rpFile.mkdirs(); }
-
-
上传文件时【允许上传同名文件】
-
解决方案:使用UUID处理文件名
- UUID:是一个32位16进制随机数
- 特点:全球唯一【当前项目中唯一】
String uuid = UUID.randomUUID().toString().replace("-", ""); //获取上传文件名称 String filename = uploadFile.getOriginalFilename(); //File.separator:获取当前系统目录分隔符 File uFile = new File(realPath+File.separator+uuid+filename);
-
-
上传文件时【设置文件上传大小上限】
- 目的:降低服务器压力
<!-- 装配CommonsMultipartResolve实现文件上传 id必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--设置字符集--> <property name="defaultEncoding" value="UTF-8"></property> <!--设置上传文件大小上限:100kb 1b [byte] 1024b = 1kb 1024kb = 1mb 1024mb = 1gb 1024gb = 1tb pb zb --> <property name="maxUploadSizePerFile" value="102400"></property> </bean>
-
注意:如上传文件大小,超出设置大小上限,会报如下错
org.apache.commons.fileupload.FileUploadBase$FileSizeLimitExceededException: The field uploadFile exceeds its maximum permitted size of 102400 bytes.
第十二章 SpringMVC拦截器【重点】
12.1 拦截器与过滤器区别
-
过滤器
- 过滤器所属于:服务器端组件【web组件】
- 过滤器作用:过滤Servlet
- 过滤器执行时机
- 在Servlet执行之前
- 在Servlet执行之后
-
拦截器
- 拦截器所属于:框架【SpringMVC】,主流框架,都有拦截器
- 拦截器作用:拦截Controller
- 拦截器执行时机
- 在Controller执行之前
- 在Controller执行之后
- 在最后执行【视图渲染之后】
12.2 拦截器【Interceptor】概述
-
Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能
-
实现拦截器两种方式
- 实现接口:HandlerInterceptor
- 继承适配器类:HandlerInterceptorAdapter
-
HandlerInterceptor接口中的三个方法
-
preHandle():这个方法在业务处理器处理请求之前被调用,可以在此方法中做一些权限的校验。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
-
postHandle():这个方法在业务处理器处理请求之后,渲染视图之前调用。在此方法中可以对ModelAndView中的模型和视图进行处理。
-
afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
-
12.3 拦截器应用【Helloworld】
-
新建Controller
-
定义拦截器
- 实现HandlerInterceptor接口
- 重写三个方法
package com.atguigu.interceptor; import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Chunsheng Zhang 尚硅谷 * @create 2022/5/23 11:23 */ public class MyInterceptor implements HandlerInterceptor { /** * 执行Controller之前 * @param request * @param response * @param handler * @return * @throws Exception */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("===>proHandle()!!!"); return true; } /** * 执行Controller之后 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { System.out.println("===>postHandle()!!!"); } /** * 执行DispatcherServlet之后【视图渲染】 * @param request * @param response * @param handler * @param ex * @throws Exception */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { System.out.println("===>afterCompletion()!!!"); } }
-
配置拦截器
<!-- 装配拦截器--> <!-- 局部配置:为指定局部URL装配当前拦截器--> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/TestInterceptorController"/> <ref bean="myInterceptor"></ref> </mvc:interceptor> </mvc:interceptors> <!-- 全局配置:为所有Controller装配当前拦截器--> <!-- <mvc:interceptors>--> <!-- <bean class="com.atguigu.interceptor.MyInterceptor"></bean>--> <!-- <ref bean="myInterceptor"></ref>--> <!-- </mvc:interceptors>-->
-
测试
12.4 拦截器工作原理
-
单个拦截器工作原理
- 浏览器向服务器发送请求
- 执行拦截器第一个方法【preHandle()】
- 执行Controller处理请求,做出响应
- 执行拦截器第二个方法【postHandle()】
- 执行DispatcherServlet【渲染视图】
- 执行拦截器第三个方法【afterCompletion()】
- 响应【文本|页面】
-
多个拦截器工作原理【重点】
拦截器顺序由装配拦截器顺序决定
- 浏览器向服务器发送请求
- 执行拦截器1第一个方法【preHandle()】
- 执行拦截器2第一个方法【preHandle()】
- 执行Controller处理请求,做出响应
- 执行拦截器2第二个方法【postHandle()】
- 执行拦截器1第二个方法【postHandle()】
- 执行DispatcherServlet【渲染视图】
- 执行拦截器2第三个方法【afterCompletion()】
- 执行拦截器1第三个方法【afterCompletion()】
- 响应【文本|页面】
-
preHandle()方法返回值问题
- 当第一个拦截器的preHandle()方法返回false时
- 效果:只执行第一个拦截器的preHandle()方法,程序结束
- 当非第一个拦截器的preHandle()方法返回false时
- 效果:执行【当前及之前拦截器】的preHandle()方法,再执行【之前拦截器】的afterCompletion()方法,程序结束
- 当第一个拦截器的preHandle()方法返回false时
第十三章 SpringMVC中异常处理器
13.1 回顾异常处理
- 为什么处理异常?
- 程序中一般可能出现异常位置,都需要处理【因为不处理出现异常时,程序会终止 】
- 如何处理
- JavaSE
- try-chatch-finally
- throw|throws
- JavaWeb
- 自定义Filter过滤器,处理异常
- JavaSE
13.2 Spring中异常处理
-
Spring MVC 通过 HandlerExceptionResolver【异常处理器|异常适配器】 处理程序的异常
-
SpringMVC中常用异常处理器
- DefaultHandlerExceptionResolver
- SimpleMappingExceptionResolver
-
DefaultHandlerExceptionResolver【默认异常处理器】
- 默认已经装配
- 支持处理10+中异常
-
SimpleMappingExceptionResolver
-
默认未装配,如需使用需要手动装配
-
语法
<!-- 装配异常处理器【SimpleMappingExceptionResolver】--> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.NullPointerException">error/error_np</prop> <prop key="java.lang.ArithmeticException">error/error_am</prop> </props> </property> </bean>
-
13.3 源码解析异常处理器【SimpleMappingExceptionResolver】
-
异常处理器处理异常后,最终返回ModelAndView对象
-
如Controller中存在异常,不会执行拦截器第二个方法【postHandle()】
第十四章 SpringMVC工作原理【面试题】
14.1 扩展三个对象
-
HandlerAdapter对象:请求处理器适配器对象
- 作用:调用【触发】请求处理器中的相应方法【DispatcherServlet的1061行代码】
- ha.handle()
- 作用:调用【触发】请求处理器中的相应方法【DispatcherServlet的1061行代码】
-
HandlerExecutionChain【请求处理器执行链对象】
-
作用:通过HandlerExecutionChain获取HandlerAdapter对象
-
描述
/** //官方描述 * Handler execution chain, consisting of handler object and any handler interceptors. //自己理解 请求处理器执行链对象,由【当前请求处理器】和【请求处理器对应的所有拦截器】组成 */ public class HandlerExecutionChain {}
-
-
HandlerMapping【请求处理器映射器对象】
-
作用:通过HandlerMapping对象获取HandExecutionChain对象
-
描述
/** //官方描述 * Interface to be implemented by objects that define a mapping between * requests and handler objects. //自己理解 HandlerMapping是一个接口,当前接口定义一个映射关系【所有请求与所有请求处理器对象】 public interface HandlerMapping {}
-
14.2 SpringMVC工作原理1【URL不存在】
-
浏览器端向服务器端发送请求
-
通过DispatcherServlet处理URL
-
判断URL存在
-
判断URL不存在
-
判断是否配置标签【mvc:default-servlet-handler】
-
配置:出现404,显示请求资源不可用【资源:...】
-
未配置:出现404,不显示其他
-
-
-
14.3 SpringMVC工作原理2【URL存在】
-
浏览器向服务器发送请求
-
通过DispatcherServlet判断URL是否存在【存在】
-
创建HandlerMapping对象,通过HandlerMapping对象获取HandlerExecutionChain,再通过HandlerExecutionChain对象获取HandlerAdapter对象
-
通过HandlerExecutionChain【请求处理器执行链对象】对象调用拦截器中第一个方法【preHandle()】
-
通过HandlerAdapter【请求处理器适配器】对象调用Controller中的相应方法【处理请求,做出响应】
-
判断Controller中是否存在异常
-
存在异常,执行异常处理器【ExceptionResolver】
- 异常处理器处理异常之后,返回ModelAndView【不会执行拦截器第二个方法postHandle()】
-
不存在异常,正常执行后续操作,并返回ModelAndView对象
- 通过HandlerExecutionChain对象调用拦截器第二个方法【postHandle()】
-
-
-
通过ViewResolver【视图解析器】将View从ModelAndView中解析出来
-
通过View对象视图渲染【将数据共享到域中,跳转页面,默认解决响应乱码】
-
通过HandlerExecutionChain对象,调用拦截器第三个方法【afterCompletion()】
-
服务器响应浏览器
第十五章 SSM整合【Spring+SpringMVC+Mybatis】
15.1 SSM整合思路
- Spring+SpringMVC
- 组件扫描问题
- SpringMVC只扫描Controller层
- Spring扫描排除Controller层
- 容器对象的管理问题
- SpringMVC容器对象【子】,交给DispatcherServlet管理【启动服务器】
- Spring容器对象【父】,由ContextLoaderListener管理
- 组件扫描问题
- Spring+Mybatis
- 数据源&事务管理问题【mybatis&spring】
- 最终:将数据源及事务管理代码,统一交给Spring管理
- Mybatis的核心对象,交给Spring管理
- SqlSessionFactory对象
- Mapper代理对象
- 数据源&事务管理问题【mybatis&spring】
15.2 SSM整合步骤
-
Spring+Spring整合
-
导包
<!--spring-webmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.1</version> </dependency> <!-- 导入thymeleaf与spring5的整合包 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>
-
配置文件
- web.xml
- Filter
- CharacterEncodingFilter:解决POST请求乱码
- HiddenHttpMethodFilter:支持REST风格CRUD【PUT&DELETE提交方式】
- Servlet
- DispatcherServlet:前端控制器
- Listener
- ContextLoaderListener:管理Spring容器对象
- 上下文参数
- contextConfigLocation:设置spring.xml配置文件路径
- Filter
- springmvc.xml
- 开启组件扫描【只扫描Controller层】
- 装配视图解析器【ThymeleafViewResolver】
- 装配视图控制器【mvc:view-controller】
- 装配标签:mvc:default-servlet-handler
- 装配标签:mvc:annotation-driven
- spring.xml
- 开启组件扫描【排除Controller层】
- web.xml
-
-
Spring+Mybatis整合
-
导包
<!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!-- pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.8</version> </dependency> <!--导入druid的jar包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!--导入mysql的jar包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!--spring-orm--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.3.1</version> </dependency> <!--spring-aspects--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version> </dependency> <!--mybatis-spring--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
-
配置文件
-
mybatis核心配置文件【mybatis-config.xml】
- 可省略
- 加载外部属性文件:properties
- 设置别名:typeAliases
- 设置数据源&事务管理:environments
- 设置映射文件路径:mappers
- 不可省略
- mybatis设置标签:settings
- 设置插件【分页插件】:plugins
- 可省略
-
mybatis映射文件【xxxMapper.xml】
- 作用:定义SQL语句
- 注意:三个一致
-
spring配置文件【spring.xml】
-
开启组件扫描【排除Controller层】
-
加载外部属性文件
<!-- 开启组件扫描--> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
装配数据源【DruidDataSource】
<!-- 配置DataSource数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driverClassName}"></property> <property name="url" value="${db.url}"></property> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> </bean>
-
装配事务管理器【DataSourceTransactionManager】
<!-- 装配事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
-
开启事务管理注解支持【tx:annotaion-driven】
<!-- 开启事务注解支持--> <tx:annotation-driven></tx:annotation-driven>
-
装配SqlSessionFactoryBean对象管理SqlSessionFactory对象
- 必须装配属性
- 数据源属性
- mybatis-config.xml核心配置文件路径
- 可选装配属性
- 设置类型别名
- 设置映射文件路径
- 必须装配属性
-
装配MapperScannerConfigurer,管理代理对象
- <mybatis-spring:scan base-package="">
-
-
-