目录
一、SSM整合
1.1 步骤分析
①Spring整合上Mybatis
把Service层Dao层都注入Spring容器中
②引入配置SpringMVC
把Controller层注入SpringMVC容器中
③让web项目启动时自动读取Spring配置文件来创建Spring容器
可以使用ContextLoaderListener来实现Spring容器的创建。
1.2 常见疑惑
-
为什么要用两个容器?
因为Controller如果不放在MVC容器中会没有效果,无法处理请求。而Service如果不放在Spring容器中,声明式事务也无法使用。 -
SpringMVC容器中的Controller需要依赖Service,能从Spring容器中获取到所依赖的Service对象嘛?
Spring容器相当于是父容器,MVC容器相当于是子容器。子容器除了可以使用自己容器中的对象外还可以使用父容器中的对象。 -
是如何实现这样父子容器的关系的?
具体可以看源码解析阶段的视频。但是我们目前可以用代码模拟下。 -
是什么时候让两个容器产生这种父子容器的关系的?
在ContextLoaderListener中,会在创建好容器后把容器存入servletContext域。这样在DispatcherServlet启动时,创建完SpringMVC容器后会从servletContext域中获取到Spring容器对象,设置为其父容器,这样子容器就能获取到父容器中的bean了。详情请见源码解析视频。
1.3 准备工作
引入所有依赖
<!--Spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--AOP相关依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- mybatis整合到Spring的整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--log4j依赖,打印mybatis日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--分页查询,pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- spring整合junit的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--springmvc的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- jackson,帮助进行json转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!--commons文件上传,如果需要文件上传功能,需要添加本依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Spring5和Thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
1.4 相关配置
①整合Spring和Mybatis
在resources目录下创建Spring核心配置文件: applicationContext.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--扫描组件(除控制层)-->
<context:component-scan base-package="com.lx">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入jdbc.properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
开启事务的注解驱动
将使用注解@Transactional标识的方法或类中所有的方法进行事务管理
-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--配置SqlSessionFactoryBean,可以直接在Spring的IOC中获取SqlSessionFactory-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--设置MyBatis的核心配置文件的路径-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--设置数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--设置类型别名所对应的包-->
<property name="typeAliasesPackage" value="com.lx.pojo"></property>
<!--设置映射文件的路径,只有映射文件的包和mapper接口的包不-致时需要设置-->
<!-- <property name="mapperlocations" value="classpath:mappers/*.xml"></property>-->
</bean>
<!--开启aop注解支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--配置mapper接口的扫描,可以将指定包下所有的mapper接口,通过sqlSession创建代理实现类对象,并将这些对象交给IOC容器管理-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lx.mapper"></property>
</bean>
</beans>
在resources目录下创建jdbc.properties 文件,内容如下:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_db?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
在resources目录下创建mybatis-config.xml ,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--将下划线映射为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
在resources目录下创建log4j.xml,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
②SpringMVC配置文件
在resources目录下创建springmvc.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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描控制层组件-->
<context:component-scan base-package="com.lx.controller"></context:component-scan>
<!--配置Thymeleaf视图解析器,前后端不分离项目使用-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
<!--配置默认的servlet处理静态资源-->
<mvc:default-servlet-handler />
<!--开启mvc注解驱动,解决响应乱码-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--配置视图控制器 前后端不分离项目使用-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!--配置文件上传解析器 前后端不分离项目使用-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
</beans>
③Spring整合入web项目,配置web,xml
~~~~~~ 让web项目启动的时候就能够创建Spring容器。可以使用Spring提供的监听器ContextLoaderListener,所以我们需要再web.xml中配置这个监听器,并且配置上Spring配置文件的路径。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置spring的编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置处理请求方式的过滤器,前后端不分离使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置springmvc的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--将DispatcherServlet的初始化时间提前到服务器启动时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置Spring的监听器,在服务器启动时加载Spring的配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--设置Spring配置文件自定义的位置和名称-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
</web-app>
1.5 编写Controller,Service,Dao
我们来编写根据id查询用户的接口来进行测试
Controller层
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Integer id){
User user = userService.findById(id);
return user;
}
}
Service层
public interface UserService {
User findById(Integer id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public User findById(Integer id) {
return userDao.findById(id);
}
}
Dao层
public interface UserDao {
/**
* 根据id查询用户
* @param id
* @return
*/
User findById(Integer id);
}
mapper.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.lx.dao.UserDao">
<select id="findById" resultType="com.lx.domain.User">
select * from user where id = #{id}
</select>
</mapper>
二、案例
2.1 响应格式统一
~~~~~~ 我们要保证一个项目中所有接口返回的数据格式的统一。这样无论是前端还是移动端开发获取到我们的数据后都能更方便的进行统一处理。
~~~~~~ 所以我们定义以下结果封装类
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
/**
* 状态码
*/
private Integer code;
/**
* 提示信息,如果有错误时,前端可以获取该字段进行提示
*/
private String msg;
/**
* 查询到的结果数据,
*/
private T data;
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
2.2 查询所有用户
@RestController
public class UserController {
@Autowired
private UserService userService;
//省略其他无关代码
@GetMapping("/user")
public ResponseResult findAll(){
List<User> list = userService.findAll();
return new ResponseResult(200,"操作成功",list);
}
}
public interface UserService {
User findById(Integer id);
List<User> findAll();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
//省略其他无关代码
public List<User> findAll() {
return userDao.findAll();
}
}
public interface UserDao {
//省略其他无关代码
List<User> findAll();
}
<?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.lx.dao.UserDao">
<select id="findAll" resultType="com.lx.domain.User">
select * from user
</select>
</mapper>
2.3 分页查询用户
~~~~~~ 分页查询的结果除了要包含查到的用户数据外还要有当前页数,每页条数,总记录数这些分页数据。
分页数据封装类
public class PageResult<T> {
private Integer currentPage;
private Integer pageSize;
private Integer total;
private List<T> data;
public PageResult(Integer currentPage, Integer pageSize, Integer total, List<T> data) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.total = total;
this.data = data;
}
public Integer getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Integer currentPage) {
this.currentPage = currentPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{pageSize}/{pageNum}")
public ResponseResult findByPage(@PathVariable("pageSize") Integer pageSize,@PathVariable("pageNum") Integer pageNum){
PageResult pageResult = userService.findByPage(pageSize,pageNum);
return new ResponseResult(200,"操作成功",pageResult);
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public PageResult findByPage(Integer pageSize, Integer pageNum) {
PageHelper.startPage(pageNum,pageSize);
List<User> list = userDao.findAll();
PageInfo pageInfo = new PageInfo(list);
PageResult pageResult = new PageResult(pageInfo.getPageNum(),pageInfo.getPageSize(), (int) pageInfo.getTotal(),list);
return pageResult;
}
}
2.4 插入用户
@PostMapping("/user")
public ResponseResult insertUser(@RequestBody User user){
userService.insertUser(user);
return new ResponseResult(200,"操作成功");
}
service层
void insertUser(User user);
public void insertUser(User user) {
userDao.insertUser(user);
}
Dao层
void insertUser(User user);
在mapper映射文件中
<insert id="insertUser">
insert into user values(null,#{username},#{age},#{address})
</insert>
2.5 删除用户
Controller层
@DeleteMapping("/user/{id}")
public ResponseResult deleteUser(@PathVariable("id") Integer id){
userService.deleteUser(id);
return new ResponseResult(200,"操作成功");
}
Service层
在Service接口中增加方法定义
void deleteUser(Integer id);
实现类中实现该方法:
public void deleteUser(Integer id) {
userDao.deleteUser(id);
}
Dao层
在接口中定义方法
void deleteUser(Integer id);
在mapper映射文件中
<delete id="deleteUser">
delete from user where id = #{id}
</delete>
2.6 更新用户
Controller层
@PutMapping("/user")
public ResponseResult updateUser(@RequestBody User user){
userService.updateUser(user);
return new ResponseResult(200,"操作成功");
}
Service层
在Service接口中增加方法定义
void updateUser(User user);
实现类中实现该方法:
public void updateUser(User user) {
userDao.updateUser(user);
}
Dao层
在接口中定义方法
void updateUser(User user);
在mapper映射文件中
<update id="updateUser">
update user set username = #{username},age = #{age},address = #{address} where id = #{id}
</update>
三、异常统一处理
我们可以使用@ControllerAdvice实现对异常的统一处理。让异常出现时也能返回响应一个JSON。
代码如下:
@ControllerAdvice
public class SGControllerAdvice {
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseResult handleException(Exception e){
return new ResponseResult(500,e.getMessage());
}
}
四、拦截器
public class MyHandlerInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
springmvc.xml
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--
-->
<mvc:mapping path="/**"/>
<!--配置排除拦截的路径-->
<!--<mvc:exclude-mapping path="/"/>-->
<!--配置拦截器对象注入容器-->
<bean class="com.lx.interceptor.MyHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
五、声明式事务
已经做好了相应的配置,只要在service方法上加上注解即可
@Transactional
public void test() {
userDao.insertUser(new User(null,"test1",11,"cc"));
// System.out.println(1/0);
userDao.insertUser(new User(null,"test2",12,"cc2"));
}
六、AOP
~~~~~~ 注意,自己去使用AOP进行增强时,应该是对Service进行增强。不能对Controller进行增强,因为切面类会被放入父容器,而我们的Controller是在子容器中的。父容器不能访问子容器。
~~~~~~ 并且我们如果需要对Controller进行增强,使用拦截器也会更加的好用。
@Aspect
@Component
public class MyAspect {
//定义切点
@Pointcut("execution(* com.sangeng.service..*.*(..))")
public void pt(){
}
//进行增强
@Before("pt()")
public void before(){
System.out.println("before");
}
}