Spring+SpringMVC+Mybatis
Spring MVC 简介
DispatcherServlet前端控制器
SpringMVC 提供一个名为 org.springframework.web.servlet.DispatcherServlet 的 Servlet 充当前端控制器
<servlet>
<!-- Servlet 的名称 -->
<servlet-name>springmvc</servlet-name>
<!-- Servlet 对应的 Java 类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 当前 Servlet 参数信息 -->
<init-param>
<!-- contextConfigLocation 是参数名称,该参数的值包含 SpringMVC 的配置文件路径 -->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<!-- 在 Web 应用启动时立即加载 Servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<!--请求对应的 Servlet 的名称 -->
<servlet-name>springmvc</servlet-name>
<!-- 监听当前域的所有请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet 加载时会需要一个 SpringMVC 的配置文件,
默认情况下,应用会去 /WEB-INF/ 下查找对应的 [servlet-name]-servlet.xml 文件
SpringMVC 执行的流程
DispatcherServlet 源代码
protected void initStrategies(ApplicationContext context) {
// 初始化上传文件解析器
this.initMultipartResolver(context);
// 初始化本地化解析器
this.initLocaleResolver(context);
// 初始化主题解析器
this.initThemeResolver(context);
// 初始化处理器映射器,将请求映射到处理器
this.initHandlerMappings(context);
// 初始化处理器适配器
this.initHandlerAdapters(context);
// 初始化啊处理异常解析器,执行过程中遇到异常交给HandlerExceptionResolvers解析
this.initHandlerExceptionResolvers(context);
// 初始化请求到视图名称解析器
this.initRequestToViewNameTranslator(context);
// 初始化视图解析器,通过 ViewResolvers 解析逻辑视图名到具体视图实现
this.initViewResolvers(context);
// 初始化 flash 映射管理器
this.initFlashMapManager(context);
}
org.springframework.web.servlet 路径下有一个 DispatcherServlet .properties 配置文件
该文件指定了 DispatcherServlet 所使用的默认组件
一次 请求 → 响应的完整流程

①:用户向服务器发送请求,请求被 Spring 的前端控制器 DispatcherServlet 截获;
②:DispatcherServlet 对请求 URL(统一资源定位符)进行解析,得到URI(请求资源标识符)。然后根据该 URI 调用
HandlerMapping 获得该 Handler 配置的所有相关的对象,包括 Handler 对象以及 Handler 对象对应的拦截器,这些对象会被封装到一个 HandlerExecutionChain 对象当中返回。
③:DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。HandlerAdapter 的设计符合面向对象中的单一职责原则,HandlerAdapter 会被用于处理多种 Handler,调用 Handler 实际处理请求的方法,
④:提取请求中的数据模型,开始执行 Handler(Controller),在填充 Handler 的入参过程中,根据配置,Spring 将帮你做一些额外的工作。
👉 消息转换 :将请求信息(Json、XML 等数据),转换成一个对象,将对象转换为指定的响应信息。
👉 数据转换 :对请求信息进行数据转换,如 String 转换成 Integer、Double等。
👉 数据格式化 :对请求信息进行数据格式化,如将字符串转换成格式化数据或格式化日期等。
👉 shuju验证(校验) :验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Errors 中。
⑤ :Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象,ModelAndView 对象中应该包含视图名或视图名和模型。
⑥ :根据返回的 ModelAndView 对象,选择一个合适的 ViewResolver(视图解析器)返回给 DispatcherServlet 。
⑦ :ViewResolver 结合 Model 和 View 来渲染视图
⑧ :将视图渲染结果返回给客户端
基于 实现 Controller 接口
第一个 Spring MVC 应用
1、导包,可以把 spring-framework lib 包里面 所有 .jar 文件拷贝到 项目lib文件中

2、配置前端控制器 DispatcherServlet 【web.xml】
<?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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3、配置 Spring MVC 的 Controller
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 配置 Handle,映射 “/hello” 请求-->
<bean name="/hello" class="com.yuanwu.controller.HelloWorldController"/>
<!-- 处理映射器将 bean 的 name 作为url进行查找,需要在配置 Handle 时指定name(即url)-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- SimpleControllerHandlerAdapter 是一个处理器适配器,所有处理器适配器都要实现HandleAdapter接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
4、Controller 的实现
实现 Controller 接口
package com.yuanwu.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 YuanWu
* @ClassName HelloWorldController
* @Date 2020/11/9
**/
// @Controller
public class HelloWorldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
//封装要显示到视图中的数据
mv.addObject("message","Hello World");
//视图名
mv.setViewName("index.jsp");
return mv;
}
}
5、 View 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${requestScope.message}
</body>
</html>
6、测试
基于 注解的 Controller
1、Controller
@Controller
public class HelloWorldController{
@RequestMapping(value = "/hello")
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("message","Hello World");
mv.setViewName("index.jsp");
return mv;
}
}
2、web.xml
<?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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3、spring-config.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
4、测试
结果跳转的方式
1、设置 ModelAndView 对象,根据 View 的名称,和视图解析器 跳转到指定的页面。
页面:视图解析器的前缀 + ViewName + 视图解析器的后缀
//视图名
mv.setViewName("hello"); //前缀+hello+后缀
2、通过 ServletAPI 对象 来实现。不需要视图解析器
response.getWriter().println("。。。。。");
3、转发和重定向
4、@ResponseBody 响应 json 数据
5、无返回值
6、String 返回值
SpringMVC常用注解
常用注解
| 注解 | 使用 |
|---|---|
@Controller |
用于标记一个类,标记的类就是一个SpringMVC Controller对象,即一个控制类,Spring会扫描这个类 |
@RequestMapping |
告诉Spring用哪一个类或方法来处理请求动作,可用于类或方法 |
@RequestParam |
用于将指定的请求参数赋值给方法中的形参 接收 key-value ,GET方式,用 @RequestParam 接收值 ,在方法中使用 |
@PathVariable |
用于获取请求URL中的动态参数 |
@RequestHeader |
用于将请求的头信息区数据映射到功能处理方法的参数上 |
@CookieValue |
用于将请求的 Cookie 数据映射到功能处理方法的参数上 |
@SessionAttributes |
允许我们有选择得指定 Model 中的哪些属性需要转存到 HttpSession 对象中(只能声明在类中) |
@ModelAttribute |
将请求参数绑定到 Model 对象 |
@ResponseBody |
将 Java 对象转为 JSON 格式的数据。用来返回 JSON 数据或者 XML 数据。作用在方法上,一般在异步获取数据使用,效果等同于通过response输出数据,不会再走视图解析器,直接将数据写入输入流中。 |
@RequestBody |
主要用来接收前端传递给后端的 json 字符串中的数据(请求体中的数据);GET无请求体,前端用POST进行提交, |
@Valid |
用于对提交的数据进行校验 |
信息转换
HttpMessageConverter<T>接口
| 接口 | 作用 |
|---|---|
| HttpMessageConverter<T> | 负责将请求信息转换为一个对象(类型为T),并将对象(类型为T)绑定到请求方法的参数中或输出为响应信息。 |
转换 JSON 数据
SpringMVC 提供了处理 JSON 格式请求/响应的 HttpMessageConverter
SpringMVC 默认使用 jackson 处理 json数据 MappingJackson2HttpMessageConverter
使用 Controller 返回 JSON 数据 【原生】
1、导包,springmvc所需包
2、web.xml
<?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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符乱码过滤器-->
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
</web-app>
3、spring-config.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
4、controller
package com.yuanwu.controller;
import com.yuanwu.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Author YuanWu
* @ClassName UserController
* @Date 2020/11/10
**/
@Controller
public class UserController {
@RequestMapping("/json")
// 使用 @ResponseBody 注解,返回的数据不会走视图解析器,而是直接返回一个字符串给前端
@ResponseBody
public String json() {
User user = new User(1,"李四",18);
return user.toString();
}
}
4、测试
返回
User{Id=1, name='??', age=18}
使用 jackson 开源类包 返回JSON 格式
导包 jackson-annotations-2.10.0.jar jackson-core-2.10.0.jar jackson-databind-2.10.0.jar
1、controller
@Controller
public class UserController {
//使用 Controller 返回JSON 格式
@RequestMapping("/json")
// 使用 @ResponseBody 注解,返回的数据不会走视图解析器,而是直接返回一个字符串给前端
@ResponseBody
public String json() {
User user = new User(1,"李四",18);
return user.toString();
}
//使用 jackson 返回JSON 格式
@RequestMapping(value = "/jackJson",produces = "text/plain;charset=UTF-8")
@ResponseBody
public String jackJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User(1,"李四",18);
String str = mapper.writeValueAsString(user);
return str;
}
//通过 JSON格式返回 集合数据
@RequestMapping(value = "/arrayJson")
@ResponseBody
public String arrayJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User(2,"李四1",18);
User user1 = new User(1,"李四2",18);
User user2 = new User(3,"李四3",18);
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(user1);
userList.add(user2);
String string = mapper.writeValueAsString(userList);
return string;
}
}
2、spring-config.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: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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>
3、web.xml
<?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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符乱码过滤器-->
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
</web-app>
4、测试
通过Controller返回
http://localhost:8080/json 页面返回:User
通过 jackson 返回 JSON 数据
通过jackson 返回 集合 的 JSON 数据(会有乱码,在@RequestMapping 注解中 加入 produces = "text/plain;charset=UTF-8" ) 后面会涉及到 如何一劳永逸的 解决乱码问题
http://localhost:8080/arrayJson
[{"name":"??1","age":18,"id":2},{"name":"??2","age":18,"id":1},{"name":"??3","age":18,"id":3}]
jackson使用小结:
1、在对应 controller 类中 的方法上添加 注解 @ResponseBody ,
2、在对应的 controller 类中的方法中 new 一个 ObjectMapping 对象 mapper
3、调用 mapper.writeValueAsString(对象的引用),将对象转为一个字符串
4、将字符串 返回
响应 JSON 数据,发送 ajax请求
1、controller
@Controller
public class UserController {
/*
* 模拟异步请求和响应
* */
@RequestMapping(value = "/hello")
public void testAjax(@RequestBody String body) {
System.out.println("执行testAjax()方法");
System.out.println(body);
}
}
2、index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
//页面加载,绑定单击事件
$(function () {
$("#btn").click(function () {
//发送 ajax 请求
$.ajax({
url:"/hello",
contentType:"application/json;charset=UTF-8",
data:'{"name":"张三","password":"123","age":36}',
dataType:"json",
type:"post",
success:function (data) {
//data 服务器端响应的 json 的数据,进行解析
alert("成功");
}
});
});
});
</script>
</head>
<body>
<button id="btn">发送 ajax 请求 </button>
</body>
</html>
3、springmvc-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: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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
<!-- 前端控制器,哪些静态资源不拦截
mapping:映射请求
location:类路径文件
-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:annotation-driven/>
</beans>
jackson 把 字符串转成 对象,把对象转成json的字符串
响应 json 数据,响应json格式数据
发送 ajax 请求,传递 json 数据,UserController 收到请求后,@RequestBody 会将 json 数据设置到 Javabean 参数对应的属性当中;
用@ResponseBody 注解,后端把 json 数据封装到 Javabean对象中,响应给客户端;
客户端发送ajax请求,传的是json字符串,后端把json字符串封装到 user 对象中,
1、springmvc-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: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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<mvc:default-servlet-handler/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:annotation-driven/>
</beans>
2、controller
@RequestMapping(value = "/hello")
@ResponseBody
public User testAjax(@RequestBody User user) {
System.out.println("执行testAjax()方法");
// 客户端发送ajax请求,传的是json字符串,后端把json字符串封装到 user 对象中,
System.out.println(user);
//响应,模拟查询数据库
user.setName("元五");
user.setAge(18);
return user;
}
测试:
将 set进去的数据,响应出去,并且在控制台打印成功

