- 1. idea中使用lombok
- 2. Mybatis的使用
- 3. log4j加入依赖
- 4.Mybatis关联外部文件
- 5. 数据库表字段和实体类属性对应关系
- 6. 关联关系
- 7. 分步查询
- 8. MyBatis懒加载
- 9. 动态SQL
- 10. 一级缓存失效的情况
- 11. 二级缓存
- 12.逆向工程
- 13.bean之xml
- 14. 注解管理bean
- 15. AOP(Aspect Oriented Programming面向切面编程)
- 16. 声明式事务
- 17. @RequestMapping
- 18. RESTFul
- 19.PUT&DELETE请求
- 20. 拦截器
- 21. 注解设定类型转换
- 22. 数据校验
- 23. 异常映射
- 24. SpringMVC执行流程
@TOC
1. idea中使用lombok
- 导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
- idea添加lobbok插件,开启注解Setting -> Buid.. -> Comiler -> Annotation Processors -> Enable annotation processing
- 使用@Data
2. Mybatis的使用
- 导入依赖
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
- 准备配置文件
建议命名为mybatis-config.xml,整个Spring之后这个文件省略。
注意:配置文件存放的位置是src/main/resources目录下。
- Mybatis映射文件
写SQL语句
注意:EmployeeMapper.xml所在的目录要和mybatis-config.xml中使用mapper标签配置的一致。
- 完成Mapper接口
- 在SQL语句中,数据库表的表名不确定,需要外部动态传入,此时不能使用#{},因为数据库不允许表名位置使用问号占位符,此时只能使用${}。
- 注意:由于Mapper接口中方法名是作为SQL语句标签的id,不能重复,所以Mapper接口中不能出现重名的方法,不允许重载!
- 数据输入
有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。 数据输出
<!-- 在全局范围内对Mybatis进行配置 -->
<settings>
<!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
自增组件
SQL语句中加入属性
useGeneratedKeys="true" keyProperty="empId"
3. log4j加入依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- log4j的配置文件
文件名固定:log4j.xml 或 log4j.properties
4.Mybatis关联外部文件
- 创建jdbc.properties配置文件
<properties resource="jdbc.properties"/>
5. 数据库表字段和实体类属性对应关系
实体类型别名
<!-- 配置类型的别名 -->
<typeAliases>
<!-- 声明了实体类所在的包之后,在Mapper配置文件中,只需要指定这个包下的简单类名即可 -->
<package name="com.atguigu.mybatis.entity"/>
</typeAliases>
手动取别名
- 驼峰式命名规则
- resultMap
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">
<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>
<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>
<result column="emp_salary" property="empSalary"/>
</resultMap>
6. 关联关系
- 在“对一”关联关系中,我们的配置比较多,但是关键词就只有:association和javaType
- 在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”
7. 分步查询
<!-- orderList集合属性的映射关系,使用分步查询 -->
<!-- 在collection标签中使用select属性指定要引用的SQL语句 -->
<!-- select属性值的格式是:Mapper配置文件的名称空间.SQL语句id -->
<!-- column属性:指定Customer和Order之间建立关联关系时所依赖的字段 -->
<collection
property="orderList"
select="com.atguigu.mybatis.mapper.CustomerMapper.selectOrderList"
column="customer_id"/>
8. MyBatis懒加载
<!-- Mybatis全局配置 -->
<settings>
<!-- 开启延迟加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
9. 动态SQL
- if和where标签
有选择的加入SQL片段 - set标签
更新一部分字段 - trim标签
使用trim标签控制条件部分两端是否包含某些字符 - choose/when/otherwise
在多个分支条件中,仅执行一个。 - foreach标签
批量 - sql标签
抽取重复的SQL片段,可以引用
10. 一级缓存失效的情况
- 不是同一个SqlSession
- 同一个SqlSession但是查询条件发生了变化
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
- 同一个SqlSession两次查询期间提交了事务
11. 二级缓存
- 配置文件加入cache标签
<mapper namespace="com.atguigu.mybatis.EmployeeMapper">
<!-- 加入cache标签启用二级缓存功能 -->
<cache/>
- 实体类支持序列化
public class Employee implements Serializable {}
- 日志中打印的Cache Hit Ratio叫做缓存命中率
结论:SqlSession关闭的时候,一级缓存中的内容会被存入二级缓存
12.逆向工程
先创建数据库表,框架反向生成实体类、Mapper接口、Mapper配置文件
13.bean之xml
1. 根据id获取bean
- 导入依赖
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
- 配置文件
<!-- 实验一 [重要]创建bean -->
<bean id="happyComponent" class="com.atguigu.ioc.component.HappyComponent"/>
- java
// 创建 IOC 容器对象,为便于其他实验方法使用声明为成员变量
private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");
HappyComponent happyComponent = (HappyComponent) iocContainer.getBean("happyComponent");
2. 根据类型获取bean
类型要唯一
HappyComponent component = iocContainer.getBean(HappyComponent.class);
3. 属性赋值之setter注入
<property name="componentName" value="veryHappy"/>
4. 属性赋值之引用外部bean
<property name="happyMachine" ref="happyMachine"/>
5. 属性赋值之内部bean
<property name="happyMachine">
<!-- 在一个 bean 中再声明一个 bean 就是内部 bean -->
<!-- 内部 bean 可以直接用于给属性赋值,可以省略 id 属性 -->
<bean class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="makeHappy"/>
</bean>
</property>
6. 引入外部属性文件
导依赖
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
属性文件
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://192.168.198.100:3306/mybatis-example
jdbc.driver=com.mysql.jdbc.Driver
引入外部属性文件
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
属性赋值
<!-- 实验六 [重要]给bean的属性赋值:引入外部属性文件 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
7. 级联属性
<bean id="happyComponent6" class="com.atguigu.ioc.component.HappyComponent">
<!-- 装配关联对象 -->
<property name="happyMachine" ref="happyMachine2"/>
<!-- 对HappyComponent来说,happyMachine的machineName属性就是级联属性 -->
<property name="happyMachine.machineName" value="cascadeValue"/>
</bean>
8. 属性赋值之构造器
<!-- 实验八 给bean的属性赋值:构造器注入 -->
<bean id="happyTeam" class="com.atguigu.ioc.component.HappyTeam">
<constructor-arg value="happyCorps"/>
<constructor-arg value="10"/>
<constructor-arg value="1000.55"/>
</bean>
9. 特殊值
<property name="commonValue">
<!-- null标签:将一个属性值明确设置为null -->
<null/>
</property>
<!-- 实验九 给bean的属性赋值:特殊值处理 -->
<bean id="propValue" class="com.atguigu.ioc.component.PropValue">
<property name="expression">
<!-- 解决方案二:使用CDATA节 -->
<!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>
</bean>
10. P名称空间
<!-- 实验十 给bean的属性赋值:使用p名称空间 -->
<bean id="happyMachine3"
class="com.atguigu.ioc.component.HappyMachine"
p:machineName="goodMachine"
/>
11.集合属性
<property name="memberList">
<list>
<value>member01</value>
<value>member02</value>
<value>member03</value>
</list>
</property>
<!-- 使用set标签也能实现相同效果,只是附带了去重功能 -->
<!-- array也同样兼容 -->
<!-- 给Map类型的属性赋值 -->
<!-- 也可以使用props标签 -->
12. 自动装配
所谓自动装配就是一个组件需要其他组件时,由 IOC 容器负责找到那个需要的组件,并装配进去。
<!-- 实验十二 自动装配 -->
<bean id="happyService3" class="com.atguigu.ioc.component.HappyService"/>
<bean id="happyService2" class="com.atguigu.ioc.component.HappyService"/>
<!-- 使用bean标签的autowire属性设置自动装配效果 -->
<!-- byType表示根据类型进行装配,此时如果类型匹配的bean不止一个,那么会抛NoUniqueBeanDefinitionException -->
<!-- byName表示根据bean的id进行匹配。而bean的id是根据需要装配组件的属性的属性名来确定的 -->
<bean id="happyController"
class="com.atguigu.ioc.component.HappyController"
autowire="byName"
>
<!-- 手动装配:在property标签中使用ref属性明确指定要装配的bean -->
<!--<property name="happyService" ref="happyService"/>-->
</bean>
13. 集合类型的bean
<!-- 实验十一 给bean的属性赋值:集合属性 -->
<util:list id="machineList">
<bean class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="machineOne"/>
</bean>
<bean class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="machineTwo"/>
</bean>
<bean class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="machineThree"/>
</bean>
</util:list>
14. FactoryBean机制
FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
<!-- 实验十四 FactoryBean机制 -->
<!-- 这个bean标签中class属性指定的是HappyFactoryBean,但是将来从这里获取的bean是HappyMachine对象 -->
<bean id="happyMachine3" class="com.atguigu.ioc.factory.HappyFactoryBean">
<!-- property标签仍然可以用来通过setXxx()方法给属性赋值 -->
<property name="machineName" value="iceCreamMachine"/>
</bean>
15. bean的作用域
<!-- 实验十五 bean的作用域 -->
<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
<bean id="happyMachine4" scope="prototype" class="com.atguigu.ioc.component.HappyMachine">
<property name="machineName" value="iceCreamMachine"/>
</bean>
16. bean的生命周期
- 创建初始化方法和销毁方法
public void happyInitMethod() {
System.out.println("HappyComponent初始化");
}
public void happyDestroyMethod() {
System.out.println("HappyComponent销毁");
}
- 配置bean时指定初始化和销毁方法
<!-- 实验十六 bean的生命周期 -->
<!-- 使用init-method属性指定初始化方法 -->
<!-- 使用destroy-method属性指定销毁方法 -->
<bean id="happyComponent"
class="com.atguigu.ioc.component.HappyComponent"
init-method="happyInitMethod"
destroy-method="happyDestroyMethod"
>
<property name="happyName" value="uuu"/>
</bean>
14. 注解管理bean
- 导入spring依赖
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
- 标记的四个注解没有本质区别
- 使用@Component注解标记的普通组件
- 使用@Controller注解标记的控制器组件
- 使用@Service注解标记的业务逻辑组件
- 使用@Repository注解标记的持久化层组件
- 扫描
<!-- 配置自动扫描的包 -->
<!-- 最基本的扫描方式 -->
<context:component-scan base-package="com.atguigu.ioc.component"/>
<!-- 情况二:在指定扫描包的基础上指定匹配模式 -->
<context:component-scan
base-package="com.atguigu.ioc.component"
resource-pattern="Soldier*.class"/>
<!-- 情况三:指定不扫描的组件 -->
<context:component-scan base-package="com.atguigu.ioc.component">
<!-- context:exclude-filter标签:指定排除规则 -->
<!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
<!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 情况四:仅扫描指定的组件 -->
<!-- 仅扫描 = 关闭默认规则 + 追加规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package="com.atguigu.ioc.component" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
1. 组件的beanName
在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
①默认情况
类名首字母小写就是bean的id。例如:SoldierController类对应的bean的id就是soldierController。
②使用value属性指定
@Controller(value = "tianDog")
public class SoldierController {
}
//当注解中只设置一个属性时,value属性的属性名可以省略:
@Service("smallDog")
public class SoldierService {
}
2. 完全注解开发
①创建配置类
使用@Configuration注解将一个普通的类标记为Spring的配置类。
②根据配置类创建IOC容器对象
// ClassPathXmlApplicationContext根据XML配置文件创建IOC容器对象
③配置bean
// @Bean注解相当于XML配置文件中的bean标签
// @Bean注解标记的方法的返回值会被放入IOC容器
3. 整合junit4
- 好处1:不需要自己创建IOC容器对象了
- 好处2:任何需要的bean都可以在测试类中直接享受自动装配
- 导入依赖
<!-- Spring的测试包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
- 测试类的注解
// junit的@RunWith注解:指定Spring为Junit提供的运行器
@RunWith(SpringJUnit4ClassRunner.class)
// Spring的@ContextConfiguration指定Spring配置文件的位置
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
15. AOP(Aspect Oriented Programming面向切面编程)
- AOP的核心套路
- 前置通知(然后再调用被代理对象的目标方法)
- 返回通知
- 异常处理
- 后置通知
- 环绕通知(包括前面四种通知的所有功能。)
通知的执行顺序
- Spring版本5.3.x以前:
- 前置通知
- 目标操作
- 后置通知
- 返回通知或异常通知
- Spring版本5.3.x以后:
- 前置通知
- 目标操作
- 返回通知或异常通知
- 后置通知
- AOP术语
- 横切关注点(如权限认证、日志、事务、异常等)
- 通知advice(横切关注点上要做的事情,也被称为增强)
- 连接点joinpoint(纯逻辑概念,指被拦截到的点)
- 切入点pointcut(定位连接点的方式,是一个表达式)
- 切面aspect(切入点和通知的结合,是一个类)
- 目标target(指被代理的目标对象)
- 代理proxy(向目标对象应用通知之后创建的代理对象)
- 织入weave(指把通知应用到目标上,生成代理对象的过程,spring采用在运行期织入)
基于注解的AOP的初步实现
- 加入依赖
<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
- 创建切面类
// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
// @Before注解:声明当前方法是前置通知方法
// value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上
@Before(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogBeforeCore() {
System.out.println("[AOP前置通知] 方法开始了");
}
@AfterReturning(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogAfterSuccess() {
System.out.println("[AOP返回通知] 方法成功返回了");
}
@AfterThrowing(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogAfterException() {
System.out.println("[AOP异常通知] 方法抛异常了");
}
@After(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogFinallyEnd() {
System.out.println("[AOP后置通知] 方法最终结束了");
}
}
- 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:aop="http://www.springframework.org/schema/aop"
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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启基于注解的AOP功能 -->
<aop:aspectj-autoproxy/>
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.atguigu.aop"/>
</beans>
切入点表达式
@Component
public class AtguiguPointCut {
@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")
public void atguiguGlobalPointCut(){}
@Pointcut(value = "execution(public int *..Calculator.add(int,int))")
public void atguiguSecondPointCut(){}
@Pointcut(value = "execution(* *..*Service.*(..))")
public void transactionPointCut(){}
}
切面优先级@Order(较小的数):优先级高
各个通知获取细节信息
// @Before注解标记前置通知方法
// value属性:切入点表达式,告诉Spring当前通知方法要套用到哪个目标方法上
// 在前置通知方法形参位置声明一个JoinPoint类型的参数,Spring就会将这个对象传入
// 根据JoinPoint对象就可以获取目标方法名称、实际参数列表
@Before(value = "execution(public int com.atguigu.aop.api.Calculator.add(int,int))")
public void printLogBeforeCore(JoinPoint joinPoint) {
// 1.通过JoinPoint对象获取目标方法签名对象
// 方法的签名:一个方法的全部声明信息
Signature signature = joinPoint.getSignature();
// 2.通过方法的签名对象获取目标方法的详细信息
String methodName = signature.getName();
System.out.println("methodName = " + methodName);
int modifiers = signature.getModifiers();
System.out.println("modifiers = " + modifiers);
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("declaringTypeName = " + declaringTypeName);
// 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表
Object[] args = joinPoint.getArgs();
// 4.由于数组直接打印看不到具体数据,所以转换为List集合
List<Object> argList = Arrays.asList(args);
System.out.println("[AOP前置通知] " + methodName + "方法开始了,参数列表:" + argList);
}
16. 声明式事务
- PlatformTransactionManager 接口本身没有变化,它继承了TransactionManager。
- TransactionManager接口中什么都没有,但是它还是有存在的意义——定义一个技术体系。
- 我们现在要使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 Mybatis 用的也是这个类。它继承PlatformTransactionManager
- DataSourceTransactionManager类中的主要方法:
- doBegin():开启事务
- doSuspend():挂起事务
- doResume():恢复挂起的事务
- doCommit():提交事务
- doRollback():回滚事务
使用注解基本实现
- 加入依赖
dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Spring 持久化层支持jar包 -->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.1</version>
</dependency>
<!-- Spring 测试相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
</dependencies>
- 外部属性文件
atguigu.url=jdbc:mysql://192.168.198.100:3306/mybatis-example
atguigu.driver=com.mysql.jdbc.Driver
atguigu.username=root
atguigu.password=atguigu
- spring配置文件
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.atguigu.tx"/>
<!-- 导入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${atguigu.url}"/>
<property name="driverClassName" value="${atguigu.driver}"/>
<property name="username" value="${atguigu.username}"/>
<property name="password" value="${atguigu.password}"/>
</bean>
<!-- 配置 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 装配数据源 -->
<property name="dataSource" ref="druidDataSource"/>
</bean>
- 配置事务
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 事务管理器的bean只需要装配数据源,其他属性保持默认值即可 -->
<property name="dataSource" ref="druidDataSource"/>
</bean>
- 开启事务
<!-- 开启基于注解的声明式事务功能 -->
<!-- 使用transaction-manager属性指定当前使用是事务管理器的bean -->
<!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
- 使用注解
@Transactional
// readOnly = true把当前事务设置为只读
@Transactional(readOnly = true)
@Transactional(readOnly = false, timeout = 3)
@Transactional(rollbackFor = Exception.class)
@Transactional(isolation = Isolation.READ_COMMITTED, readOnly = false)
使用XML
- 只需要一个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
- spring配置文件
<aop:config>
<!-- 配置切入点表达式,将事务功能定位到具体方法上 -->
<aop:pointcut id="txPoincut" expression="execution(* *..*Service.*(..))"/>
<!-- 将事务通知和切入点表达式关联起来 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoincut"/>
</aop:config>
<!-- tx:advice标签:配置事务通知 -->
<!-- id属性:给事务通知标签设置唯一标识,便于引用 -->
<!-- transaction-manager属性:关联事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- tx:method标签:配置具体的事务方法 -->
<!-- name属性:指定方法名,可以使用星号代表多个字符 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<!-- read-only属性:设置只读属性 -->
<!-- rollback-for属性:设置回滚的异常 -->
<!-- no-rollback-for属性:设置不回滚的异常 -->
<!-- isolation属性:设置事务的隔离级别 -->
<!-- timeout属性:设置事务的超时属性 -->
<!-- propagation属性:设置事务的传播行为 -->
<tx:method name="save*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="update*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
<tx:method name="delete*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
17. @RequestMapping
该注解的作用就是将请求的 URL 地址和处理请求的方式关联起来,建立映射关系。SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。
@RequestMapping("/user/login")
@RequestMapping("/fruit/*")
@RequestMapping(value = "/emp",
method = RequestMethod.GET)
@GetMapping
@PostMapping
@PUTMapping
@DELETEMapping
...
// 使用 @RequestHeader 注解获取请求消息头信息
// name 或 value 属性:指定请求消息头名称
// defaultValue 属性:设置默认值
@RequestHeader
// 使用 @CookieValue 注解获取指定名称的 Cookie 数据
// name 或 value 属性:指定Cookie 名称
// defaultValue 属性:设置默认值
@CookieValue
获取请求参数
@RequestMapping("/param/one/name/one/value")
public String oneNameOneValue(
// 使用@RequestParam注解标记handler方法的形参
// SpringMVC 会将获取到的请求参数从形参位置给我们传进来
@RequestParam("userName") String userName
)
18. RESTFul
REST,即Representational State Transfer的缩写,可以翻译我为“表示性的状态转移”,“表现层资源的状态转移”
如果一个架构符合REST原则,就称它为RESTful架构。
REST 风格的好处
①含蓄,安全
②风格统一
③无状态
④严谨,规范
⑤简洁,优雅
19.PUT&DELETE请求
- web.xml
- 两个 Filter 并存,一定要让 CharacterEncodingFilter 先执行。
<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>
- 表单
- 要点1:原请求方式必须是 post
- 要点2:新的请求方式名称通过请求参数发送
- 要点3:请求参数名称必须是_method
- 要点4:请求参数的值就是要改成的请求方式
20. 拦截器
- 创建拦截器类,实现接口
public class Process01Interceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
// 在处理请求的目标 handler 方法前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("Process01Interceptor preHandle方法");
// 返回true:放行
// 返回false:不放行
return true;
}
// 在目标 handler 方法之后,渲染视图之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("Process01Interceptor postHandle方法");
}
// 渲染视图之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("Process01Interceptor afterCompletion方法");
}
}
拦截器执行顺序
- preHandle() 方法
- 目标 handler 方法
- postHandle() 方法
- 渲染视图
- afterCompletion() 方法
- xml配置
<!-- 注册拦截器 -->
<mvc:interceptors>
<!-- 直接通过内部 bean 配置的拦截器默认拦截全部请求(SpringMVC 范围内) -->
<bean class="com.atguigu.mvc.interceptor.Process01Interceptor"/>
</mvc:interceptors>
<!-- 具体配置拦截器可以指定拦截的请求地址 -->
<mvc:interceptor>
<!-- 精确匹配 -->
<mvc:mapping path="/common/request/one"/>
<bean class="com.atguigu.mvc.interceptor.Process03Interceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- /*模糊匹配,匹配路径中的一层 -->
<mvc:mapping path="/common/request/*"/>
<bean class="com.atguigu.mvc.interceptor.Process04Interceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- /**模糊匹配,匹配路径中的多层 -->
<mvc:mapping path="/common/request/**"/>
<bean class="com.atguigu.mvc.interceptor.Process05Interceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- /**匹配路径中的多层 -->
<mvc:mapping path="/common/request/**"/>
<!-- 使用 mvc:exclude-mapping 标签配置不拦截的地址 -->
<mvc:exclude-mapping path="/common/request/two/bbb"/>
<bean class="com.atguigu.mvc.interceptor.Process05Interceptor"/>
</mvc:interceptor>
多拦截器的执行顺序
- preHandle()方法:和配置的顺序一样
- 目标handler方法
- postHandle()方法:和配置的顺序相反
- 渲染视图
- afterCompletion()方法:和配置的顺序相反
作用
preHandle()方法 :决中文乱码问题,安全验证(判断是否已经登录),页面跳转(系统维护中)等
postHandle()方法:网站升级测试时,跳转到新版测试页面;进行敏感字符替换等
afterCompletion()方法:完成资源关闭,异常处理等操作
21. 注解设定类型转换
SpringMVC 对基本数据类型提供了自动的类型转换。很多带格式的数据必须明确指定格式之后才可以进行类型转换。最典型的就是日期类型。
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date productDate;
@NumberFormat(pattern = "###,###,###.###")
private Double productPrice;
22. 数据校验
标注值必须是格式正确的 Email 地址 | |
---|---|
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
23. 异常映射
- 创建异常处理器类
异常处理器加入IoC容器
<context:component-scan base-package="com.atguigu.mvc.handler,com.atguigu.mvc.exception"/>
给异常处理器类标记注解
// 异常处理器类需要使用 @ControllerAdvice 注解标记
@ControllerAdvice
public class MyExceptionHandler {
}
声明处理异常的方法
// @ExceptionHandler注解:标记异常处理方法
// value属性:指定匹配的异常类型
// 异常类型的形参:SpringMVC 捕获到的异常对象
@ExceptionHandler(value = NullPointerException.class)
public String resolveNullPointerException(Exception e, Model model) {
// 我们可以自己手动将异常对象存入模型
model.addAttribute("atguiguException", e);
// 返回逻辑视图名称
return "error-nullpointer";
}
- 异常范围
<prop key="java.lang.ArithmeticException">error-arith</prop>
<prop key="java.lang.RuntimeException">error-runtime</prop>
24. SpringMVC执行流程
- 首先浏览器发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他组件进行处理,作为统一访问点,进行全局的流程控制
- DispatcherServlet——>HandlerMapping,处理器映射器将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象
- DispatcherServlet——>HandlerAdapter,处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器
- HandlerAdapter——>调用处理器相应功能处理方法,并返回一个ModelAndView对象(Model部分是业务对象返回的模型数据,View部分为逻辑视图名)
- DispatcherServlet——> ViewResolver,视图解析器将把逻辑视图名解析为物理视图,返回View对象
- DispatcherServlet——>View,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构
- DispatcherServlet——>响应,返回控制权给DispatcherServlet,由它返回响应给用户,到此一个流程结束
SpringMVC的常见API
①中央控制器DispatcherServlet
②处理器映射器HandlerMapping
③处理器执行链HandlerExecutionChain
④处理器适配器HandlerAdapter
⑤视图解析器ViewResolver