概述
Spring框架的核心IoC(DI)和AOP的容器框架,最主要的目的就是帮我们管理Bean的生命周期(实例化和初始化操作),Spring的这种特性可以跟很多的框架进行整合,但是Spring MVC框架本身就隶属于Spring框架,他们两个之间不存在整合关系,只是在扫描注解的时候产生了重叠(@Controller @Service @Repository @Component @Configuration
)
MyBatis过程:
- 解析配置文件,生成配置
//1.指定核心配置文件位置
String path = "config/mybatis-config.xml";
//2.读取核心配置文件内容
InputStream in = Resources.getResourceAsStream(path);
- 根据配置,创建SqlSessionFactory对象(需要数据源)
//3.建立SqlSession工厂(生产SqlSession对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
- 得到一个真正可用的SqlSession对象,
//4.获取SqlSession对象(主要作用:连接数据库,执行数据库操作)
SqlSession session = factory.openSession();
- 在通过接口和映射文件在创建接口的实现类对象,
//5.可变:动态实例接口回调
/**底层相当根据映射文件构建:StudentMapper studentMapper = new StudentMapperImpl()*/
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
通过描述,创建对象的过程必须交给Spring容器
在以后整合其他框架的时候,过程是一样的,其他框架对象实例化和初始化操作交给Spring容器
1.Spring和SpringMVC的整合
springmvc和spring都是容器,容器就是管理对象的地方(如Tomcat管理servlet对象),而springMVC容器和spring容器,就是管理bean对象的地方.
说的直白点,springmvc就是管理controller对象的容器,spring就是管理service和dao的容器
添加SpringMVC的依赖(隶属于Spring),因为我们没有跟WEB服务器关联,所以需要添加Servlet API,
返回JSON数据的时候,简单需要一个JSON的转换器
使用首先添加四个依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope><!-- 提供作用范围,不会发布到webapps下 -->
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
@EnableWebMvc
是使用Java 注解快捷配置Spring Webmvc
的一个注解。在使用该注解后配置一个继承于WebMvcConfigurerAdapter
的配置类即可配置好Spring Webmvc。@ControllerAdvice
是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:- 全局异常处理
- 全局数据绑定
- 全局数据预处理
@RestControlle
官网解释:
This code uses Spring 4’s new @RestController annotation, which marks the class as a controller where every method returns a domain object instead of a view. It’s shorthand for @Controller and @ResponseBody rolled together.
此代码使用 Spring 4 的新 @RestController 注释,它将类标记为控制器,其中每个方法返回域对象而不是视图。它是 @Controller 和 @ResponseBody 组合在一起的简写。
所以可简单理解其为:
@RestController=@Controller+@ResponseBody
@ControllerAdvice
,@RestControllerAdvice
:异常得到期望的返回格式use-default-filters
属性的默认值为true
,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有@Service
,@Controller
和@Repository
的注解的类进行扫描,设为false
后不使用默认规则includeFilters
:只对指定注解类扫描excludeFilters
:对指定注解类不扫描
当两个配置类扫描包的路径有重合的时候,可以用上述属性方法来配置:
WebConfig:
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
/*设置为只扫描下列指定四个注解*/
@ComponentScan(basePackages = "com.framework.**.web",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class}),
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {RestController.class, RestControllerAdvice.class})
},useDefaultFilters = false)
public class WebConfig {
}
SpringConfig:
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Configuration
/*扫描注解的包类路径不重合,设置不扫描下列指定四个注解:*/
@ComponentScan(basePackages = "com.framework",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class}),
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {RestController.class, RestControllerAdvice.class})
},useDefaultFilters = true)
public class SpringConfig {
}
2.Spring和MyBatis的整合
pom.xml配置
因为Spring5默认不再执行log4j,那么就暂时不配置日志框架,MyBatis需要数据源、事务管理、加载映射文件、创建接口的实现类对象,统一都需要交给Spring容器管理,MyBatis只是剩下默认配置
<settings>
或者<typeAlias>
等一些特殊标签,所以引入Mybatis框架包
<!-- MyBatis框架包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
MyBatis没有提供可以直接创建SqlSessionFactory
对象的方法,那么MyBatis和Spring整合需要第三方的实现类,创SqlSessionFactory
类型的对象
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
附录代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yue.ssm</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>ssm Maven Webapp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!--jackson支持-->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
<!-- MyBatis框架包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--创建`SqlSessionFactory`对象的第三方实现类-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--连接池-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope><!-- 提供作用范围,不会发布到webapps下 -->
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>ssm</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.16.v20140903</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds><!--每隔10秒自动扫描一次class文件,如果被修改==>自动重启-->
<webApp>
<contextPath>/ssm</contextPath>
</webApp><!--发布路径-->
<stopKey/>
<stopPort/>
</configuration>
</plugin>
</plugins>
</build>
</project>
SpringConfig配置过程
1. 建立数据源
/**
* 1.建立数据源
* @return
*/
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/yue_mybatis");
druidDataSource.setUsername("root");
druidDataSource.setPassword("");
return druidDataSource;
}
2. 创建SqlSessionFactory接口的实现类对象
/**
* 2.创建SqlSessionFactory接口的实现类对象
* @param dataSource
* @return
*/
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
//读取Mybatis的本地核心配置文件
sqlSessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
//加载映射文件,文件目录为resources下面
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
//读取以Mapper结尾的映射文件
Resource[] mapperLocationResource = resourcePatternResolver.getResources("classpath:mapper/*Mapper.xml");
//存储映射文件
sqlSessionFactory.setMapperLocations(mapperLocationResource);
//注入数据源
sqlSessionFactory.setDataSource(dataSource);
return sqlSessionFactory;
}
//加载映射文件,文件目录为resources下面 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); //读取以Mapper结尾的映射文件 Resource[] mapperLocationResource = resourcePatternResolver.getResources("classpath:mapper/*Mapper.xml");
相当于
<mappers> <mapper class="com.yue.mapper.StudentMapper"/>
所以此时配置文件中便可取消加载映射文件的配置
3. 接口的动态实例化
/**
* 3.接口的动态实例化
* 以前是这样写的:sqlSession.getMapper(StudentMapper.class)
* @return
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
//定义接口位置
configurer.setBasePackage("com.framework.**.mapper");
//需要由SqlSessionFactoryBean创建的SqlSession
configurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
//如果不同的包中出现了相同的接口,那么必须给捷库设置别名
configurer.setAnnotationClass(Repository.class);//接口上必须定义该注解
return configurer;
}
4. 配置事务管理器相关
- 启动aop注解和事务注解:
@EnableAspectJAutoProxy(proxyTargetClass = true)//启动AOP的代理(注解)
@EnableTransactionManagement(proxyTargetClass = true)//启动事务注解
4.1创建事务管理器
/**
* 4.1创建事务管理器
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
4.2告知哪些方法被事务管理器拦截管理:设置方法约定
/**
* 4.2告知哪些方法被事务管理器拦截管理:设置方法约定
* @param transactionManager
* @return
*/
@Bean
public TransactionInterceptor txMethodAdvice(PlatformTransactionManager transactionManager){
NameMatchTransactionAttributeSource attributeSource = new NameMatchTransactionAttributeSource();
//设置规则:只读事务,不错更新操作
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//默认值
//设置规则:当事务存在就在该事务运行,如果不存在创建新的事务,默认值(变更操作)
RuleBasedTransactionAttribute requriedTx = new RuleBasedTransactionAttribute();
requriedTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));//默认值RuntimeException
requriedTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置哪些方法的管理规则
Map<String, TransactionAttribute> methodMap = new HashMap<>();
methodMap.put("get*",readOnlyTx);
methodMap.put("load*",readOnlyTx);
methodMap.put("find*",readOnlyTx);
methodMap.put("list*",readOnlyTx);
methodMap.put("query*",readOnlyTx);
methodMap.put("select*",readOnlyTx);
methodMap.put("check*",readOnlyTx);
methodMap.put("valid*",readOnlyTx);
methodMap.put("login*",readOnlyTx);
methodMap.put("*",requriedTx);
attributeSource.setNameMap(methodMap);
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributeSource(attributeSource);
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
4.3通过AOP设置哪个层次下的方法被事务管理器管理
/**
* 4.3通过AOP设置哪个层次下的方法被事务管理器管理
* @param txMethodAdvice
* @return
*/
@Bean
public Advisor serviceAdvisor(TransactionInterceptor txMethodAdvice){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.framework..service.*Service.*(..))");
return new DefaultPointcutAdvisor(pointcut,txMethodAdvice);
}
5. 其它类
建立持久化类
package com.framework.model;
import java.util.Date;
public class Student {
private Integer studentId;
private String studentName;
private String studentSex;
private Integer age;
private Date birthday;
private Integer classId;
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentSex() {
return studentSex;
}
public void setStudentSex(String studentSex) {
this.studentSex = studentSex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getClassId() {
return classId;
}
public void setClassId(Integer classId) {
this.classId = classId;
}
@Override
public String toString() {
return "Student{" +
"studentId=" + studentId +
", studentName='" + studentName + '\'' +
", studentSex='" + studentSex + '\'' +
", age=" + age +
", birthday=" + birthday +
", classId=" + classId +
'}';
}
}
- 取别名
<typeAliases>
<typeAlias type="com.framework.model.Student" alias="Student"/>
</typeAliases>
StudentMapper接口
package com.framework.mapper;
import com.framework.model.Student;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StudentMapper {
List<Student> list();
void add(Student student);
void update();
}
StudentMapper.xml(命名空间和StudentMapper对应)
<?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.framework.mapper.StudentMapper">
<resultMap id="BaseResultMapper" type="Student">
<!-- <id>标签:做主键映射,MyBatis框架通过ID标签区分是否是相同数据 -->
<!-- jdbcType/javaType可以省略不写 -->
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="student_sex" property="studentSex"/>
<result column="age" property="age" />
<result column="birthday" property="birthday"/>
<result column="class_id" property="classId"/>
</resultMap>
<select id="list" resultMap="BaseResultMapper">
SELECT * FROM student
</select>
<insert id="add" parameterType="Student">
INSERT INTO student (student_name,student_age,age)
VALUES (#{studentName},#{student_sex},#[age})
</insert>
<update id="update">
UPDATE student SET student_sex="123456789"WHERE student_id = 11
</update>
</mapper>
注意:此时数据表中student_sex长度设为2,
SET student_sex="123456789"
这句肯定出错
StudentService接口
package com.framework.service;
import com.framework.model.Student;
import java.util.List;
public interface StudentService {
List<Student> list();
void add(Student student);
void get(Student student);//get开头,添加操作-测试事务
void update(Student student);//先添加正确的数据,再更新数据-测试事务
}
注意,此处只可以用service结尾的名称,因为前面配置通过AOP设置哪个层次下的方法被事务管理器管理时设置了:
pointcut.setExpression("execution(* com.framework..service.*Service.*(..))");
在com.framework...service
包下且以Service
结尾的被事务管理器管理
StudentServiceImpl类
package com.framework.service.impl;
import com.framework.mapper.StudentMapper;
import com.framework.model.Student;
import com.framework.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Override
public List<Student> list() {
return this.studentMapper.list();
}
@Override
public void add(Student student) {
this.add(student);
}
@Override
public void get(Student student) {
this.add(student);
}
@Override
public void update(Student student) {
this.add(student);
this.studentMapper.update();
}
}
6. 定义StudentController
package com.framework.web;
import com.framework.model.Student;
import com.framework.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/list")
public List<Student> list(){
System.out.println("studentService = " + studentService);
return this.studentService.list();
}
@GetMapping("/add")//正确数据
public void add(){
Student student = new Student();
student.setStudentName("起点");
student.setStudentSex("男");
student.setAge(22);
this.studentService.add(student);
}
@GetMapping("/insert")//GET方法只是只读状态
public void insert(){
Student student = new Student();
student.setStudentName("终点");
student.setStudentSex("男");
student.setAge(22);
this.studentService.get(student);
}
@GetMapping("/update")//正确数据
public void update(){
Student student = new Student();
student.setStudentName("起点001");
student.setStudentSex("男");
student.setAge(22);
this.studentService.update(student);//事务相关
}
}
7.启动时加载配置类
javax.servlet.ServletContainerInitializer
接口的实现类在Servlet3.0环境中,用于配置容器。它反过来会查找实现WebApplicationInitializer
的类,将配置的任务交给他们来完成。
AbstractAnnotationConfigDispatcherServletInitializer
就是WebApplicationInitializer
的基础实现,所以当部署到servlet容器中时,容器会发现它的子类,并用子类来配置Servlet上下文。
子类中的重写方法:
getServletMappings()
:将一个或多个路径映射到DispatcherServlet上;getServletConfigClasses()
:返回的带有@Configuration注解的类用来配置DispatcherServlet;getRootConfigClasses()
:返回的带有@Configuration注解的类用来配置ContextLoaderListener;
DispatcherServlet
启动时,创建Spring应用上下文并加载配置文件或配置类中声明的beanDispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射;
在Spring web应用中,通常还有由ContextLoaderListener
创建的另一个上下文。ContextLoaderListener加载应用中的其它bean,通常指驱动应用后端的中间层和数据层组件。
AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener。是传统web.xml方式的替代方式。
此段内容来自侵删
package config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class GlobalApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
/*读取springBoot的配置类*/
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
/*读取servlet的配置*/
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}