自定义 HttpMessageConverter 接收 JSON格式的数据
使用 fastjson
1、导包,
fastson-xxx.jar
需要注意的是,使用fastson时,需要移除 Jackson的包,否则还是会默认加载 jackson
2、修改 json 方式为 fastjson
将转换器 配置成 com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter
3、springmvc-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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/shema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Spring 可以自动区扫描 base-pack 下面的包或者子包下面的 java 文件-->
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven>
<!-- 设置不使用默认的消息转换器 -->
<mvc:message-converters register-defaults="false">
<!-- 配置 Spring 的转换器 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<!-- 配置 fastjson 中 实现 HttpMessageConverter 接口的转换器 -->
<!-- FastJsonHttpMessageConverter 是 fastjson 中实现了 HttpMessageConverter接口的类 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 加入支持的媒体类型,返回 contentType -->
<property name="supportedMediaTypes">
<list>
<!-- 顺序不能反,一定要先写 text/html,不然 IE 会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
4、controller
@Controller
public class UserController {
@RequestMapping(value = "/getJson")
@ResponseBody
public Object getJson() {
List<User> userList = new ArrayList<>();
userList.add(new User("张三","123",18));
userList.add(new User("李四","123",28));
userList.add(new User("王五","123",38));
userList.add(new User("赵六","123",48));
return userList;
}
}
5、web.xml
<?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>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6、index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
$.ajax({
url: "/getJson",
contentType: "application/json;charset=UTF-8",
dataType: 'json',
type: "post",
success: function (data) {
console.log("成功");
console.log(data);
console.log(data[0].age);
console.log(data[1].name);
}
});
});
});
</script>
</head>
<body>
<button id="btn">发送 ajax 请求</button>
</body>
</html>
7、测试
启动服务器,点击页面 发送ajax请求 chrome控制台输出
==>>> 
使用Ajax 异步渲染html
导包: fastjson-1.2.73.jar、以及spring依赖包
如果不是大范围的刷新页面的话,建议使用 Ajax 异步请求,
步骤1、bean
public class User {
private String name;
private int age;
private String sex;
.....省略 set get
}
步骤2、controller AjaxController.java
@RestController
public class AjaxController {
@RequestMapping(value = "/emps")
public List<User> ajax() {
List<User> userList = new ArrayList<User>();
userList.add(new User("java",1,"男"));
userList.add(new User("java",1,"男"));
userList.add(new User("java",1,"男"));
userList.add(new User("java",1,"男"));
return userList;
}
}
步骤3、jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
console.log("111");
$.ajax({
url: "/emps",
contentType: "application/json;charset=UTF-8",
dataType: 'json',
type: "post",
success: function (data) {
console.log("成功");
var html = "";
for (let i = 0; i < data.length; i++) {
html += "<tr>" +
"<td>"+ data[i].name +"</td>" +
"<td>"+ data[i].age +"</td>" +
"<td>"+ data[i].sex +"</td>" +
"</tr>"
}
$("#content").html(html);
}
})
});
});
</script>
</head>
<button id="btn" type="button">查询</button>
<body>
<table>
<tr>
<td>性名</td>
<td>年龄</td>
<td>性别</td>
</tr>
<tbody id="content">
<%-- 数据:后台 --%>
</tbody>
</table>
</body>
</html>
步骤4、xml配置文件 spring-config.xml
<!-- Spring 可以自动区扫描 base-pack 下面的包或者子包下面的 java 文件-->
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven>
<!-- 设置不使用默认的消息转换器 -->
<mvc:message-converters register-defaults="false">
<!-- 配置 Spring 的转换器 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<!-- 配置 fastjson 中 实现 HttpMessageConverter 接口的转换器 -->
<!-- FastJsonHttpMessageConverter 是 fastjson 中实现了 HttpMessageConverter接口的类 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 加入支持的媒体类型,返回 contentType -->
<property name="supportedMediaTypes">
<list>
<!-- 顺序不能反,一定要先写 text/html,不然 IE 会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
步骤5、测试 http://localhost:8080/ 点击查询

补充
请求处理可出现、可返回的参数类型
| 参数类型 | 作用 |
|---|---|
Model |
SpringMVC在调用处理方法之前会创建一个隐含的模型对象,作为模型数据的存储容器,如果处理方法的参数为Model或ModelMap类型,则SpringMVC会将隐含模型的引用传递给这些参数,可以通过参数对象访问模型中的所有数据。 |
ModelMap |
跟Model 用法基本一致, |
ModelAndView |
既包含数据模型信息,也包含视图信息,SpringMVC将使用包含的视图对数据模型进行渲染。 |
ModelAndView 方法名称 |
作用 |
| addObject (String s,Object s) | 添加数据模型 |
| setViewName(String s) | 设置视图 |
| 方法名称 | 用法 |
|---|---|
| setAttribute(key,value) | 将数据保存在 request/session 作用域中 |
| getAttribute(key) | 将保存在 request/session 作用域中的数据取出 |
| request.getSession() | 获取 HttpSession 类型的对象,session对象 |
| request.getServletPath() | 获取请求路径 |
| java.lang.String.contains() | 当前字符串是否包含指定的子字符串 |
重定向和转发
| 区别 | 转发 | 重定向 |
|---|---|---|
| request.getRequestDispatcher("").forward() | response.sendRedirect("") | |
| 地址栏 | 不会显示出转向的地址 | 会显示转向之后的地址 |
| 请求 | 至少提交了两次请求 | |
| 数据 | 对 request 对象的信息不会丢失,可以在多个页面交互实现请求数据的共享 | request 信息将丢失 |
| 原理 | 是在服务器内部控制器的转移,由服务器区请求 | 服务器告诉客户端要转向哪个地址,客户端再自己去请求转向的地址 |
| 选择 | 增删改需要跳转到其他页面,所以用重定向 | 而查询新,一般只在当前页面,常用转发方式 |
SpringMVC 标签库
| 标签 | 描述 |
|---|---|
| form | 渲染表单元素 |
| input | 渲染元素 |
| password | 渲染元素 |
| hidden | 渲染元素 |
| textarea | 渲染 textarea 元素 |
| checkbox | 渲染一个元素 |
| checkboxs | 渲染多个元素 |
| radiobutton | 渲染一个元素 |
| radiobuttons | 渲染多个元素 |
| select | 渲染一个选择元素 |
| option | 渲染一个可选元素 |
| options | 渲染一个可选元素列表 |
| errors | 在 span 元素中渲染字段错误 |
语法:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form method="post" action="/login">
<form:input path="name"/>
.................
</form:form>
使用 SpringMVC 的 form 标签主要有两个作用,第一个是它会自动的绑定来自 Model 中的属性值到当前 from 对应的实体对象,第二个是在提交表单的时候不仅仅能使用 GET POST提交,包括 DELETE PUT 等
默认从 request 域对象中读取 command 的表单bean,如果不存在,且没有 modelAttribute 属性,则会报错
1、使用
modelAttribute属性指定绑定的模型属性
<form:form method="post" action="/login" modelAttribute="user">
2、手动将user对象添加到 request 请求域中
public String login() {
User u = new User();
model.addAttribute("u",u);
return "index";
}
3、使用 @ModelAttribute
@ModelAttribute
public void login(@RequestParam("id") String id, Model model) {
User u = new User();
model.addAttribute("u",u);
}
更详细的 SpringMVC 标签库的使用 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
乱码 及 Restful
web.xml
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
restful
原有的url:
参数具体锁定到操作的是谁。id=3 的用户
rest 风格:
http://localhost:8080/user/queryUserById/3
@RequestMappin(value="/delete/{id}") 删除传进来的id 的用户记录
代表的是 id 为 3 的用户
GET 查询 获取资源
POST 增加 新建资源
PUT 更新
DELETE 删除资源
Spring MVC ⽀持 RESTful ⻛格请求,具体讲的就是使⽤ @PathVariable 注解获取 RESTful ⻛格的请求
Spring MVC 的数据转换、格式化和数据校验
数据转换
SpringMVC 中接收到的数据都是 String 形式,然后根据反射机制将 String 转换成对应的类型
比如 SpringMVC 接收到的是 String 类型,我们想要得到 Date类型,就可以定义一个转换器完成
示例1: 使用ConversionService 转换数据
导包,jstl标签jar包 standard-1.1.2.jar, , jstl-1.2.jar ,可以自行去spring官网下载 spirng相关源码 源码,包含jar包
省略了 bean ,User.java
步骤1、web.xml
<!--字符乱码过滤器-->
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
步骤2、spring-config.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 自定义类型转换器
覆盖默认实现类
-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.yuanwu.controller.UserController"
<!-- 给属性 datePattern 复制为 日期格式类 -->
p:datePattern="yyyy-MM-dd"></bean>
</list>
</property>
</bean>
<!--视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
步骤3、controller,UserController.java 实现 Converter 接口
开发自定义的转换器,将传递的字符串转换成 Date 类型。
import org.springframework.core.convert.converter.Converter;
import java.text.SimpleDateFormat;
import java.util.Date;
// 实现 Converter<S,T>接口
public class UserController implements Converter<String, Date> {
// 日期类型模板:如yyyy-MM-dd,通过配置时注入
private String datePattern;
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}
// Converter<S,T>接口的类型转换方法
@Override
public Date convert(String date) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(this.datePattern);
// 将日期字符串转换成Date类型返回
return dateFormat.parse(date);
} catch (Exception e) {
e.printStackTrace();
System.out.println("日期转换失败!");
return null;
}
}
步骤4、UserDatePattern.java
@Controller
public class UserDatePattern {
@RequestMapping(value = "/{formName}")
public String loginForm(@PathVariable String formName) {
return formName;
}
@RequestMapping(value = "/register",method = RequestMethod.POST)
public String register(@ModelAttribute User user, Model model) {
model.addAttribute("user", user);
return "success";
}
}
步骤5、jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3>注册页面</h3>
<form action="/register" method="post">
<table>
<tr>
<td>登录名</td>
<td><input type="text" id="name" name="name"></td>
</tr>
<tr>
<td>生日</td>
<td><input type="text" id="birthday" name="birthday"></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录名:${requestScope.user.name} <br/>
生日: <fmt:formatDate value="${requestScope.user.birthday}"
pattern="yyyy年MM月dd日"/>
</body>
</html>
步骤6,测试 ,启动 tomcat 服务器

示例2、@InitBinder 添加自定义编辑器转换数据
示例3、 WebBindingInitializer 注册全局自定义编辑器转换数据
多种转换器的优先顺序
既使用 ConversionService 装配自定义的转换器,又使用 通过 WebBindingInitializer 接口装配定义全局自定义编辑器,同时使用注解 @InitBinder 装配自定义编辑器,SpringMVC会根据优先顺序查找对应的编辑器:
- 1)、查询通过 @InitBinder 装配的自定义编辑器;
- 2)、查询通过 ConversionService 装配的自定义编辑器;
- 3)、查询通过 WebBindingInitializer 接口装配的全局自定义编辑器;
数据格式化 Formatter<T>
Formatter 只能将 String 转换成另一种 Java 类型,例如将 String 字符串 转为 Date 类型。但是不能将 Long 转换成 Date 。如果项转换表单中的用于输入,建议使用 Formatter
使用 Formatter 格式化数据
导包,jstl标签jar包 standard-1.1.2.jar, , jstl-1.2.jar ,可以自行去spring官网下载 spirng相关源码 源码,包含jar包
web.xml 配置文件 基本是固定的,前面写过的,这里不再阐述
步骤1、index.jsp、success.jsp 为了方便,我将两张jsp代码,放在一起,用分割线隔开
<%-- -------------------------index.jsp--------------------------%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3>注册页面</h3>
<form action="/test" method="post">
<table>
<tr>
<td>登录名</td>
<td><input type="text" id="name" name="name"></td>
</tr>
<tr>
<td>生日</td>
<td><input type="text" id="birthday" name="birthday"></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
<%-- -------------------------success.jsp--------------------------%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录名:${requestScope.user.name} <br/>
生日: <fmt:formatDate value="${requestScope.user.birthday}"
pattern="yyyy年MM月dd日"/>
</body>
</html>
步骤2、controller,实现 Formatter <T> 接口 ,实现业务逻辑
为了方便,我也是将两个java代码,也在一起
//--------------------------日期格式化类 Formatter <T> -------------------
public class UserFormatter implements Formatter<Date> {
// 日期类型模板 比如 yyyy-MM-dd
private String datePattern;
// 日期格式化对象
private SimpleDateFormat simpleDateFormat;
// 构造器,通过依赖注入的日期类型 创建日期格式化对象
public UserFormatter(String datePattern) {
this.datePattern = datePattern;
this.simpleDateFormat = new SimpleDateFormat(datePattern);
}
// 解析文本字符串,返回一个 Formatter<T> 的 T 类型对象
@Override
public Date parse(String s, Locale locale) throws ParseException {
try {
return simpleDateFormat.parse(s);
} catch (Exception e) {
throw new IllegalArgumentException();
}
}
// 显示 Formatter<T> 的 T 类型对象
@Override
public String print(Date date, Locale locale) {
return simpleDateFormat.format(date);
}
}
//----------------------------业务逻辑--------------------------------------
@Controller
public class UserController {
// //动态跳转页面
// @RequestMapping(value = "/{formName}")
// public String loginName(@PathVariable String formName) {
// return formName;
// }
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String test(@ModelAttribute User user, Model model) {
model.addAttribute("user", user);
System.out.println(user.toString());
return "register";
}
}
步骤3、spring-config.xml 配置文件
<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" xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="com.yuanwu.controller.UserFormatter" c:_0="yyyy-MM-dd"/>
</list>
</property>
</bean>
<!--视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>

