学习教程框架SpringSecurity入门
sinarcsinxSSM整合
SSM整合
步骤分析
步骤
①Spring整合上Mybatis
通过Service层Dao层都注入Spring容器中
②引入配置SpringMVC
把Controller层注入SpringMVC容器中
③让web项目启动时自动读取Spring配置文件来创建Spring容器
可以使用ContextLoaderListener来实现Spring容器的创建
常见疑惑
为什么要用两个容器?
因为Controller如果不放在MVC容器中会没有效果,无法处理请求。而Service如果不放在Spring容器中,声明式事务也无法使用
SpringMVC容器中的Controller需要依赖Service,能从Spring容器中获取到所依赖的Service对象嘛?
Spring容器相当于是父容器,MVC容器相当于是子容器。子容器除了可以使用自己容器中的对象外还可以使用父容器中的对象
是如何实现这样父子容器的关系的?
具体可以看源码解析阶段的视频。但是我们目前可以用代码模拟下
是什么时候让两个容器产生这种父子容器的关系的?
在ContextLoaderListener中,会在创建好容器后把容器存入servletContext域,这样在DispatcherServlet启动时,创建完SpringMVC容器后会从servletContext域中获取到Spring容器对象,设置为其父容器,这样子容器就能获取到父容器中的bean了
准备工作
引入所有依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency>
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.9.RELEASE</version> </dependency>
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency>
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency>
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.0.0</version> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
<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>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
<dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency>
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
|
数据库初始化语句
1 2 3 4 5 6 7 8 9 10 11
| CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis_db` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `mybatis_db`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, `age` int(11) DEFAULT NULL, `address` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; insert into `user`(`id`,`username`,`age`,`address`) values (1,'UZI',19,'上海'),(2,'PDD',25,'上海');
|
相关配置
①整合Spring和Mybatis
在resources目录下创建Spring核心配置文件: applicationContext.xml 内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <?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" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.sinarcsinx"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter> </context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driver}"></property> </bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactory"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> </bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer"> <property name="basePackage" value="com.sinarcsinx.dao"></property> </bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
|
在resources目录下创建jdbc.properties 文件,内容如下:
1 2 3 4
| jdbc.url=jdbc:mysql://localhost:3306/mybatis_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.username=root jdbc.password=123456
|
在resources目录下创建mybatis-config.xml ,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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="logImpl" value="LOG4J"/> </settings> <typeAliases> <package name="com.sinarcsinx.domain"></package> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> </plugin> </plugins> </configuration>
|
在resources目录下创建log4j.properties ,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=c:/mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=debug, stdout
|
②SpringMVC引入
在resources目录下创建spring-mvc.xml ,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <?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.sinarcsinx.controller"/> <mvc:default-servlet-handler/> <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>
</beans>
|
修改web.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <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:spring-mvc.xml</param-value> </init-param>
<load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> </servlet-mapping>
<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>
|
③Spring整合入web项目
让web项目启动的时候就能够创建Spring容器,可以使用Spring提供的监听器ContextLoaderListener,所以我们需要再web.xml中配置这个监听器,并且配置上Spring配置文件的路径
1 2 3 4 5 6 7 8 9
| <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
|
编写Controller,Service,Dao
我们来编写根据id查询用户的接口来进行测试
1 2 3 4 5 6 7 8 9 10 11 12
| @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; } }
|
1 2 3
| public interface UserService { User findById(Integer id); }
|
1 2 3 4 5 6 7 8 9
| @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao;
public User findById(Integer id) { return userDao.findById(id); } }
|
1 2 3 4 5 6 7 8
| public interface UserDao {
User findById(Integer id); }
|
1 2 3 4 5 6 7 8
| <?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.sinarcsinx.dao.UserDao">
<select id="findById" resultType="com.sinarcsinx.domain.User"> select * from user where id = #{id} </select> </mapper>
|
案例
响应格式统一
我们要保证一个项目中所有接口返回的数据格式的统一,这样无论是前端还是移动端开发获取到我们的数据后都能更方便的进行统一处理
所以我们定义以下结果封装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @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; } }
|
之前的接口修改为 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestController public class UserController {
@Autowired private UserService userService;
@GetMapping("/user/{id}") public ResponseResult findById(@PathVariable("id") Integer id){ User user = userService.findById(id); if(user==null){ return new ResponseResult(500,"没有该用户"); } return new ResponseResult(200,"操作成功",user); } }
|
查询所有用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @RestController public class UserController {
@Autowired private UserService userService;
@GetMapping("/user") public ResponseResult findAll(){ List<User> list = userService.findAll(); return new ResponseResult(200,"操作成功",list); }
}
|
1 2 3 4 5
| public interface UserService { User findById(Integer id);
List<User> findAll(); }
|
1 2 3 4 5 6 7 8 9 10
| @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao;
public List<User> findAll() { return userDao.findAll(); } }
|
1 2 3 4 5
| public interface UserDao { List<User> findAll(); }
|
1 2 3 4 5 6 7
| <?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.sinarcsinx.dao.UserDao"> <select id="findAll" resultType="com.sinarcsinx.domain.User"> select * from user </select> </mapper>
|
分页查询用户
分页查询的结果除了要包含查到的用户数据外还要有当前页数,每页条数,总记录数这些分页数据
分页数据封装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| 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; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| @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); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @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; } }
|
插入用户
Controller层
1 2 3 4 5
| @PostMapping("/user") public ResponseResult insertUser(@RequestBody User user){ userService.insertUser(user); return new ResponseResult(200,"操作成功"); }
|
Service层
在Service接口中增加方法定义
1
| void insertUser(User user);
|
实现类中实现该方法:
1 2 3
| public void insertUser(User user) { userDao.insertUser(user); }
|
Dao层
在接口中定义方法
1
| void insertUser(User user);
|
在mapper映射文件中
1 2 3
| <insert id="insertUser"> insert into user values(null,#{username},#{age},#{address}) </insert>
|
测试
1
| {"username":"sin","age":22,"address":"宁波"}
|
删除用户
Controller层
1 2 3 4 5
| @DeleteMapping("/user/{id}") public ResponseResult deleteUser(@PathVariable("id") Integer id){ userService.deleteUser(id); return new ResponseResult(200,"操作成功"); }
|
Service层
在Service接口中增加方法定义
1
| void deleteUser(Integer id);
|
实现类中实现该方法:
1 2 3
| public void deleteUser(Integer id) { userDao.deleteUser(id); }
|
Dao层
在接口中定义方法
1
| void deleteUser(Integer id);
|
在mapper映射文件中
1 2 3
| <delete id="deleteUser"> delete from user where id = #{id} </delete>
|
更新用户
Controller层
1 2 3 4 5
| @PutMapping("/user") public ResponseResult updateUser(@RequestBody User user){ userService.updateUser(user); return new ResponseResult(200,"操作成功"); }
|
Service层
在Service接口中增加方法定义
1
| void updateUser(User user);
|
实现类中实现该方法:
1 2 3
| public void updateUser(User user) { userDao.updateUser(user); }
|
Dao层
在接口中定义方法
1
| void updateUser(User user);
|
在mapper映射文件中
1 2 3
| <update id="updateUser"> update user set username = #{username},age = #{age},address = #{address} where id = #{id} </update>
|
异常统一处理
我们可以使用@ControllerAdvice实现对异常的统一处理,让异常出现时也能返回响应一个JSON
代码如下:
1 2 3 4 5 6 7 8 9
| @ControllerAdvice public class SINControllerAdvice {
@ExceptionHandler(Exception.class) @ResponseBody public ResponseResult handleException(Exception e){ return new ResponseResult(500,e.getMessage()); } }
|
拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class SINHandlerInterceptor 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"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <bean class="com.sinarcsinx.interceptor.SINHandlerInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
|
声明式事务
已经做好了相应的配置,只要在service方法上加上注解即可
1 2 3 4 5 6
| @Transactional public void test() { userDao.insertUser(new User(null,"test1",11,"cc"));
userDao.insertUser(new User(null,"test2",12,"cc2")); }
|
AOP
注意,自己去使用AOP进行增强时,应该是对Service进行增强,不能对Controller进行增强
因为切面类会被放入父容器,而我们的Controller是在子容器中的,父容器不能访问子容器
并且我们如果需要对Controller进行增强,使用拦截器也会更加的好用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Aspect @Component public class SINAspect { @Pointcut("execution(* com.sinarcsinx.service..*.*(..))") public void pt(){
} @Before("pt()") public void before(){ System.out.println("before"); } }
|