示例:使用 FormatterRegister 注册 Formatter
使用注解定义格式化数据
| 注解 | 用法 |
|---|---|
@DateTimeFormat |
可对 java.util.Date,java.util.Calendar 等时间属性进行格式化 |
@NumberFormat |
对 数字类型 进行格式化 |
步骤1、index.jsp success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3>测试表单数据格式化</h3>
<form action="/test" method="post">
<table>
<tr>
<td>日期类型:</td>
<td><input type="text" id="birthday" name="birthday"></td>
</tr>
<tr>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>测试表单数据格式化</h3>
<form:form modelAttribute="user" method="post" action="">
<table>
<tr>
<td>日期类型:</td>
<td><form:input path="birthday"/></td>
</tr>
</table>
</form:form>
</body>
</html>
步骤2、web.xml spring-config.xml
<!--字符乱码过滤器-->
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven/>
<!--视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
步骤3、controller
@Controller
public class UserController {
// @RequestMapping(value = "/{formName}")
// public String loginName(String formName) {
// return "formName";
// }
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String test(@ModelAttribute User user, Model model) {
model.addAttribute("user", user);
return "success";
}
}

数据校验
Spring MVC 提供了两种数据校验功能,一种是 Spring 自带的 Validation 校验框架。一种是 JSR 303 (Java验证规范)实现校验功能
Validation 校验框架
示例:JSR303校验
导包 jar包可以去 https://mvnrepository.com/ 这个网站下载,
| classmate-1.5.1.jar | hibernate-validator-5.2.4.Final.jar | log4j-api-2.6.2.jar |
|---|---|---|
| jboss-logging-3.2.1.Final.jar | log4j-1.2.17.jar | hibernate-validator-annotation-processor-5.2.4.Final.jar |
| slf4j-log4j12-1.6.1.jar | validation-api-1.1.0.Final.jar | hibernate-validator-cdi-5.2.4.Final.jar |
| slf4j-api-1.6.1.jar | spring 依赖包 |
步骤1、xml配置文件 ,spring-config.xml
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven/>
<!--视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
步骤2、controller,CserController.xml
@Controller
public class UserController {
@RequestMapping("/formName")
public String index(Model model) {
User user = new User();
model.addAttribute("user", user);
return "index";
}
@RequestMapping(value = "/login" , method = RequestMethod.POST)
public String login(@Valid @ModelAttribute User user, Errors errors, Model model) {
System.out.println(user);
System.out.println(errors);
if (errors.hasErrors()) {
return "index";
}
model.addAttribute("user", user);
return "success";
}
}
步骤3、jsp文件 ,index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form:form modelAttribute="user" method="post" action="login">
<table>
<tr>
<td>用户名:</td>
<td><form:input path="name"/></td>
<td><form:errors path="name" cssClass="color-pink"/></td>
</tr>
<tr>
<td>密码:</td>
<td><form:input path="pwd"/></td>
<td><form:errors path="pwd" cssClass="color-pink"/></td>
</tr>
<tr>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form:form>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
用户名:${requestScope.user.name} <br/>
密码:${requestScope.user.pwd}
</body>
</html>
步骤4、测试 http://localhost:8080/formName http://localhost:8080/login http://localhost:8080/login

SpringMVC 实现文件上传
MultipartFilechan常用方法
| 方法名称 | 作用 |
|---|---|
| byte[] getBytes() | 获取文件数据 |
| String getContentType() | 获取文件MIME类型,比如 image/jpeg 等 |
| InputStream getInputStream() | 获取文件流 |
| String getName() | 获取表单中文件组件的名字 |
| String getOriginalFilename() | 获取上传文件的原名 |
| long getSize() | 获取文件的字节大小,单位 为 byte |
| boolean isEmpty() | 是否有上传的文件 |
| void transferTo(File dest) | 将上传的文件保存在一个目标文件中 |
| File.separator | 间隔符 |
示例:SpringMVC 的文件上传
1、导入 jar 包, commons-fileupload-1.3.1.jar,commons-io-2.4.jar
2、在 spring-config.xml 中配置MultipartFile
<?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven/>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和 jsp 的 pageEncoding 属性一致, -->
<property name="defaultEncoding" value="UTF-8"></property>
<!--上传文件大小上限,单位为字节(10M)-->
<property name="maxUploadSize" value="10240000"></property>
</bean>
<!--
视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
3、controller
@Controller
public class FileController {
/*
* 文件上传
* */
@RequestMapping(value = "/fileUpload",method = RequestMethod.POST)
public String fileUpload01(HttpServletRequest request,@RequestParam("file") MultipartFile file) throws Exception {
//如果文件不为空,写入上传路径
if (!file.isEmpty()) {
//上传文件路径
String path = request.getServletContext().getRealPath("/images/");
System.out.println(path);
//上传文件名
String filename = file.getOriginalFilename();
File filepath = new File(path,filename);
//判断路径是否存在,如果不存在就创建一个
if (!filepath.getParentFile().exists()) {
filepath.getParentFile().mkdirs();
}
//将上传文件保存到一个目标文件中
file.transferTo(new File(path + File.separator + filename));
return "success";
} else {
return "index.jsp";
}
}
}
4、jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>文件上传</h1>
<form action="${pageContext.request.contextPath}/fileUpload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" id="file" name="file"> <br/>
<input type="submit" value="上传">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>成功上传</h1>
</body>
</html>
5、测试

批量上传文件 如果上传单个,可以把for循环去除,并且把 [] 数组去除,即可
public String filewpload(CommonsMultipartFile file[],HttpServletRequest request) throws Exception {
//获取上传文件的路径
String path = request.getRealPath("/upload");
for (int i =0; i < file.length; i++) {
InputStream is = file[i].getInputStream();
OutputStream os = new FileOutputStream(new File(path, file[i].getOriginalFilename()));
int len = 0;
byte[] buffer = new byte[1024];
while((len = is.read(buffer)) != -1){
os.write(buffer,0,len);
os.close();
is.close();
}
}
return "/index";
}
使用对象接收上传文件
上传的文件会作为对象的属性被保存
步骤1、jsp index.jsp userInfo.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>文件上传</h1>
<form action="/register" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="name"> <br/>
选择文件:<input type="file" id="file" name="image"> <br/>
<input type="submit" value="上传">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>文件下载</h2>
<a href="download?filename=${requestScope.user.image.originalFilename}">
${requestScope.user.image.originalFilename}
</a>
</body>
</html>
步骤2、controller,Javabean
@Controller
public class UserController {
@RequestMapping(value = "/register")
public String register(HttpServletRequest request, @ModelAttribute User user, Model model) throws IOException {
System.out.println(user.getImage());
//如果文件不为空,写入上传路径
if (!user.getImage().isEmpty()) {
String path = request.getServletContext().getRealPath("/images");
//获取上传文件名
String filename = user.getImage().getOriginalFilename();
File filepath = new File(path,filename);
//判断路径是否存在,如果不存在就创建一个
if (!filepath.getParentFile().exists()) {
filepath.getParentFile().mkdirs();
}
//将上传的文件保存到一个目标文件中
user.getImage().transferTo(new File(path + File.separator + filename));
//将用户添加到model
model.addAttribute("user", user);
//跳转到下载页面
return "userInfo";
}
return "error";
}
}
public class User implements Serializable {
private String name;
private MultipartFile image;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
步骤3、xml配置 spring-config.xml
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven/>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="10240000"></property>
</bean>
<!--
视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
步骤4、测试 http://localhost:8080/ http://localhost:8080/register

文件下载
只需在上传文件的代码 添加如下代码即可 UserController.java
@RequestMapping(value = "/download")
public ResponseEntity<byte[]> download(HttpServletRequest request,
@RequestParam("filename") String filename, Model model) throws Exception {
//获取下载路径
String path = request.getServletContext().getRealPath("/images");
File file = new File(path + File.separator + filename);
HttpHeaders headers = new HttpHeaders();
// 获取下载显示的中文名,解决中文名乱码问题
String downloadFileName = new String(filename.getBytes("UTF-8"), "iso-8859-1");
//通知浏览器以 attachment (下载方式)打开图片
headers.setContentDispositionFormData("attachment", downloadFileName);
// application/octet-stream ,二进制流数据(最常见的文件下载)
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 201 HttpStatus.CREATED
return new ResponseEntity<byte []>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
}
文件下载测试就不展示,我测试过没问题
拦截器
主要作用是拦截用户请求,并对其进行相应的处理,可以通过拦截器进行用户权限验证,判断用户是否登录。通过实现 HandlerInterceptor 接口完成
示例:拦截器实现用户权限验证
导包 jstl标签包,spring依赖包
jstl-1.2.jar、standard-1.1.2.jar
taglibs-standard-compat-1.2.5.jar taglibs-standard-impl-1.2.5.jar
taglibs-standard-jstlel-1.2.5.jar taglibs-standard-spec-1.2.5.jar
如果用户直接跳过登录页面访问主页面,将被拦截下来
步骤1、xml配置文件 spring-config.xml
<context:component-scan base-package="com.yuanwu.controller"/>
<!-- 使用默认的 Servlet 来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 设置配置方案 ,启用注解支持-->
<mvc:annotation-driven/>
<!-- SpringMVC拦截器定义 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有请求 -->
<mvc:mapping path="/*"/>
<!-- 定义一个Interceptor拦截器 -->
<bean class="com.yuanwu.interceptor.AuthorizationInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!--
视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
步骤2、controller,
UserController.java
@Controller
public class UserController {
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping(value = "/login")
public ModelAndView login(
String name,String password,
ModelAndView mv,
HttpSession session) {
if (name != null && name.equals("admin") &&
password != null && password.equals("123")) {
User user = new User();
user.setName(name);
user.setPassword(password);
user.setUserName("超级管理员");
session.setAttribute("user", user);
System.out.println(user.toString());
mv.setViewName("redirect:main");
} else {
mv.addObject("message", "请重新输入");
mv.setViewName("/index");
}
return mv;
}
}
BookController
@Controller
public class BookController {
@RequestMapping(value = "/main")
public String main(Model model) {
List<Book> bookList = new ArrayList<Book>();
bookList.add(new Book("java1.jpg","Java基础","李刚",75.3));
bookList.add(new Book("java2.jpg","Java基础","李刚",75.3));
bookList.add(new Book("java3.jpg","Java基础","李刚",75.3));
bookList.add(new Book("java4.jpg","Java基础","李刚",75.3));
model.addAttribute("bookList", bookList);
return "main";
}
}
步骤3、拦截器 实现 Interceptor 接口
需要在 spring-config.xml 中配置 拦截器,在第一步中,已经写清楚了
public class AuthorizationInterceptor implements HandlerInterceptor {
// 不拦截 “/index” 和 “/login” 请求
private static final String[] IGNORE_URI = {"/index","/login"};
/*
* 该方法在整个请求完成之后执行,主要用于清理资源
* 也之能在当前的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行
* */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("AuthorizationInterceptor afterCompletion......执行");
}
/*
* 该方法将在 Controller 的方法调用之后执行,方法中可以对 ModelAndView 进行操作,
* 但也只能在当前 Interceptor 的 preHandle 方法的返回值为 true 时才会执行
* */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("AuthorizationInterceptor postHandle.....执行");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("AuthorizationInterceptor preHandle.....执行");
// flag 变量用于判断用户是否登录,默认为false
boolean flag = false;
// 获取请求的路径,进行判断
String servletPath = request.getServletPath();
System.out.println(servletPath);
// 判断请求是否需要拦截
for (String s : IGNORE_URI) {
if (servletPath.contains(s)) {
flag = true;
break;
}
}
// 拦截请求
if (!flag) {
// 1、获取 Session 中的用户
User user = (User) request.getSession().getAttribute("user");
System.out.println(user);
// 2、判断用户是否已经登录
if (user == null) {
// 如果用户没有登录,则设置提示信息,跳转到登录页面
System.out.println("AuthorizationInterceptor preHandle.....拦截请求");
request.setAttribute("message", "请先登录再访问网页");
request.getRequestDispatcher("/index").forward(request, response);
} else {
// 如果用户已经登录,则放行
System.out.println("AuthorizationInterceptor preHandle.....放行");
flag = true;
}
}
return flag;
}
}
步骤4、jsp文件
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1>登录页面</h1>
<form action="login" method="post">
<font color="red" size="50">${requestScope.message}</font>
<table>
<tr>
<td>登录名:</td>
<td><input type="text" id="name" name="name"></td>
</tr>
<td>
<td>密码:</td>
<td><input type="password" id="password" name="password"></td>
</td>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>欢迎[${sessionScope.user.userName}]访问</h1>
<br>
<table border="1">
<th>封面</th>
<th>书名</th>
<th>作者</th>
<th>单价</th>
<c:forEach items="${requestScope.bookList}" var="book">
<tr>
<td><img src="images/${book.image}" height="60"></td>
<td>${book.name}</td>
<td>${book.author}</td>
<td>${book.price}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
步骤5、测试 http://localhost:8080/main http://localhost:8080/login http://localhost:8080/main
输入账号admin 密码123

MyBatis__全局配置文件
1、引入dtd约束
联网状态下: http://mybatis.org/dtd/mybatis-3-config.dtd
2、properties_引入外部配置
mybatis可以使用propertie来引入外部propertie配置文件的内容
resource == >> 引入类路径下的资源
url == >> 引入网络路径或者磁盘路径下的资源
<properties resource="dbconfig.properties"></properties>
<dataSource>
<properties name="driver" value="${}"/>
..............
</dataSource>
3、settings_运行时行为设置
Mybatis3--配置 https://mybatis.org/mybatis-3/zh/configuration.html#settings
举例:【是否开启驼峰命名规则】
是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
<!--
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
4、typeAliases_别名
【别名处理器】
类型别名可为 Java 类型设置一个缩写名字
引用【resultType="emp"】
【别名不区分大小写】
<typeAliases>
<!--1、
typeAlias:为某个Java类型起别名
type:指定要起别名的全类名,默认别名是类名小写
alias:指定别名
-->
<typeAlias type="全类名" alias="指定别名"/>
<!--2、
package:为某个包下的所有类批量取别名
name:指定包名【起一个默认别名小写,当前包以及后代包】
-->
<package name="包名"/>
<!--3、
【批量取别名】包扫描情况下,使用注解方式 @Alias 为某个类指定别名
-->
</typeAliases>
5、typeHandlers_类型
typeHandlers_【类型处理器】
自定义类型处理器
6、plugins_插件简介
- Executor 【执行器】(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler【参数处理器】 (getParameterObject, setParameters)
- ResultSetHandler【结果集处理器】 (handleResultSets, handleOutputParameters)
- StatementHandler【SQL语句处理器】 (prepare, parameterize, batch, update, query)
7、environments_环境配置
数据库数据源
<environments default="development">
<!--
environments:可以配置成适应多种环境 ,default:指定使用某种环境,达到快速切换环境
environment:配置一个具体的环境信息,↓↓↓↓↓这两个参数必须存在
transactionManager【事务管理器】,
type:事务管理器类型,type="[JDBC|MANAGED]"
自定义事务管理器:实现TransactionFactory 接口,type指定为全限定类名
dataSource【数据源】
type:数据源类型,type="[UNPOOLED|POOLED|JNDI]"
自定义数据源:实现 DataSourceFactory 接口,type指定为全类名
id="development":表示当前环境的唯一标识
-->
<environment id="development">
<transactionManager type=""></transactionManager>
<dataSource type=""></dataSource>
</environment>
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
MyBatis 可以配置成适应多种环境
每个SqlSessionFactory实例只能选择一种环境
一个environment 对应一个 SqlSessionFactory实例
8、databaseIdProvider_数据库厂商标识
<!--
databaseIdProvider:支持多数据库厂商
type="DB_VENDOR":得到数据库厂商标识【驱动getDatabaseProductName()】,mybatis就能根据数据库厂商标识来执行不同的sql
-->
<databaseIdProvider type="DB_VENDOR">
<!--
为不同的数据库厂商起别名,在xxxmapper.xml里面,sql语句databaseId="mysql"表示在mysql环境下才执行sql
-->
<property name="MYSQL" value="mysql"/>
......
</databaseIdProvider>
9、mappers_映射器
MyBatis需要开发者自己写SQL语句,mapper映射器告诉MyBatis到哪里去找映射文件
<!--
mappers:将sql映射 注册到全局配置中
-->
<mappers>
<!--
mapper:注册一个sql映射
resource:引用类路径下的sql映射文件
url:引用网络路径或者磁盘路径下的sql映射文件
class:引用接口
-->
<mapper resource=""/>
<!--批量注册,类名和xml要在同一路径下-->
<package name="包名"/>
</mappers>
Mybatis__SQL映射文件
1、增删改查
cache– 该命名空间的缓存配置。cache-ref– 引用其它命名空间的缓存配置。resultMap– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。parameterMap– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。sql– 可被其它语句引用的可重用语句块。insert– 映射插入语句。update– 映射更新语句。delete– 映射删除语句。select– 映射查询语句。
EmployeeMapper.java
public void addEmp(Employee employee);
public void updateEmp(Employee employee);
public void deleteEmpById(Integer id);
EmployeeMapper.xml
<!-- public void addEmp(Employee employee); -->
<!--
parameterType:可以省略
-->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee">
insert tbl_employee(last_name,gender,email)
values(#{lastName},#{gender},#{email})
</insert>
<!-- public void updateEmp(Employee employee); -->
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},gender=#{gender},email=#{email}
where id=#{id}
</update>
<!-- public void deleteEmpById(Integer id); -->
<delete id="deleteEmpById">
delete from tbl_employee where id=#{id}
</delete>
MyBatisTest.java
@Test
public void test03() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
// Employee employee = new Employee(null, "Jerry", "0", "Jerry@yuanwu.com");
// mapper.addEmp(employee);
//测试修改
// Employee employee = new Employee(1, "Back", "1", "Back@yuanwu.com");
// mapper.updateEmp(employee);
//测试删除
mapper.deleteEmpById(1);
//1、手动提交数据
openSession.commit();
} finally {
openSession.close();
}
}
2、insert_获取自增主键的值
<!--
获取自增主键的值:
mybatis支持自增主键,自增主键值的获取,mybatis也是利用 statement.getGeneratedKeys();
useGeneratedKeys="true";使用自增主键获取主键值策略
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给JavaBean的某个属性
-->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert tbl_employee(last_name,gender,email)
values(#{lastName},#{gender},#{email})
</insert>
3、insert_获取非自增主键的值
<!--
Oracle不支持自增:Oracle使用序列来模拟自增
每次插入的数据的主键是从序列中拿到的值,如何拿?
-->
<insert id="addEmp" databaseId="oracle">
<!-- keyProperty:查出的主键值封装给JavaBean的哪个属性
order="BEFORE" > 当前sql在插入sql之前运行
AFTER > 当前sql在插入sql之后运行
resultType:查出的数据的返回类型
BEFORE运行顺序:
先运行selectKey 查询id的sql,查出的id值封装给JavaBean的id属性
然后运行插入的sql,就可以取出id属性对应的值
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入时的主键是从序列中拿到的 -->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email})
</insert>
4、参数处理_单个参数&多个参数&命名
EmployeeMapper.java
public Employee getEmpByIdAndLastName(
@Param("id") Integer id,@Param("lastName") String lastName);
EmployeeMapper.xml
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName); -->
<select id="getEmpByIdAndLastName" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
MyBatis.java
@Test
public void test04() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//多个参数查询
Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
System.out.println(employee);
} finally {
openSession.close();
}
}
mybatis参数处理.txt 总结
传入单个参数:
#{参数名}:取出参数值
传入多个参数:
多个参数会被封装成一个map,
key:param1, param2...或者参数的索引
value:传入的参数值
#{}就是从map中获取指定的key的值;
绑定异常:
Cause: org.apache.ibatis.binding.BindingException:
Parameter 'id' not found.
Available parameters are [arg1, arg0, param1, param2]
操作:
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
取值:id = #{id} and last_name=#{lastName}
命名参数:明确指定封装参数时map的key;@Param("id")
多个参数会被封装成一个map,
key:使用@Param注解指定的值
value:参数值
#{指定的key}取出对应参数值
5、参数处理_POJO&Map&TO
POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们可以直接传入pojo;【传入Javabean对象】
#{属性名},取出传入的pojo的属性值
Map:
如果多个参数不是业务逻辑的数据模型,没有对应的pojo,我们也可以传入map;【不经常使用】
#{key},取出map中对应的值
TO
如果多个参数不是业务逻辑的数据模型,但是要经常使用,推荐编写一个TO(Transfer Object)数据传输对象
EmployeeMapper.xml
<!-- public Employee getEmpByMap(Map<String,Object> map); -->
<select id="getEmpByMap" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>
EmployeeMapper.java
public Employee getEmpByMap(Map<String,Object> map);
MyBatis.java
@Test
public void test04() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//多个参数查询
// Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
Map<String,Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName","Jerry");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee);
} finally {
openSession.close();
}
}
6、源码分析_参数封装map的过程
1.MapperProxy > 2.MapperMethod > 3.ParamNameResolver
===================结合源码,mybatis怎么处理参数===============================
ParamNameResolver解析参数封装map
//1、names:{ 0 -> id, 1 -> lastName}:构造器的时候就确定好了
确定流程:
1、获取每个标注了@param注解的参数的@Param的值:id,lastName;赋值给name;【name = ((Param) annotation).value();】
2、每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
name的值:
标注了param注解:注解的值
没有标注:
1、全局配置:useActualParamName(jdk1.8):name=参数名;【在全局配置中配置了useActualParamName属性】
2、name = String.valueOf(map.size());相当于当前元素的索引
{ 0 -> id, 1 -> lastName,2=2}【如果没标注注解,索引是2,参数也是2】
args[2, "Jerry"]:
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
//1、如果参数为null,直接返回
if (args == null || paramCount == 0) {
return null;
//2、如果只有一个元素,并且没有标注Param注解,返回第一个key, args[0],单个参数
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//3、多个元素或者标注Param注解
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
//4、遍历names集合:{ 0 -> id, 1 -> lastName,2=2}
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//names集合的value作为key,names集合的key又作为取值的参考args[0]
//最终 ==> eg:{ id=args[0]:1, lastName=args[1]:Jerry, 2=args[2] }
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
//额外的将每一个参数也保存到map中,并使用新的key:param1...paramN
//也就是==> 既可以使用有Param注解的#{指定的key},也可以使用#{param1}
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
7、参数处理_#与$取值区别
==============================参数值的获取==============================
#{}:可以获取map中的值或者pojo对象属性的值
${}:可以获取map中的值或者pojo对象属性的值
区别:
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中,会有安全问题;
大多数情况下,我们取参数的值都应该使用#{}
表名字段名都不支持预编译,字段值可以
但是,原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序
8、参数处理_#取值时指定参数相关规则
#取值时指定参数相关规则
jdbcType通常需要在某种特定的条件下被设置:
在数据库为null时,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
因为mybatis对所有的null都映射的是原生jdbc的OTHER类型,Oracle不认识
全局配置中,jdbcTypeForNull=OTHER【默认】,Oracle不支持,有两种方式
1、#{email,jdbcType=OTHER}
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>【全局配置】
9、select_返回List
<!-- public List<Employee> getEmpsByLastNameLike(String lastName);
resultType:如果返回的是一个集合,要写集合中元素的类型
-->
<select id="getEmpsByLastNameLike" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
10、select_记录封装Map
EmployeeMapper.java
//多条记录封装一个map,Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的JavaBean
//告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("id")
public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);
//返回一条记录的map,key=列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
EmployeeMapper.xml
<!-- public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>
<!-- public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
MyBatisTest.java
// Map<String, Object> map = mapper.getEmpByIdReturnMap(3);
// System.out.println(map);
Map<Integer, Employee> map = mapper.getEmpByLastNameLikeReturnMap("%r%");
System.out.println(map); //{2=Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}, 3=Employee{id=3, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}}
11、select_resultMap【自定义结果映射规则】
EMployeeMapperPlus.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yuanwu.mybatis.dao.EmployeeMapperPlus">
<!-- 自定义某个JavaBean的封装规则
type:自定义规则的Java类型
id:唯一标识符,方便引用
-->
<resultMap id="MyEmp" type="com.yuanwu.mybatis.bean.Employee">
<!-- 指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的JavaBean属性
-->
<id column="id" property="id"/>
<!-- result定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装,只要写resultMap就把全部的映射规则都写上-->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<!-- resultMap:自定义结果集映射规则 -->
<!-- public Employee getEmpById(Integer id);-->
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id=#{id}
</select>
</mapper>
12、select_resultMap_级联属性
EmployeeMapperPlus.xml
<!--
模拟场景一:
查询Employee的同时查询员工对应的部门
Employee===Department 【一个Employee 对应 一个部门,每一个员工都有部门信息】
一个员工有与之对应的部门信息;
id last_name gender email 【d_id did dept_name【private Department dept;】
-->
<!--
联合查询:级联属性,封装结果集
-->
<resultMap id="MyDifEmp" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
d.id did,d.dept_name dept_name
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>
测试类
Employee empAndDept = mapper.getEmpAndDept(2);
System.out.println(empAndDept);
System.out.println(empAndDept.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/
13、select_resultMap_association属性
<!--
使用association标签定义关联单个对象的封装规则;
-->
<resultMap id="MyDifEmp2" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!-- association标签可以指定联合的JavaBean对象
property="dept":指定哪个属性是联合对象
javaType:指定这个属性对象的类型【不能省略】
-->
<association property="dept" javaType="com.yuanwu.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp2">
SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
d.id did,d.dept_name dept_name
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>
14、select_resultMap_association分布查询
DepartmentMapper.xml
<!-- public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.yuanwu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
EmployeeMapperPlus.xml
<!-- 使用association进行分布查询
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值 拿去部门表查出部门信息
3、部门设置到员工中;
-->
<!-- id last_name gender email d_id -->
<resultMap id="MyEmpByStep" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定方法查出的结果
column:将哪一列的值传给这个方法
流程:
使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept" select="com.yuanwu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id); -->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
</select>
测试类
Employee empByIdStep = mapper.getEmpByIdStep(2);
System.out.println(empByIdStep);
System.out.println(empByIdStep.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/
15、select_resultMap_懒加载
mybatis-config.xml 【在全局配置文件中,设置两个属性】
<!-- 可以使用延迟加载【懒加载】:
Employee==>Dept 【Employee对象包含一个Dept属性】
我们每次查询Employee对象的时候,都将一起查询处理。
我们希望部门信息在我们需要使用的时候再去查询;
分布查询的基础之上加上两个配置;
-->
<!-- 显式的指定需要更改的配置的值,即使它是默认的。-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
测试类
System.out.println(empByIdStep.getLastName());
System.out.println(empByIdStep.getDept());
/*
DEBUG 10-16 21:55:46,405 ==> Preparing: select * from tbl_employee where id=? (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,447 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,551 <== Total: 1 (BaseJdbcLogger.java:143)
Jerry
DEBUG 10-16 21:55:46,552 ==> Preparing: select id,dept_name departmentName from tbl_dept where id=? (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,553 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,562 <== Total: 1 (BaseJdbcLogger.java:143)
Department{id=1, departmentName='开发部'}
*/
场景二
<!--
场景二:
查询部门的时候将部门对应的所有员工信息也查询出来,注释在DepartmentMapper.xml
-->
Mybatis__动态SQL
if:判断
choose (when, otherwise):分支选择 switch-case
如果带了id就用id查,如果带了gender就用gender查,只会进入一个分支
trim (where(封装查询条件), set(封装修改条件)):字符串截取
foreach(遍历集合)
1、if_判断&OGNL
<!-- 查询员工,要求携带了哪个字段查询条件就带上这个字段的值 -->
<!-- test:判断表达式(OGNL)
从参数中取值进行判断
-->
select * from tbl_employee
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null and lastName!=''">
and last_name like #{lastName}
</if>
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
<if test="email!=null and email.trim()!=''">
and email=#{email}
</if>
2、where_查询条件
<!--where
查询的时候如果某些条件没带可能sql拼装会有问题
1、在where 后面 添加1=1(不安全)
2、mybatis使用where标签来将所有的查询条件包括在内【mybtais推荐】
mybatis会将where标签中拼装的sql,多出来的and或者or去掉
where只会去掉第一个多出来的and或者or
-->
select * from tbl_employee
<where>
<!-- test:判断表达式(OGNL)
从参数中取值进行判断
-->
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null and lastName!=''">
and last_name like #{lastName}
</if>
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
<if test="email!=null and email.trim()!=''">
and email=#{email}
</if>
</where>
3、trim_自定义字符串截取
select * from tbl_employee
<!--
prefix="" :前缀,trim标签体中是整个字符串拼装后的结果。
prefix给拼串后的整个字符串加一个前缀【where】
prefixOverrides="":前缀覆盖,去掉整个字符串前面多余的字符
suffix="":后缀,
suffix给拼串后的整个字符串加一个后缀
suffixOverrides="":后缀覆盖,去掉整个字符串后面多余的字符
-->
where
<!-- 自定义字符串的截取规则 -->
<trim suffix="" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="lastName!=null and lastName!=''">
last_name like #{lastName} and
</if>
<if test="gender==0 or gender==1">
gender=#{gender} and
</if>
<if test="email!=null and email.trim()!=''">
email=#{email}
</if>
</trim>
4、choose_分支选择
select * from tbl_employee
<where>
<!-- 如果带了id就用id查,如果带了gender就用gender查,只会进入一个分支 -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<when test="email!=null">
email=#{email}
</when>
<otherwise>
1=1
<!-- gender =0 -->
</otherwise>
</choose>
</where>
5、set_与if结合的动态更新
查询不需要提交外,增删改都需要提交,openSession.commit();手动提交
<!-- public void updateEmp(Employee employee);
可以使用set标签,或者trim标签,来去除多余的,逗号
-->
<update id="updateEmp">
update tbl_employee
<!--<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="email!=null">
email=#{email}
</if>
</set>-->
set
<trim suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="email!=null">
email=#{email}
</if>
</trim>
where id=#{id}
</update>
6、foreach_遍历集合
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
带IN关键字的查询 【IN关键字用于判断某个字段的值是否在指定集合中,如果是,则满足条件,该字段所在的记录将被查询出来,【也可以是增删改】】
SQL语句:
select * 【或者】字段名1,字段名2,...
from 表名
where 字段名 [not]in(元素1[值],元素2,...)
<!-- public List<Employee> getEmpsByConditionForeach(List<Integer> ids);-->
<select id="getEmpsByConditionForeach" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee
where id in
<!--
collection:指定要遍历的集合;
1、强制指定为list且不可改变
[传入list的时候,MyBatis会自动包装在一个map中,
List实例将会以“list”做为键,会通过“list”去寻找]
2、利用注解@Param指定入参名称
3、将List包装成Map参数进行传递
item:将当前遍历出的元素赋值给指定的变量;
separator:每个元素之间的分隔符;
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引,遍历list的时候index就是所有,item就是当前值
遍历map的时候index表示map的key,item就是map的值value
#{变量名} 取出变量的值,当前遍历出的元素
-->
<foreach collection="list" item="item_id" separator=","
open="(" close=")" index="">
#{item_id}
</foreach>
</select>
测试类
List<Employee> list = mapper.getEmpsByConditionForeach(Arrays.asList(1,2,3,4));
for (Employee emp: list) {
System.out.println(emp);
}
/*
Preparing: select * from tbl_employee where id in ( ? , ? , ? , ? ) (BaseJdbcLogger.java:143)
Parameters: 1(Integer), 2(Integer), 3(Integer), 4(Integer) (BaseJdbcLogger.java:143)
DEBUG 10-21 22:23:55,801 <== Total: 4 (BaseJdbcLogger.java:143)
Employee{id=1, lastName='Admin', gender='1', email='Tom@yuanwu.com'}
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Employee{id=3, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Employee{id=4, lastName='Herbert', gender='1', email='Herbert@yuanwu.com'}
*/
Mybatis_缓存
缓存_缓存介绍
-
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便的配置和定制。缓存可以极大的提升查询效率
-
MyBatis系统中 默认定义了两级缓存
-
一级缓存和二级缓存
- 1、默认情况下:只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启;
- 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 3、为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
缓存_一级缓存体验
两级缓存
一级缓存:(本地缓存)sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的Map
与数据库同一次会话期间查询到的数据会放在本地缓存中。
如果再次获取相同的数据,直接从缓存中拿。
二级缓存:(全局缓存)
缓存_一级缓存失效的四种情况
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还要再次向数据库发送sql)
1、sqlSession不同
2、sqlSession相同,查询条件不同
3、sqlSession相同,两次查询之间执行增删改操作(这次增删改操作可能影响当前数据)
4、sqlSession相同,手动清除一级缓存(缓存情况)openSession.clearCache();
@Test
public void testFirstLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
//一级缓存初体验
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee empById01 = mapper.getEmpById(1);
System.out.println(empById01);
//1、sqlSession不同。
/*SqlSession openSession2 = sqlSessionFactory.openSession();
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);*/
//3、sqlSession相同,两次查询之间执行了增删改操作;(增删改操作有可能影响当前数据)
/*mapper.addEmp(new Employee(null,"cache","1","testCache@qq.com"));
System.out.println("数据添加成功");*/
//4、sqlSession相同,手动清空一级缓存
// openSession.clearCache();
//2、sqlSession相同,查询条件不同
Employee empById02 = mapper.getEmpById(1);
System.out.println(empById02);
System.out.println(empById01 == empById02);
// openSession2.close();
} finally {
openSession.close();
}
}
缓存_二级缓存使用&细节
1、开启全局二级缓存配置
<setting name="cacheEnabled" value="true"></setting>
2、去mapper.xml中配置使用二级缓存
<cache eviction="" flushInterval="" readOnly="" size="" type=""></cache>
<!--
eviction:缓存回收策略
· LRU:最近使用最少的,移除最长时间不被使用的对象
· FIFO:先进先出,按对象进入缓存的顺序来移除
· SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
· WEAK:弱引用,更积极的移除垃圾回收器状态和弱引用规则的对象
· 默认是LRU
flushInterval:刷新间隔,单位毫秒
· 默认情况下是不设置,没有刷新时间,缓存仅仅调用语句时刷新
size:引用数目,正整数
· 代表缓存最大可以存储多少个对象,太大容易导致内存溢出
readOnly:只读,true/false
· true:只读缓存,会给所有调用者返回缓存对象的相同实例
1 false:读写缓存,会返回缓存对象的拷贝(通过序列化),
type:指定自定义缓存的全类名;
实现Cache接口即可
-->
/**
*两级缓存:
* 一级缓存(本地缓存):sqlSession级别的缓存,一级缓存是一直开启的,sqlSession级别的一个Map
* 与数据库同一次会话期间查询到的数据会放在本地缓存中。
*
* 一级缓存失效情况(没有使用到当前一级缓存的情况):
* 1、sqlSession不同。
* 2、sqlSession相同,查询条件不同(当前一级缓存中还没有这个数据)
* 3、sqlSession相同,两次查询之间执行了增删改操作;(增删改操作有可能影响当前数据)
* 4、sqlSession相同,手动清空一级缓存
*
* 二级缓存(全局缓存):基于namespace级别的缓存,一个namespace对应一个二级缓存;
* 工作机制:
* 1、一次会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
* 2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中。新的会话查询信息,就可以参照二级缓存
* 3、不同的namespace查出的数据会放在自己对应的缓存中(namespace级别的map)
*
* 查出的数据都会默认先放在一级缓存中,
* 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存
* 使用&细节:
* 1)、开启全局二级缓存配置.<setting name="cacheEnabled" value="true"/>
* 2)、去mapper.xml中配置使用二级缓存;<cache></cache>
* 3)、POJO需要实现序列化接口
*/
@Test
public void testSecondLevelCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
try {
//
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
//第二次查询是从二级缓存中拿到的数据
Employee emp02 = mapper2.getEmpById(1);
System.out.println(emp02);
openSession2.close();
} finally {
// openSession.close();
}
}
缓存_缓存有关的配置以及属性
/**
* 和缓存有关的设置/属性
* 1)、cacheEnabled=true,【false:关闭缓存(二级缓存关闭,不会关闭一级缓存)】
* 2)、每个select标签都有一个useCache标签,useCache="true"
* false:不使用缓存【一级缓存仍然可以使用,二级缓存不使用】
* 3)、每个增删改标签都有flushCache标签,flushCache="true"【一级二级都会清除】
* 增删改执行后就会清除缓存
* 测试:flushCache="true",一级缓存清空了,二级缓存也会被清除
* 查询标签也有flushCache="true"
* 如果设置为false,每次查询之后都会清空缓存,缓存不会被使用
* 4)、openSession.clearCache();只是清除当前session的一级缓存
* 5)、localCacheScope:本地缓存作用域(一级缓存SESSION),当前会话的所有数据保存在会话缓存中。
* 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。【禁用一级缓存】
* */
缓存_缓存原理图示
MyBatis_Spring整合_ssm
配置文件
log4j.properties
LOG4J 日志框架 jar包,类路径下增加一个 log4j.properties
控制台输出SQL语句
# 全局的日志配置
log4j.rootLogger=ERROR, stdout
# MyBatis的日志配置
log4j.logger.[SQL映射文件]全限定类名=DEBUG
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPatern=%5 [%t] - %m%n
web.xml
前端控制器DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<!-- 定义Spring MVC 控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-configs.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc-config.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--自动配置-->
<!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-packge="全限定类名【controller】"/>
<!--让Spring Mvc不处理静态资源 .css .js .html ....-->
<mvc:default-servlet-handler/>
<!--
启用注解,分别为HandlerMapping的实现类和HandlerAdapter的实现类
自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter
spring 3.x以后 对应实现类改为RequestMappingHandlerMapping的Bean
注解驱动,以使得访问路径与方法的匹配可以通过注解配置
-->
<mvc:annotation-driven/>
<!--视图解析器:DispatcherSevlet给他的ModelAdnView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
<!-------------------------------------------------------------------------------------->
<!--手动配置-->
<bean id="/hello" class="全限定类名,【controller控制器类】"/>
</beans>
Spring学习
Spring 模块划分图:

从底向上看:一个绿色就是一个模块
Test :Spring 的单元测试模块 ;
spring-test-5.1.5.RELEASE.jar
Core Container :核心容器(IOC0) ;黑色部分代表这部分的功能由哪些 jar 包组成 ;要使用完整功能,这些 jar 包要导入
spring-beans-5.1.5.RELEASE.jar、
spring-context-5.1.5.RELEASE.jar、
spring-core-5.1.5.RELEASE.jar、
spring-expression-5.1.5.RELEASE.jar
AOP+Aspects(面向切面编程模块)
spring-aop-5.1.5.RELEASE.jar、
spring-aspects-5.1.5.RELEASE.jar
数据访问/ :Spring 访问数据库模块
spring-jdbc-5.1.5.RELEASE.jar、spring-orm(Object RelationMapping)-5.1.5.RELEASE.jar、
spring-tx-5.1.5.RELEASE.jar(事务) (上面三个jar包跟数据访问相关)
spring-ox(xml)m-5.1.5.RELEASE.jar、spring-jms-5.1.5.RELEASE.jar、(Intergration 集成)
Web :Spring 开发web应用的模块 ;
spring-websocket-5.1.5.RELEASE.jar、(新的技术)
spring-web-5.1.5.RELEASE.jar、和原生的web相关(servlet)
spring-webmvc-5.1.5.RELEASE.jar、开发web项目的(web)
IOC
IOC :(Inversion(反转) Of Control):控制反转;
控制 :资源的获取方式 ;
主动式 :(要什么资源都自己创建出来即可)
BookServlet {
BookService bs = new BookService();
AirPlane ap = new AirPlane();// 但是要创建飞机对象,飞机有很多零件。复杂对象的创建时比较庞大的工程 ;
}
被动式 :资源的获取不是我们自己创建,而是交给一个容器来创建和设置 ;
BookServlet {
BookService bs;
public void test01() {
bs.checkout();
}
}
容器 :管理所有的组件(有功能的类) ;假设 ,BookServlet受容器管理 ,BookService也受容器管理 ,容器可以自动探查出哪些组件(类)需要用到另一个组件(类) ;容器帮我们创建BookService对象 ,并把BookService对象赋值过去 ;
组件就是所说的 类
容器 :主动的 new 资源变为被动的接收资源 ;
(容器)婚介所 :容器相当于婚介所 ,主动创建变为被动获取
IOC是一种思想,DI就是对这种思想实现的一种描述 ;
DI :(Dependency Injection)依赖注入 ;
容器能指定哪个组件(类)运行的时候 ,需要另外一个组件(类) ;容器通过反射的形式 ,将容器中准备好的BookService对象注入(利用反射给属性赋值)到BookServlet中 ;
Spring运行的是依赖一日志包,commons-logging
HelloWorld第一步:给容器注册一个组件
- 注入对象 :使用
ref属性
实验1、通过IOC容器创建对象,并为属性赋值
省略Person对象.......有兴趣的可以自行创建
IOC.xml
<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">
<!-- 注册一个Person对象,Spring会自动创建这个Person对象 -->
<!-- 一个bean标签可以注册一个主键(对象、类) -->
<!--
class:写要注册的组件的全类名
id:这个对象的唯一标识;
-->
<bean class="com.yuanwu.bean.Person" id="person01">
<!-- 使用property标签为Person对象的属性赋值
name:指定属性名
value:为属性赋值
-->
<property name="lastName" value="张三"/>
<property name="age" value="18"/>
<property name="gender" value="男"/>
<property name="email" value="zhangsan@163.com"/>
</bean>
</beans>
HelloWorld第二步:从容器中获取这个组件
public class IOCTest {
/**
* 从容器中拿到这个组件
* */
@Test
public void test() {
/*
* ApplicationContext:代表ioc容器
* ClassPathXmlApplicationContext:当前应用的xml配置文件在ClassPath(类路径下)
* 根据Spring 的配置文件得到ioc容器对象
* */
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
// 容器帮我们创建好对象
Person bean = (Person) ioc.getBean("person01");
System.out.println(bean);
// Person{lastName='张三', age=18, gender='男', email='zhangsan@163.com'}
}
}
实验2:根据bean的类型从IOC容器中获取bean的示例★
ioc.xml
<bean class="com.yuanwu.bean.Person" id="person01">
<!-- 使用property标签为Person对象的属性赋值
name:指定属性名
value:为属性赋值
-->
<property name="lastName" value="张三"/>
<property name="age" value="18"/>
<property name="gender" value="男"/>
<property name="email" value="zhangsan@163.com"/>
</bean>
<bean class="com.yuanwu.bean.Person" id="person02">
<!-- 使用property标签为Person对象的属性赋值
name:指定属性名
value:为属性赋值
-->
<property name="lastName" value="小花"/>
</bean>
</beans>
/**
* 实验2:根据bean的类型从IOC容器中获取bean的示例★
* 如果ioc容器中当前类型的bean有多个,查找会报错
* org.springframework.beans.factory.NoUniqueBeanDefinitionException:
* No qualifying bean of type 'com.yuanwu.bean.Person' available:
* expected single matching bean but found 2: person01,person02
*
* */
@Test
public void test02() {
// 当前类型的bean只有一个的时候可用
/*Person bean = ioc.getBean(Person.class);
System.out.println(bean); */
Person person02 = ioc.getBean("person02", Person.class);
System.out.println(person02);
/*
Person被创建
setLastName:>>>小花
Person{lastName='小花', age=null, gender='null', email='null'}
*/
}
实验3、通过构造器为bean的属性赋值
ioc.xml
<bean class="com.yuanwu.bean.Person" id="person03">
<!-- public Person(String lastName, Integer age, String gender, String email) -->
<constructor-arg name="lastName" value="小明"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="gender" value="男"/>
<constructor-arg name="email" value="xiaoming@qq.com"/>
</bean>
测试类
private ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
@Test
public void test03() {
Person person03 = ioc.getBean("person03", Person.class);
System.out.println(person03);
/*
Person被创建
setLastName:>>>张三
Person被创建
setLastName:>>>小花
有参构造器
Person{lastName='小明', age=18, gender='男', email='xiaoming@qq.com'}
*/
}
通过构造器为bean的属性赋值(index-type)
index(指定索引)、(type):指定类型,例如 :type="java.lang.Integer"
通过P名称空间为bean赋值
ioc.xml
<bean class="com.yuanwu.bean.Person" id="person06"
p:age="18" p:email="xiaoming@163.com"
p:gender="男" p:lastName="哈哈" >
</bean>
测试类
private ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
@Test
public void test03() {
Person person06 = ioc.getBean("person06", Person.class);
System.out.println(person06);
/*
有参构造器
Person被创建
setLastName:>>>哈哈
Person{lastName='哈哈', age=18, gender='男', email='xiaoming@163.com'}
*/
}
实验4、正确的为各种属性赋值
测试 null
<bean class="..." id="...">
<property name="lastName">
<!-- 进行复杂的赋值 -->
<null/
</property>
</bean>
ref引用外部的值
ioc.xml
<bean class="x.y.Car" id="car01">
<property name="color" value="红色"></property>
......
</bean>
<bean class="x.y.Person" id="person">
<!-- 引用类型 对象-->
<property name="car" ref="car01"></property>
</bean>
引用内部bean
ioc.xml
<bean class="x.y.Person" id="person">
<!-- 引用类型 对象-->
<property name="car">
<bean class="x.y.Car">
<property name="color" value="红色"></property>
</bean>
</property>
</bean>
如果是引用类型 、对象,可用在引用内部bean
- 集合注入总结
- List 属性使用
<list>元素定义注入,使用多个<ref>元素 去引用Bean
<bean class="x.y.Book" id="book01">
<property name="bookName" value="十万个为什么"/>
</bean>
<bean class="x.y.Person" id="person01">
<property name="list">
<list>
<bean class="x.y.Book" id="book0XX0" p:Bookname="西游记"></bean>
<!-- 引用外部一个元素 -->
<ref bean="bean1"/>
</list>
</property>
</bean>
- Map

c命名空间装配bean
会直接引用构造方法中的参数的名称 ;
测试 :学生类
package pojo;
public class Student {
int id;
String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// setter and getter
}
xml
<!-- 引入 c-命名空间之前 -->
<bean name="student1" class="pojo.Student">
<constructor-arg name="id" value="1" />
<constructor-arg name="name" value="学生1"/>
</bean>
<!-- 引入 c-命名空间之后 -->
<bean name="student2" class="pojo.Student"
c:id="2" c:name="学生2"/>
Spring_依赖注入(修正)
1、依赖注入_Dependency injection
-
依赖 :指 bean 对象创建依赖于容器 ,Bean 对象的依赖资源
-
注入 :指 bena 对象依赖的资源由容器来设置和装配
2、Spring 注入 --- 构造器注入
3、Spring 注入 --- setter 注入
//测试类
public class TestStudent {
private ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
@Test
public void test01() {
Student student = (Student) ioc.getBean("student");
System.out.println(student);
}
}
-
要求被注入的属性必须要有 set 方法 。
- a)常量注入
<bean id="student" class="com.yuanwu.bean.Student"> <property name="name" value="灭绝师太"/> </bean>- b)bean(对象)注入
<bean class="com.yuanwu.bean.Address" id="address"> <property name="address" value="湖北" /> </bean> <bean id="student" class="com.yuanwu.bean.Student"> <property name="name" value="灭绝师太"/> <property name="address" ref="address"/> </bean>- c) 数组注入
<bean id="student" class="com.yuanwu.bean.Student"> <property name="name" value="灭绝师太"/> <property name="address" ref="address"/> <!-- 数组注入 --> <property name="books"> <array> <value>傲慢与偏见</value> <value>雾都孤儿</value> <value>水浒传</value> </array> </property> </bean>- List 注入
部分代码,List注入 格式如下 ,后面会将所有方式的注入代码贴出来 ;【参考 】
<property name="hobbies"> <list> <value>羽毛球</value> <value>乒乓球</value> <value>篮球</value> <value>足球</value> <value>橄榄球</value> </list> </property>- Map 注入
<property name="cards"> <map> <entry key="中国银行" value="147258369123456"/> <entry key="广发银行" value="147258369331654"/> <entry key="浦发银行" value="147258369987456"/> <entry> <key><value>建设银行</value></key> <value>123456987456321</value> </entry> </map> </property>- Set 注入
<property name="games"> <set> <value>死神vs火影1.1</value> <value>死神vs火影1.2</value> <value>死神vs火影1.3</value> </set> </property>- null 注入
<property name="wife"><null/></property>- properties 注入
<property name="info"> <props> <prop key="学号">1213698</prop> <prop key="sex">女</prop> <prop key="name">小明</prop> </props> </property>- c命名空间 注入
要求有对应参数的构造方法
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> <!-- traditional declaration --> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> <constructor-arg value="foo@bar.com"/> </bean> <!-- c-namespace declaration --> <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> </beans>
IOC常用注解分类
在 xml 配置文件中 开启对注解的支持
<beans>
<context:component-scan base-package="告诉spring ,创建容器时要扫描的包"/>
</beans>
-
用于创建对象的 :
- 作用和 xml配置文件中
标签实现的功能是一样的 @Component:- 作用 :用于把当前类对象存入 spring 容器中
- 属性
- value :用于指定 bean 的id,默认是当前类名且首字母小写
- 作用和 xml配置文件中
-
用于注入数据的 :
-
作用和 xml配置文件中 bean标签中 写一个
标签作用一样 -
@Autowired- 作用:自动按照类型注入。只要容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功。
- 如果ioc容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错。
- 如果Ioc容器中有多个类型匹配时:使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
- 出现位置:
可以是变量上,也可以是方法上。
-
Qualifier- 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用必需和 @Autowire一起使用。但是在给方法参数注入时可以独立使用。
- 属性:
value:用于指定注入bean的id。
-
@Resource- 作用:直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
- 属性: name:指定 bean 的 id。
-
@Value-
作用:注入基本数据类型和 String 类型数据的
-
属性: value:用于指定值。它可以使用spring中SpEL
SpEL 写法 :#
-
-
-
用于改变作用范围的 :
- 作用和在 bean 标签中使用
属性实现的功能是一样的 @Scope- 作用:用于指定bean的作用范围
- 属性:
value:指定范围的取值。常用取值:singleton prototype
- 作用和在 bean 标签中使用
-
是生命周期相关的 :
- 作用和在 bean标签中使用 init-metho 和 destory-method 作用一样
@PostConstruct- 作用:用于指定初始化方法
@PreDestroy- 作用 :用于指定销毁方法
| 注解 | 作用 |
|---|---|
@Component |
用于把当前类对象存入 spring 容器中 |
@Controller |
|
@Service |
|
@Repository |
一般用在持久层 |
@Autowired |
自动按照类型注入 |
@Qualifier |
在按照类中注入的基础上再按照名称注入 |
@Resource |
按照 bean 的 id 注入 , |
@Value |
注入基本数据类型和 String 类型数据 |
@Scope |
用于指定 bean 的作用范围 |
@PostConstruct |
用于指定初始化方法 |
@PreDestroy |
用于指定销毁方法 |
bean的作用域
scope="singleton"
scope="prototype"
scope="request"
scope="session"
- singleton 单例 :整个容器中只有一个对象实例
- prototype 原型 :每次获取bean都产生一个新的对象
- request :在每次请求时创建一个新的对象
- session :在会话的范围内是一个对象
bean的生命周期
单例对象 :
- 出生 : :当容器创建时对象出生
- 活着 :只要容器还在,对象一直存活
- 死亡 :容器销毁 ,对象消亡
- 总结 :单例对象的生命周期和容器相同
多例对象 :
- 出生 :当我们使用对象时 Spring 为我们创建
- 活着 :对象只是在使用过程中就一直活着
- 死亡 :当对象长时间不用 ,且没有别的对象引用时,由Java垃圾回收器回收
创建bean 对象的三种方式
1、通过构造器(有参无参)
<bean id="" class=""/>
2、通过静态工厂
<bean id="" class="工厂类" factory-method="静态工厂方法"/>
3、通过实例工厂方法(非静态方法)
<bean id="factory" class="工厂类"/> <bean id="" factory-bean="factory" factory-method="实例工厂方法"/>
自动装配
@Autowired
由 Spring 自己发现对应的 Bean ,自动完成装配工作的方式 。
使用 @Autowired ,这个时候 Spring 会根据类型去寻找定义的 Bean 然后将其注入 。
- 再次理解 :
@Autowired注解表示在 Spring IOC 定位所有的 Bean 后 ,再根据类型寻找资源 ,然后将其注入 - 过程 :定义 Bean ----> 初始化 Bean(扫描)---->根据属性需要从 SpringIOC容器中搜寻满足要求的 Bean ---->满足要求则注入
@Autowired的required 属性指定某个熟悉允许不被设置
context:component-scan :自动组件扫描
base-package :指定扫描的基础包 ,扫描基础包及子包所有加了注解的类 ,自动扫描进 容器中
动态代理
-
1、动态代理和静态代理的角色是一样的 。
-
2、动态代理的代理类是动态生成的 。
-
3、分为两类实现方式 :基于接口动态代理、基于类的动态代理
- a)、基于接口的动态代理 :jdk 动态代理
- b)、基于类的动态代理 :cglib
现在 Javasist 来生成动态代理 。
-
4、 jdk 动态代理 --- Proxy 类 和 InvocationHandler 接口
1、
InvocationHandler是代理实例的调用处理程序 实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的
invoke方法
| 方法类型 | 作用 |
|---|---|
Object |
invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。 |
| 方法参数 | |
| proxy | 调用方法的代理实例(代理类) |
| method | 代理实例上调用的接口方法的 Method 实例 |
| args | 代理实例上方法调用的参数值的对象数组 |
2、Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
| 方法类型 | 作用 |
|---|---|
static Object |
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 |
| 方法参数 | |
| loader | 代理类的类加载器 , |
| interfaces | 代理类要实现的接口列表 ,(被代理类的接口) |
| h | 指派方法调用的调用处理程序,(代理类的实例) |
JDK动态代理的一般实现步骤如下:
(1)创建一个实现InvocationHandler接口的类,它必须实现invoke方法
public class ProxyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {(2)创建被代理的类以及接口
interface Rent {}
(3)调用Proxy的静态方法newProxyInstance,创建一个代理类
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
(4)通过代理调用方法
Rent proxy = (Rent) proxyInvocationHandler.getProxy();
proxy.rent();
动态代理具体步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
public class ProxyInvocationHandler implements InvocationHandler {
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); // 利用反射调用类里面的实际方法 Object result = method.invoke(target , args); fare(); return result; }
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
Host host = new Host(); ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(); proxyInvocationHandler.setTarget(host); Rent proxy = (Rent) proxyInvocationHandler.getProxy(); proxy.rent();
测试,实现 InvocationHandler 接口
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成代理类
* */
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* proxy :代理类
* method :代理类的调用处理程序的方法对象
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
// 利用反射调用类里面的实际方法
Object result = method.invoke(target , args);
fare();
return result;
}
private void seeHouse() {
System.out.println("带租客看房");
}
private void fare() {
System.out.println("收取中介费");
}
}
测试类
public class Client {
@Test
public void test01() {
Host host = new Host();
System.out.println(host.getClass());
System.out.println(host.getClass().getInterfaces());
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(host);
Rent proxy = (Rent) proxyInvocationHandler.getProxy();
proxy.rent();
}
}
一个动态代理可用代理多个类
面向切面编程 —aop
1、aop :aspect oriented programming 面向切面彼岸
2、aop 在 Spring 中作用
- 提供声明式事务
- 允许用户实现自定义切面
面向切面编程【AOP模块】
AOP的一个思想:让关注点代码与业务代码分离
使用注解来开发 Spring AOP
-
🎃 第一步:选择连接点
Spring是方法级别的AOP框架,选择哪一个类的哪一个方法用以增强功能
package com.yuanwu.pojo; import org.springframework.stereotype.Component; /** * @Author YuanWu * @ClassName Landlord * @Date 2020/8/8 **/ @Component("landlord") public class Landlord { public void service(){ System.out.println("签合同"); System.out.println("收房租"); } }选择Landlord类中的service()方法作为连接点
配置xml
在bean.xml中配置自动注入,并告诉Spring IOC容器去哪里扫描这两个Bean:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.yuanwu.aspect"/> <context:component-scan base-package="com.yuanwu.pojo"/> <aop:aspectj-autoproxy/> </beans> -
🎃 第二步:创建切面
在Spring 中只需要使用 @Aspect 注解类,Spring IOC容器就会认为这是一个切面
package com.yuanwu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @Author YuanWu
* @ClassName Broker
* @Date 2020/8/8
**/
@Component
@Aspect
public class Broker {
@Before("execution(* com.yuanwu.pojo.Landlord.service())")
public void before(){
System.out.println("带租客看房子");
System.out.println("谈价格");
}
@After("execution(* com.yuanwu.pojo.Landlord.service())")
public void after(){
System.out.println("交钥匙");
}
}
- 这里需要注意一下: 被定义为切面的类仍然是一个Bean,需要
@Component注解标注
Spring 中的AspectJ注解:
| 注解 | 说明 |
|---|---|
@Before |
前置通知,在连接点方法前调用 |
@Around |
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法 |
@After |
后置通知,在连接点方法后调用 |
@AfterReturning |
返货通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常 |
@AfterThrowing |
异常通知,当连接点方法异常时调用 |
注解中间使用了定义切点的正则式,告诉SpringAOP需要拦截说明对象的说明方法
-
🎃第三步:定义切点
Spring通过这个正则表达式判断具体要拦截的是哪一个类的哪一个方法
execution(* com.yuanwu.pojo.Landlord.service())
- execution :代表执行方法的时候会触发
- ***** : 代表任意返回类型的反法
- com.yuanwu.pojo.Landlord : 代表全限定类名
- service() :被拦截的方法名称
可以使用 @Pointcut 注解来定义一个切点,来避免很多重复的表达式
package com.yuanwu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @Author YuanWu
* @ClassName Broker
* @Date 2020/8/8
**/
@Component
@Aspect
public class Broker {
@Pointcut("execution(* com.yuanwu.pojo.Landlord.service())")
public void lService(){
}
//@Before("execution(* com.yuanwu.pojo.Landlord.service())")
@Before("lService()")
public void before(){
System.out.println("带租客看房子");
System.out.println("谈价格");
}
//@After("execution(* com.yuanwu.pojo.Landlord.service())")
@After("lService()")
public void after(){
System.out.println("交钥匙");
}
}
-
🎃第四步:测试
-
带租客看房子 谈价格 签合同 收房租 交钥匙
🥨 环绕通知
Spring AOP 中最强大的通知,因为它集成了前置和后置通知,保留了连接点原有方法的功能
使用 @Around 注解来同时完成前置和后置通知
@Around("execution(* com.yuanwu.pojo.Landlord.service())")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("带租客看房子");
System.out.println("谈价格");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("交钥匙");
}
使用XML配置开发SpringIOC
| AOP 配置元素 | 用途 | 备注 |
|---|---|---|
aop:advisor |
定义 AOP 的通知 | 一种很古老的方式,很少使用 |
aop:aspect |
定义一个切面 | —— |
aop:before |
定义前置通知 | —— |
aop:after |
定义后置通知 | —— |
aop:around |
定义环绕通知 | —— |
aop:after-returning |
定义返回通知 | —— |
aop:after-throwing |
定义异常通知 | —— |
aop:config |
顶层的 AOP 配置元素 | AOP 的配置是以它为开始的 |
aop:declare-parents |
给通知引入新的额外接口,增强功能 | —— |
aop:pointcut |
定义切点 | —— |
<!-- 装配Bean -->
<bean name="productService" class="com.yuanwu.service.ProductService"/>
<bean name="loggerAspect" class="com.yuanwu.aspect.LoggerAspect" />
<!-- 配置AOP -->
<aop:config>
<!-- where: 在那些地方(包.类.方法)做增加-->
<aop:pointcut id="loggerCutpoint" expression="execution(* com.yuanwu.service.ProductService.*(..))"/>
<!-- what: 做什么增强-->
<aop:aspect id="logAspect" ref="loggerAspect">
<!-- when: 在什么时机(方法前/方法后)-->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
在Spring 中使用 AspectJ注解支持
1、导入jar 包
头文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="包名"></context:component-scan>
<!-- 基于注解使用AspectJ 主要作用是为切面中通知能作用到目标类生成代理-->
<aop:aspectJ-autoproxy/>
</beans>
定义一个日志切面类
前置通知
目标方法执行之前执行
@Component //标识为一个组件
@Aspect //标识为一个切面
public class LoggingAspect{
@Before("execution(public int 全类名.方法名(参数类型,参数类型))")
public void beforeMethod(){
System.out.println("日志记录");
}
}
后置通知
目标方法执行之后执行;不管目标方法有没有抛出异常都会执行,不能获取方法的结果
execution(* com.yuanwu.anntion.类名.*(..))
*:任意修饰符
*:任意类
*:任意方法
..:任意参数
获取方法名字
连接点对象 Joinpoint
getSignature()===》方法签名
public void afterMethod(Joinpoint joinpoinr){
String methodName = joinpoint.getSignature().getName();
System.out.println("日志记录");
}
@After("execution(* 全限定类名.*(..))")
public void afterMethod(){
System.out.println("日志记录");
}
返回通知
在目标方法正常执行结束后执行;可以获取到返回值
@AfterReturning(value="execution(* 全限定类名.*(..))" ,returning="result")
//指定名字必须与参数名一致
public void afterRetueningMethod(Object result){
System.out.println("日志记录" + result);
}
异常通知
在目标方法抛出异常后执行
可以通过形参异常的类型,去设置抛出指定异常,才执行异常通知;比如指定空指针异常,才会执行异常通知
public void afterThrowingMethod(NullPointerExecption EX){
System.out.println("日志记录" + EX);
}
@AfterThrowing(value="execution(* 全限定类名.*(..))" , throwing="EX")
//指定名字必须与参数名一致
public void afterThrowingMethod(Execption EX){
System.out.println("日志记录" + EX);
}
环绕通知
环绕目标方法执行,可以理解为:前置后置返回异常,四个通知的组合体,更像是动态代理的整个过程
@Around("execution(* 全限定类名.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjp){
try{
//前置通知
...
//执行目标方法
pjp.proceed();
//返回通知
...
} catch(Throwable e){
//异常通知
...
e.printStackTrace();
}finally {
//后置通知
...
}
return null;
}
@Oreder();
优先级
重用切入点表达式
@Pointcut("execution(* com.atguigu.spring.aop.impl.Cacl.*(int,int))")
public void declareJoinPoint(){}
@After("declareJoinPoint()")
public void after(JoinPoint joinPoint){
补充
jdbcTemplate
jdbctemplate 对 JDBC 简单的封装
QueryRunner 对JDBC 的简单封装
Spring 事务管理
事务的概念:
以转账为例,必定要有两个操作:先将A账户的金额减去要转账的数目,然后将B账户加上响应的金额数目。这两个操作必定要全部完成,才表示当前转账操作成功。若有任何一方失败,则另一方必须回滚(即全部失败)。即事务:这组操作是不可分割的,要么全部成功,要么全部失败。
所谓事务管理,就是“按照给定的事务规则来执行提交或者回滚操作”;
事务的特性:
具有ACID四个特性:
原子性(Atomicity):事务是不可分割的工作单位,要么都发生,要么都不发生。
一致性(Consistency):事务在完成后数据的完整性必须保持一致。
隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间的数据要相互隔离;
持久性(Durability):一个事务被提交,它对数据库数据的改变应该是永久性的,即使数据库发生故障也不应该对其有任何影响;
Spring 事务管理接口:
三个高层抽象接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
1、PlatformTransactionManager事务管理器
org.springframework.transaction.PlatformTransactionManager 事务管理器接口;事实上,Spring 框架并不直接管理事务,而是通过这个接口为不同的持久层框架提供了不同的PlatformTransactionManager接口实现类,将事务管理的职责 委托给Hibernate 或者iBatis等持久化框架的事务来实现;
PlatformTransactionManager源码:
public interface PlatformTransactionManager {
//事务管理器通过TransactionStatus获得"事务状态",从而管理事务
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
//根据状态提交
void commit(TransactionStatus status) throws TransactionException;
//根据状态回滚
void rollback(TransactionStatus status) throws TransactionException;
}
在使用JDBCC或者MyBatis进行数据持久化操作时,xml配置通常:
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
2、TransactionDefinition 定义事务基本属性
包: org.springframework.transaction.TransactionDefinition ;用于定义一个事务。
它定义了 Spring 事务管理的五大属性:隔离级别、传播行为、是否只读、事务超时、回滚规则
2.1、隔离级别
隔离级别是用来描述并发事务之间隔离程度的大小;
脏读: 一个事务读到了另一个事务的未提交的数据;
不可重复读: 一个事务读到了另一个事务已经提交的 update 的数据,导致多次查询结果不一致;
幻读: 一个事务读到了另一个事务已经提交的 insert 的数据,导致多次查询结果不一致;
Spring 事务管理定义的隔离级别::
ISOLATION_DEFAULT:使用数据库默认的隔离级别;
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取已改变而没有提交的数据,可能会导致脏读、幻读或不可重复读;
ISOLATION_READ_COMMITTED:允许读取事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生;
ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据事务本身改变,可以阻止脏读和不可重复读,但幻读仍有可能发生;
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保不发生脏读、不可重复读、以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的;
2.2、传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

Spring 事务管理传播机制规定了事务方法和事务方法 发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间发生调用时的事务上下文的规则;
Spring 定义了七种传播行为:方法之间发生嵌套调用时如何传播事务?
PROPAGATION_REQUIRED: A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务;
PROPAGATION_SUPPORTS: A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行;
PROPAGATION_MANDATORY: A如果有事务,B将使用该事务;如果A没有事务,B将抛出异常;
PROPAGATION_REQUIRES_NEW: A如果有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务;
PROPAGATION_NOT_SUPPORTED: A如果有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行;
PROPAGATION_NEVER:A如果有事务,B将抛出异常;A如果没有事务,B将以非事务执行;
PROPAGATION_NESTED:A和B底层采用保存点机制,形成嵌套事务;
2.3、是否只读
如果将事务设置为只读,表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务;
2.4、事务超时
事务超时就是事务的一个定时器,在特定的时间内事务如果没有执行完毕,就会自动回滚,而不是一致等待其结束。在TransactionDefinition 中以 int 的值表示超时时机,默认值是 -1 ,单位秒
2.5、回滚规则
回滚规则定义了哪些异常会导致事务回滚或哪些不会。默认情况下,事务只有遇到运行期异常才会回滚;
3、TransactionStatus事务状态
org.springframework.transaction.TransactionStatus 接口是用来记录事务的状态,用来获取或判断事务的相应状态信息:
TransactionStatus源码:
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction(); //是否是新的事务
boolean hasSavepoint(); //是否有回复点
void setRollbackOnly(); //设置为只回滚
boolean isRollbackOnly(); //是否为只回滚
void flush(); //刷新
boolean isCompleted(); //是否已完成
}
Spring 事务管理实现方式
Spring 事务管理有两种方式:编程式事务管理;声明式事务管理;
编程式事务管理:通过TransactionTemplate 手动管理事务;
声明式事务管理:基于TransactionProxyFactoryBean的方式、基于Aspect的XML方式、基于注解(@Transactional)的方式;代码侵入性最小,实际是通过AOP实现的;(推荐使用)
配置事务管理器
数据源 让 事务管理器 进行管理
ref="dataSource" ==>>引用数据源 id名字
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transactionManager-manager="dataSourceTransactionManager"/>
xml方式配置事务
<!-- 基于xml配置事务管理, -->
<tx:advice transaction-manager="dataSourceTransactionManager" id="toAdvice">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 具体方法使用的事务属性 -->
<tx:method name="方法名" ...事务属性/>
<!-- 约定方法的名字 -->
<!-- select开头的方法 -->
<tx:method name="select*"/>
<!-- 上述指定之外的所有方法 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="executiom(* 全限定类名.*.*(..))" id="toPointCut"/>
<aop:advisor advice-ref="toAdvice" pointcut-ref="toPointCut"/>
</aop:config>
注解方式配置事务
1、开启 spring 对注解的支持
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置扫描包路径 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 配置jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring_01"></property>
<property name="username" value="root"></property>
<property name="password" value="abc123"></property>
</bean>
<!-- 1. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.开启spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
2、在需要事务支持的方法或类上使用 @Transactional 注解
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class AccountServiceImpl implements AccountService {
- propagation: 指定事务的传播级别
- isolation:指定事务隔离级别
- timeout: 指定事务的超时时间
- readOnly: 是否只读
- rollbackFor:异常回滚
3、抽离jdbcTemplate 【可选】
如果使用注解开发 ,需要自己抽离 jdbcTemplate
<!-- 配置jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="引用数据源"></property>
</bean>
更多Spring事务基于注解参考
https://blog.csdn.net/java_green_hand0909/article/details/78602920
Spring 整合 MyBatis 未完待续...
1、导包
spring核心包 、aopalliance.jar、aspectjweaver.jar、mybatis核心包、mybatis-spring

SSM整合思想
什么是SSM ?
Spring + SpringMVC + MyBatis 三个开源框架组成的框架集 ;
Spring 是一个轻量级(在系统初始化的时候不用加载所有的)的控制反转(IOC)和面向切面编程(AOP)的容器 ;
- 何为IOC ?
- 将主动创建资源变为被动接收资源
- 比如 :new ,将主动 new 变为被动接收 ,
SpringMVC 分离了控制器、模型对象、分派器以及处理程序对象的角色 ;
MyBatis :支持普通 SQL 查询 ,SQL映射 持久层框架;
将各自的对象讲个各自容器来管理
1、SpringMVC 是视图层(UI) 框架 ,将视图使用的对象交给 SpringMVC 容器管理,放在其配置文件中 。
- 1)、处理器对象 (controller)
- 2)、注册组件扫描器 <context:component-scan base-package="controller 注解所在包名"/>
- 3)、注册视图解析器 :IntemalResourceViewResolver ,配置前缀和后缀
- 4)、注册注解驱动 :<mvc:anntation-driven /> ;
- 5)、注册处理器的映射器 ,注册处理器的适配器 (可选)
2、Spring 管理业务层和持久层对象(Service 和 Dao),这些对象放在 Spring 的配置文件中 ,交给 Spring 的容器管理
- 1)、Service 对象交给 Spring ,使用
@Service注解 ;
<context:component-scan base-package="Service 注解所在类的全类名"/>
- 2)、Dao 对象交给 Spring ,MyBatis 对象交给 Spring 。
- a)、数据源 DataSource :c3p0 ,dbcp ;
- b)、注册 SqlSessionFactoryBean ,目的是创建 SqlSessionFactory ;
- c)、注册动态代理扫描器 ,目的是创建Dao 接口的动态代理对象 ,即Dao 层的对象 ;【Mapper接口】
c 步骤详解 : https://blog.csdn.net/Edison_03/article/details/72796691?utm_source=blogxgwz4
- 3)、把事务管理交给 Spring 。
- a)、使用注解处理事务 ;
- b)、使用 AspectJ 的AOP 在配置文件中管理事务 。
Spring 容器和 SpringMVC 容器的关系 :
Spring 是 SpringMVC 的父容器 。
在 SpringMVC容器(子容器)中可以知道Spring容器(父容器)的存在 ,子容器可以访问父容器 ,而父容器不能访问子容器 。
SSM整合的配置文件 :
1)、SpringMVC 的配置文件 ,文件名是自定义的 ,dispatcherServlet.xml 。
2)、Spring 的配置文件 ,文件名是自定义的 ,applicationContext.xml 。
3)、MyBatis 的主配置文件 ,配置别名和SQL映射文件的位置 ;
SQL映射文件 ,编写 SQL 语句 。
4)、数据库属性配置文件 ,dbconf.properties 。
5)、web.xml
- a)、注册 SpringMVC 的中央调度器 :作用是接收请求 ,在启动的时候创建 SpringMVC 容器 ,读取 SpringMVC配置文件
- b)、注册 Spring 监听器 ContextLoadListener ,在启动的时候创建 Spring 容器 ,读取 Spring 配置文件 。
- b)、注册字符集过滤器 ,解决 POST 请求的乱码问题 。
开发步骤 :
- 重要 导 jar 包
1、建表
2、创建动态web项目
3、建包 :实体类 ,Service ,Controller ...... 工具包
4、写配置文件
5、编写 第三步骤 的代码 (逻辑代码)
6、定义请求页面
7、定义处理结果页面
8、定义事务
.............

浙公网安备 33010602011771号