文章目录
- 一、Spring框架
一、Spring框架
第一章 Spring框架概述
官网:https://spring.io
1.1 主要作用
1、Spring是轻量级的开源的Java EE框架。
2、Spring 可以解决企业应用开发的复杂性。
1.2 优点
1)轻量。
2)针对接口编程,解耦合。
3)AOP编程的支持。
4)方便集成各种优秀的框架。
1.3 Spring体系结构
第二章 IOC 控制反转
作用:能够实现业务对象之间的解耦合,例如service和dao对象之间的解耦合。
2.1 入门案例
2.1.1 导入Maven依赖
<!-- spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
2.1.2 插件
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
2.1.3 定义接口和实现类
//接口
public interface SomeService {
void doSome();
}
//实现类
public class SomeServiceImpl implements SomeService {
public SomeServiceImpl() {
System.out.println("SomeServiceImpl的无参数构造方法!");
}
@Override
public void doSome() {
System.out.println("执行了SomeServiceImpl的doSome()方法");
}
}
2.1.4 创建spring配置文件
在src/main/resources/目录下创建一个xml文件,文件名建议为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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
告诉spring创建对象
声明bean,就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值。spring通过这个名称找到对象
class:类的全限定名称。(不能是接口,因为spring是反射机制创建对象,必须使用类)
spring就完成 SomeService someService = new SomeServiceImpl();
spring是把创建好的对象放入到Map中,spring框架中有一个map存放对象的。
springMap.put(id的值,对象)
例如:
springMap.put("someService", new SomeServiceImpl());
一个bean标签声明一个对象。
-->
<bean id="someService" class="com.service.impl.SomeServiceImpl"/>
<bean id="someService1" class="com.service.impl.SomeServiceImpl"/>
<!--
spring 能创建一个非自定义类的对象吗,创建一个存在的某个类的对象。
-->
<bean id="myDate" class="java.util.Date"/>
</beans>
<!--
spring的配置文件
1、beans:是根标签,spring吧Java对象成为bean
2、spring-beans.xsd 是约束文件,和mybatis指定,dtd是一样的。
-->
2.1.5 定义测试类
public class SomeServiceImplTest {
/**
* spring 默认创建对象的时间:在创建spring的容器的时,会创建配置文件中的所有的对象。
* spring 创建对象:默认调用的是无参数构造方法
*/
@Test
public void doSome() {
// 1、指定spring配置文件的名称。
String config = "beans.xml";
// 2、创建表示spring容器的对象,ApplicationContext
// ApplicationContext表示spring容器,通过容器获取对象
// ClassPathXmlApplicationContext从类路径中去加载spring的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(config);
// 3、从容器中获取某个对象,你要调用对象方法
// getBean("配置文件中的id属性的值")
SomeService someService = (SomeService) context.getBean("someService");
// 4、使用spring创建好的对象。
someService.doSome();
}
}
2.2 容器接口 和 实现类
2.2.1 ApplicationContext 接口 (容器)
ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现类有两个。
1)ClassPathXmlApplicationContext实现类
---------->用于类路径
若Spring配置文件存放在项目的类路径下,则使用ClassPathXmlApplicationContext实现类加载。
ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。 以后代码中若要使用到这些对 象,只需从内存中直接获取即可。执行效率较高。但占用内存
2)FileSystemXmlApplicationContext实现类
针对于磁盘的路径,可以将配置文件放在电脑磁盘的任意一个位置,然后使用FileSystemXmlApplication来获取配置文件信息。
2.3 DI注入
DI:依赖注入,表示创建对象,给对象赋值。
DI的两种实现:
1)在spring配置文件中,使用标签和属性完成,叫做基于XML的d实现。
2)使用spring中的注解,完成属性赋值,叫做基于注解的DI实现。
DI注入分类:
1)set注入(设值注入):spring调用类的set方法,在set方法可以实现属性的赋值。
2)构造注入,spring调用类的有参数构造方法,创建对象。在构造方法中完成赋值。
2.3.1 set注入(配置文件)
2.3.1.1 简单类型的注入
语法:
<bean id = "xx" class = "yy">
<property name = "属性名字" value = "此属性的值"/>
<!-- 一个property只能给一个属性赋值 -->
</bean>
示例:
<bean id="myStudent" class="com.Ioc_DI.domain.Student">
<property name="name" value="李四"/>
<property name="age" value="20"/>
</bean>
2.3.1.2 引用类型的set注入
语法格式:
<!--引用类型的注入-->
<bean id="xxx" class="com.Ioc_DI.domain.Student">
<property name="属性名称" ref="bean的id(对象名称)"
</bean>
示例:
<bean id="myStudent" class="com.Ioc_DI.domain.Student">
<property name="name" value="李四"/>
<property name="age" value="23"/>
<!--引用类型的注入-->
<property name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.Ioc_DI.domain.School">
<property name="name" value="凤鸣中学"/>
<property name="address" value="重庆市云阳县凤鸣镇"/>
</bean>
2.3.2 构造注入
构造注入:spring调用类的有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。
构造注入使用 标签。
语法:
标签:一个 表示构造方法一个参数。
标签属性:
1)name:表示构造方法的形参名。
2)index:表示构造方法参数的位置,参数从左到右的位置是0,1,2的顺序。
3)value:构造方法形参类型是简单类型的,使用value。
4)ref:构造方法的形参是引用类型的,使用ref。
示例:
2.3.2.1 name属性 实现构造注入
<!-- 使用name属性构造注入 -->
<bean id="myStudentConstructor" class="com.Ioc_DI.domain.Student">
<constructor-arg name="name" value="Jane"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="school" ref="SchoolConstructor"/>
</bean>
<bean id="SchoolConstructor" class="com.Ioc_DI.domain.School">
<constructor-arg name="name" value="重庆大学"/>
<constructor-arg name="address" value="重庆市"/>
</bean>
2.3.2.2 index属性 实现构造注入
<!-- 使用index属性构造注入 -->
<bean id="myStudentConstructor02" class="com.Ioc_DI.domain.Student">
<constructor-arg index="0" value="邓进"/>
<constructor-arg index="1" value="21"/>
<constructor-arg index="2" ref="mySchoolConstructor02"/>
</bean>
<bean id="mySchoolConstructor02" class="com.Ioc_DI.domain.School">
<constructor-arg index="0" value="广州"/>
<constructor-arg index="1" value="广州大学"/>
</bean>
2.4 引用数据类型自动注入
自动注入:spring框架根据某些规则可以给引用类型赋值。
使用的规则常用的是:byName,byType。
2.4.1 byName(按名称注入)
Java类中引用类型的属性名和spring容器中的(配置文件)<bean>的id名称一样。且数据类型是一致的。这样的容器中的bean,spring能够赋值给引用类型。
语法:
<bean id = "xxx" class = "yyy" autowire = "byName">
简单类型属性赋值
</bean>
示例:
<!-- 采用自动注入的方式 -->
<bean id="myStudent" class="com.autowire.domain.Student" autowire="byName">
<property name="name" value="马化腾"/>
<property name="age" value="47"/>
</bean>
<!-- 引用类型School,通过id为上面Studet的属性school,实现按名称自动注入 -->
<bean id="school" class="com.autowire.domain.School">
<property name="name" value="香港大学"/>
<property name="address" value="香港"/>
</bean>
2.4.2 byType(按类型注入)
Java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性是同源关系的,这样的bean能够赋值给引用类型。
什么是同源关系?
1、Java类中引用类型的数据类型和bean的class的值是一样的。
2、Java类中引用类型的数据类型和bean的class的值父子类关系的。
3、Java类中引用类型的数据类型和bean的class的值接口和实现类关系的。
示例:
<!-- 通过引用类型注入-->
<bean id="stu" class="com.autowire.domain.Student" autowire="byType">
<property name="name" value="小风"/>
<property name="age" value="22"/>
</bean>
<bean id="mySchool" class="com.autowire.domain.School">
<property name="name" value="澳门大学"/>
<property name="address" value="澳门"/>
</bean>
注意:
1、在byType中,在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的。
2.5 多个配置文件的优势
1、每个文件的大小比一个文件要小很多,效率高。
2、避免多人竞争带来的冲突。
如果你的项目有多个模块(相关的功能在一起),一个模块一个配置文件。
学生考勤模块一个配置文件,张三。
学生成绩一个配置文件,李四。
多文件的分配方式:
1、按功能模块:一个模块一个配置文件。
2、按类的功能:数据库相关的配置一个文件配置文件,做事务的功能一个配置文件,做service功能的一个配置文件等。
2.6 包含其他配置文件
关键字:"classpath:"表示类路径(class文件所在的目录),在spring的配置文件中要指定其他文件的位置,需要使用classpath,告诉 spring到哪去加载读取配置文件。
语法:
<import resource = "其他配置文件的路径"/>
示例:
<import resource = "classpath:autowire/autowire.xml"/>
通配符:*
total.xml配置文件
<import reource = "classpath:autowire/spring-student.xml"/>
<import reource = "classpath:autowire/spring-school.xml"/>
<!-- 可以将以上的文件利用通配符一次性加载,抽取公共部分 -->
<import reource = "classpath:autowire/spring-*.xml"/>
spring-student.xml配置文件
<bean id="student" class="com.includeFile.Student" autowire="byType">
<property name="name" value="小王"/>
<property name="age" value="20"/>
</bean>
spring-school.xml配置文件
<bean id="school" class="com.includeFile.School">
<property name="name" value="湖北大学"/>
<property name="address" value="湖北省"/>
</bean>
2.7 基于注解的DI
通过注解完成Java对象的创建,属性赋值。
2.7.1 使用注解的步骤
1、加入Maven依赖
加入依赖spring-context,在加入spring-context的同时,间接加入spring-aop的依赖。使用注解必须加入spring-aop依赖。
2、在类中加入spring的注解(多个不同功能的注解)
3、在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置。
4、使用注解创建对象,创建容器ApplicationContext
2.7.2 涉及到使用的注解
1、@Component
2、@Respository
3、@Service
4、Controller
5、@Value
6、@Autowired
7、@Resource
2.7.3 组件扫描器
在resource目录下, 声明组件扫描器(component-scan),组件就是java对象
语法格式:
基于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"
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">
<!--
第一种方式:指定一个包
base-package:指定注解在你的项目中的包名。
加入了component-scan标签,配置文件有相应的变化:
1、加入一个新的约束文件spring-context.xsd
2、给这个新的约束文件起个命名空间的名称
-->
<context:component-scan base-package="com.annotation_DI"/>
<!--
第二种方式:指定多个包
-->
<context:component-scan base-package="com.annotation_DI"/>
<context:component-scan base-package="com.autowire"/>
<context:component-scan base-package="com.Ioc_DI"/>
<!--
指定多个包的第二种方式
使用分隔符(;或,)分隔多个包名
-->
<context:component-scan base-package="com.autowire;com.Ioc_DI;com.annotation_DI"/>
<!--
指定多个包的第二种方式
使用父包
-->
<context:component-scan base-package="com"/>
</beans>
工作方式:
spring会扫描遍历base-package指定的包,把包中和子包中所有的类,找到类中的注解,按照注解的功能创建对象,或 给属性赋值。
2.7.4 @Component
含义:
/**
* @Component :创建对象的,等同于<bean>的功能
* 属性:value就是对象的名称,也就是bean的id值。
* Value的值是唯一的,创建的对象在整个spring容器中只有一个。
* 位置:在类的上面
*/
//1、使用value属性,指定对象名称
@Component(value = "myStudent")
//2、等同于,省略value
@Component("myStudent")
//3、不指定对象名称,由spring提供默认名称:类名的首字母小写
@Component
注意:
1、以下三个注解的使用语法和@Component一样的。都能创建对象,但是这三个注解都有格外的功能。
2、@Repository、@Service、@Controller是给项目分层的。
2.7.5 @Repository
放在dao的是实现类上面,表示创建dao对象,dao对象是能访问数据库的。用在持久层类的上面。
2.7.6 @Service
放在Service的实现类上面,创建Service对象,service是做业务处理的,可以有事务等功能的。用在业务层类的上面。
2.7.7 @Controller
放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接收用户提交的参数,显示请求处理的结果。用在控制器的上面。
2.7.8 @Value
1. 简单类型的属性赋值
属性:value 是String类型的,表示简答数据类型的属性值。
位置:
1、在属性定义的上面,无需set方法,推荐使用。
2、在set方法上面。
在属性上赋值
@Value(value = "王五")
private String name;
@Value(value = "23")
private Integer age;
//value属性可以省略
@Value("王五")
private String name;
@Value("23")
private Integer age;
在set方法上赋值(用的很少)
@Value("30")
public void setAge(Integer age) {
System.out.println("age = " + age);
this.age = age;
}
2. 引用类型的属性赋值
@Autowired:spring框架提供的注解,实现引用类型的赋值。
spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType。
@Autowired:默认使用的是byType自动注入。
属性:required,是一个Boolean类型的,默认为true。
required = true:表示引用类型赋值失败,程序报错,并终止执行。
require = false:引用类型如果赋值失败,程序正常执行,引用类型是null。
@Autowired(required = true)
2.1 byType方式
位置:
1、在属性定义的上面,无需set方法,推荐使用。
2、在set方法的上面。
在Student.java中
@Value("故里")
private String name;
@Value("30")
private Integer age;
/**
* 申明一个引用类型
*/
@Autowired
private School school;
在School.java中
@Value("北京大学")
private String name;
@Value("北京市")
private String address;
2.2 byName方式
1、在属性上面加入@AutoWired
2、在属性上面加入@Qualifier(value = “bean的id”):表示使用指定的名称的bean完成赋值。
//byName自动注入
@Autowired
@Qualifier("mySchool")
private School school;
2.7.9 @Resource
来自JDK中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值,使用的也是自动注入原理,
支持byName,byType,默认为byName。
位置:1、在属性定义的上面,无需set方法,推荐使用。
2、在set方法的上面。
默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType。
@Resource指使用byName的方式,需要增加一个属性name。
// name的属性是bean的id值。
@Resource(name = "mySchool")
private School school;
第三章 AOP面向切面编程
3.1 AOP简介
AOP(Aspect Orient Programming),面向切面编程。面向切面编是从动态角度考虑程序运行过程。
Aspect(切面):切面,给你的目标类增加的功能,就是切面。
切面的特点:一般都是非业务方法,独立使用的。
AOP底层,就是采用动态代理模式实现的。可以使用jdk,cglib两种代理的方式。
AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。
3.1.1 如何理解面向切面编程(*)
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前,还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能。
3.1.2 术语
1)Aspect(切面):切面,表示增强的功能,就是一堆代码,完成某一个功能。非业务功能,常见的切面功能有日志,事务, 统计信息,参数检查,权限验证。
2)JoinPoint:连接点,连接 业务方法和切面的位置。就是某类中的业务方法。
3)PointCut:切入点,指多个连接点方法的集合。多个方法。
4)目标对象:给哪个类的方法增加功能,这个类就是目标对象。
5)Advice:通知,通知表示切面功能执行的时间。
3.1.3 三个关键要素
1)切面的功能代码,切面干什么。
2)切面的执行位置,使用PointCut表示切面执行的位置。
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
3.2 动态代理
3.2.1 实现方式(*)
1)jdk动态代理,使用jdk中的proxy,Method,InvocationHandler创建代理对象。
2)jdk动态代理必须要求目标类实现接口。
3.2.2 cglib动态代理(*)
1)第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。
2)子类就是代理对象。要求目标类不能是final的,方法也不能是final的。
3.2.3 作用(*)
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复。
3)专注业务逻辑代码。
4)解耦合,让你的业务功能和日志,事务非业务功能分离。
3.3 AOP的实现
aop是一个规范,是动态的一个规范化,一个标准。
3.3.1 aop的技术实现框架
1)spring:spring在内部实现了aop规范,能做到aop的工作。
spring主要在事务处理时使用的aop。
项目开发中很少使用spring的aop实现。因为spring的aop比较笨重。
2)aspectJ:一个开源的专门做aop的框架。
spring中集成了aspectJ框架,通过spring就能使用aspectJ的功能。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
官网地址:http://www.eclipse.org/aspectj/
aspectJ框架实现aop有两种方式:
1)使用xml的配置文件:配置全局事务。
2)使用注解,在项目中要做aop功能,一般都是使用注解,aspectJ有5个注解。
3.4 aspectJ框架的使用
3.4.1 通知类型
切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)。
在aspectJ框架中使用注解表示的:
1)@Before
2)@AfterReturning
3)@Around
4)AfterThrowing
5)@After
也可以使用xml配置文中的标签。
3.4.2 AspectJ 的切入点表达式execution(掌握)
切入点表达式要匹配的对象就是目标方法的方法名。
表达式原型:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
1、modifiers-pattern] 访问权限类型
2、ret-type-pattern 返回值类型
3、declaring-type-pattern 包名类名
4、name-pattern(param-pattern) 方法名(参数类型和参数个数)
5、throws-pattern 抛出异常类型
6、?表示可选的部分
示例:
1、execution(public * *(..))
指定切入点为:任意公共方法。
2、execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
3、execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
4、execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。
5、execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点。
3.5 aspectJ实现aop的基本步骤
1、新建maven项目
2、加入依赖
1)spring依赖
2)aspectJ依赖
3)junit单元测试
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<!-- aspectJ框架依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.14</version>
</dependency>
3、创建目标类:接口和他们的实现类。要做的是给类中 的方法增加功能。
//接口
public interface SomeService {
/**
*
* @param name
* @param age
*/
void doSome(String name, Integer age);
}
//实现类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("====目标方法doSome()方法执行====");
}
}
4、创建切面类:普通类
1)在类中定义方法,方法就是切面要执行的功能代码。
2)在方法的上面加入aspectJ中的通知注解,例如@Before
还需要指定切入点表达式execution()。
@Before(value = "execution(void com.example.SomeServiceImpl.doSome(String,Integer))")
public void myBefore() {
// 就是你切面要执行的功能代码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("1====前置通知,切面功能:在目标方法之前输出执行时间:" + sdf.format(new Date()));
}
5、创建spring的配置文件:声明对象,把对象交给容器统一管理。
声明对象你可以使用注解,或者xml配置文件
1)声明目标对象。
2)声明切面类对象。
3)声明aspectJ框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的。
<!-- 把对象交给spring容器,由spring容器统一创建,管理对象 -->
<bean id="someService" class="com.example.SomeServiceImpl"/>
<!-- 声明切面对象-->
<bean id="myAspect" class="com.example.MyAspect"/>
<!--声明自动代理生成器:使用aspectJ框架内部的功能,创建目标对象的代理对象.
创建代理对象是在内存中实现的,修改目标对象的内存结构.
创建为代理对象所以目标对象就是被修改后的代理对象,
aspectj-autoproxy:会把spring容器中的所有目标对象,一次性生成代理对象.
-->
<aop:aspectj-autoproxy/>
6、创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的功能增强。
@Test
public void test01() {
String config = "example/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
SomeService someService = (SomeService) context.getBean("someService");
someService.doSome("lisi",20);
}
3.6 通知类型及术语解析
3.6.1 @Before 前置通知
含义:
1、在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参 数。
2、该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。
3、不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
* @Before:前置通知注解 属性:value,是切入点表达式,表示切面的功能执行的位置。
* 位置:在方法的上面。
* 特点:
* 1、在目标方法之前先执行的
* 2、不会改变目标方法的执行结果。
* 3、不会影响目标方法的执行。
示例:
@Before(value = "execution(void com.example.SomeServiceImpl.doSome(String,Integer))")
public void myBefore() {
//就是你切面要执行的功能代码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("1====前置通知,切面功能:在目标方法之前输出执行时间:" + sdf.format(new Date()));
}
3.6.2 JoinPoint 连接点
业务方法,要加入切面功能的业务方法。
作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入JoinPoint。
这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数。
@Before(value = "execution(void *..SomeServiceImpl.do*(..))")
public void myBefore(JoinPoint jp) {
// 就是你切面要执行的功能代码
System.out.println(jp.getArgs());
//获取方法的实参。
Object[] args = jp.getArgs();
for (Object obj:args) {
System.out.println(obj);
}
System.out.println("目标方法名称" + jp.getSignature().getName());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("4====前置通知,切面功能:在目标方法之前输出执行时间:" + sdf.format(new Date()));
}
}
3.6.3 @AfterReturning 后置通知
后置通知定义方法,方法是实现切面功能的。
1)方法定义的要求:
1、公共方法public。
2、方法没有返回值。
3、方法名称自定义。
4、方法有参数,推荐是Object,参数名自定义。
* @AfterReutrning: 后置通知 属性:
* 1、value 切入点表达式
* 2、returning 自定义变量,表示目标方法的返回值的。
* 自定义变量名必须和通知方法的形参名一样。
2)位置:在方法定义的上面。
3)特点:
1、在目标方法之后执行的。
2、能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能。
3、可以修改返回值。
示例:
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther02(..))",
returning = "obj")
public void myAfterReturning(Object obj) {
//Object obj:是目标方法执行后的返回值,根据返回值做你的切面的功能处理。
((User) obj).setName("罗翔");
System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:" + obj);
}
// 添加JoinPoint参数,获取目标方法的参数值,JoinPoint只能放到第一个参数的位置。
3.6.4 @Around 环绕通知
经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务。
1)环绕通知方法的定义格式
1、public
2、必须有一个返回值,推荐使用Object。
3、方法名称自定义。
4、方法有参数,固定的参数 ProceedingJoinPoint。
2)属性:value切入点表达式
3)位置:在方法定义的上面
4)特点
1、它是功能最强的通知。
2、在目标方法前和后都能增强功能。
3、控制目标方法是否被调用执行。
4、修改原来的目标方法的执行结果。影响最后的调用结果。
5)作用:执行目标方法
6)返回值:就是目标方法的执行结果,可以被修改。
3.6.5 @AfterThrowing 异常通知
1)环绕通知方法的定义格式。
1、public
2、没有返回值
3、方法名称自定义。
4、方法有一个Exception参数,如果有是JoinPoint,Exception。
2)属性:
1. value切入点表达式。
2. throwing 自定义变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样。
3)特点
1、在目标方法抛出异常时执行的。
2、可以做异常的监控程序,监控目标方法执行时是不是有异常。如果有异常,可以发送邮件,短信通知。
示例:
aspect切面类代码
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doException(..))", throwing = "e")
public void myAfterThrowing(Exception e) {
System.out.println("异常通知:方法发生异常时,执行:" + e.getMessage());
// 通过短信或邮件发送给开发人员
}
接口实现类中,制造异常
@Override
public User doException(User user) {
int i = 1/0;
System.out.println("执行了doOther02()方法,user=" + user);
return user;
}
测试类执行代码
@Test
public void doException() {
String config = "exception/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
SomeService proxy = (SomeService) context.getBean("someService");
proxy.doException(new User("王五","wang"));
}
异常通知执行原理
try{
SomeServiceImpl.doException(..);
}catch(Exception e){
myAfterThrowing(e);
}
3.6.6 @After 最终通知
1)环绕通知方法的定义格式
1、public
2、没有返回值。
3、方法名称自定义。
4、方法没有参数,如果还有就是JoinPoint。
2)属性:value 切入点表达式
3)位置:在方法的上面
4)特点:
1、总会执行
2、在目标方法之后执行。
示例:
切面类Aspect中:
@After(value = "execution(* com.after.SomeServiceImpl.doAfter(..))")
public void myAfter() {
System.out.println("执行最终通知,总是会被执行的代码。");
// 一般是做资源清除工作的
}
实现类
@Override
public User doAfter(User user) {
// int i = 1/0;
System.out.println("执行了doOther02()方法,user=" + user);
return user;
}
执行原理:
try{
myAfter();
}catch(Exception e){
doException();
}finally{
doAfter();
}
3.6.7 @pointcut 定义切入点
1)含义:@Pointcut 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,可以使用@Pointcut。
2)属性:value 切入点表达式。
3)位置:在自定义的方法上面。
4)特点:
1、当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
2、其他通知中,value属性就可以使用这个方法名称,代替切入点表达式了。
示例:
切面类:
@Aspect
public class MyAspect {
@After(value = "myPointcut()")
public void myPointcutAfter() {
System.out.println("执行最终通知,总是会执行的代码!");
// 通过短信或邮件发送给开发人员
}
@Before(value = "myPointcut()")
public void myPointcutBefore() {
System.out.println("前置通知,在目标方法之前执行");
// 通过短信或邮件发送给开发人员
}
@Pointcut(value = "execution(* com.pointcut.SomeServiceImpl.doOther(..))")
private void myPointcut() {
// 无需代码
}
}
目标方法的代码
@Override
public String doOther(String name, Integer age) {
System.out.println("目标方法doOther()执行");
return name;
}
3.7 有接口也可以使用cglib动态代理
proxy-target-class属性:当为true时,表示使用cglib动态代理创建对象。
不写表示默认为false,也就是使用JDK动态代理创建对象。
<!--
1、目标类没有接口,使用cglib动态代理,spring框架会自动应用cglib。
2、如果目标类有接口,默认使用JDK动态代理,如果想要使用cglib动态代理,则设置属性proxy-target-class="true"。
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
第四章 Spring集成MyBatis
4.1 整合原因
将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注 册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。
使用到的技术:IOC。
4.2 为什么IOC能整合MyBatis?
因为IOC能够创建对象,可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象。开发人员就不用同时面对两个或多个框架了,就面对一个spring。
4.3 实现步骤
4.3.1 新建maven项目
4.3.2 加入maven依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- spring框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<!-- mybatis框架-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.3</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 插件 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
4.3.3 创建实体类
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
...
}
4.3.4 创建Dao接口和mapper文件
dao接口
public interface StudentDao {
/**
* 添加学生
*
* @param student
* @return
*/
int insertStudent(Student student);
/**
* 查询所有学生信息
*
* @return
*/
List<Student> selectStudent();
}
mapper文件
<?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.dao.StudentDao">
<!-- 使用insert,update,delete,select标签写sql-->
<insert id="insertStudent">
insert into student
values (#{id}, #{name}, #{email}, #{age})
</insert>
<select id="selectStudent" resultType="com.domain.Student">
select id, name, email, age
from student
order by id desc;
</select>
</mapper>
4.3.5 创建MyBatis主配置文件
<?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="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.domain"/>
</typeAliases>
<!-- 指定其他mapper文件的位置
找到其他文件中的sql语句
-->
<mappers>
<!--告诉 mybatis 要执行的 sql 语句的位置,可以有多个mapper文件。
使用mapper的resource属性指定mapper文件的路径。
这个路径是从target/classes路径开始的。
1、resource = “mapper文件的路径,使用 / 分割路径”。
一个mapper resource 指定一个mapper文件。
-->
<package name="com.dao"/>
</mappers>
</configuration>
4.3.6 创建Service接口和实现类
service接口
public interface StudentService {
/**
* 添加对象
*
* @param student
* @return
*/
int addStudent(Student student);
/**
* 查询所有的学生信息
*
* @return
*/
List<Student> queryStudents();
}
实现类
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
/**
* 使用set注入赋值
*
* @param studentDao
*/
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
return studentDao.insertStudent(student);
}
@Override
public List<Student> queryStudents() {
return studentDao.selectStudent();
}
}
4.3.7 创建spring配置文件
<!-- 把数据库中的配置信息,写在一个单独的文件,便于修改数据库的配置内容
spring知道jdbc.properties文件的位置。
-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 声明数据源-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- set注入给DruidDataSource提供连接数据库信息-->
<!-- 使用属性配置文件中的数据,语法${key}-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!-- (固定写法) 创建SqlSessionFactory,mybatis中提供了创建SqlSessionFactoryBean类,这个类就是创建创建SqlSessionFactory的-->
<bean id="sqlSessionFactory" class=" org.mybatis.spring.SqlSessionFactoryBean">
<!-- set注入,把数据库连接池赋值给了dataSource属性-->
<property name="dataSource" ref="myDataSource"/>
<!--
mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置。
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!-- 创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定SqlSessionFactory的对象-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次getMapper()方法,得到每个接口的dao对象
创建好的dao对象是放在spring的容器中。dao对象的默认名称是 接口的首字母小写
-->
<property name="basePackage" value="com.dao"/>
</bean>
<!-- 声明service-->
<bean id="studentService" class="com.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
4.3.8 创建测试类
@Test
public void testDaoInsert() {
String config = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
StudentDao dao = (StudentDao) context.getBean("studentDao");
int i = dao.insertStudent(new Student(1009, "小马", "horse@qq.com", 12));
System.out.println(i);
}
第五章 Spring事务
5.1 事务简介
事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层, 即 Service 层。
这样做是为了能够使用事务的特性来管理具体的业务。
在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务 。
(2)使用 AspectJ 的 AOP 配置管理事务。
将事务放在service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句。
5.2 如何处理事务
spring处理事务的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了
1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback。
事务管理器( PlatformTransactionManager)是一个接口和它的众多的实现类。
接口中定义了事务重要方法 commit,rollback。
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库------->spring创建好的是DataSourceTransactionManager
Hibernate访问数据库------->spring创建好的是HibernateTransactionManager
2)怎么使用?
你需要告诉spring你用的是哪种类型的数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对应的事务管理器的实现类。在spring中的配置文件中使用声明就可以了。
例如:
<!--使用mybatis访问数据库,你要在xml的配置文件中声明事务管理器-->
<bean id = "xxx" class = "..DataSourceTransactionManager"/>
5.3 事务类型
1、事务类型
这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。
1) DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle 默认为 READ_COMMITTED。
2) READ_UNCOMMITTED:读未提交。未解决任何并发问题。
3) READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
4)REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读 。
5)SERIALIZABLE:串行化。不存在并发问题。
2、事务的超时时间:表示一个方法执行的最长时间,如果方法执行时超过了时间,事务就会回滚。单位是秒,整数值,默认为-1。
3、事务的传播行为:控制业务方法是不是有事务的,是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的。
事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。
1)PROPAGATION_REQUIRED
//指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事
//务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为
2)PROPAGATION_REQUIRES_NEW
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
例如查询操作
3)PROPAGATION_SUPPORTS
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
以上三个需要掌握。
4)PROPAGATION_MANDATORY
5)PROPAGATION_NESTED
6)PROPAGATION_NEVER
7)PROPAGATION_NOT_SUPPORTED
4、spring提交事务,回滚事务的时机
1)当你的业务方法,执行成功,没有抛出异常,当方法执行完毕,spring在方法执行后提交事务。
2)当你的业务方法抛出运行时异常时,spring执行回滚,调用事务管理器的rollback。
运行时异常定义:RunTimeException 和它的子类都是运行时异常,例如NullPointerException
NumberFormatException。
3)当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务。
受查异常:在你写代码中,必须处理的异常。例如IOException,SQLException。
5.4 使用Spring的事务注解管理事务
适合中小型项目使用的注解方案。
5.4.1实现注解事务的基本步骤
1、声明事务管理器(applicationContext.xml)
<!-- 声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 需要知道连接数据库的信息-->
<property name="dataSource" ref="myDataSource"/>
</bean>
2、开启注解驱动(applicationContext.xml)
<!-- 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
transaction-manager:事务管理器对象的id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
3、业务层 public 方法加入事务属性
private SaleDao saleDao;
private GoodsDao goodsDao;
/**
* @param goodsId 购买商品的编号
* @param nums 购买的数量
* rollbackFor:表示发送的异常一定回滚。
rollback处理逻辑:
1)spring框架首先会检查方法抛出的异常是不是在rollback的属性值中,
如果异常在rollback列表中,不管是什么类型的异常,一定回滚。
2)如果你抛出的异常不在rollback列表中,spring会判断异常是不是RunTimeException,如果是,就一定会 回滚。
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {Exception.class}
)
@Override
public void buy(Integer goodsId, Integer nums) {
System.out.println("=====buy()方法的开始====");
// 记录销售信息,向sale表添加记录
Sale sale = new Sale();
sale.setGid(goodsId);
sale.setNums(nums);
saleDao.insertSale(sale);
// 更新库存
Goods goods = goodsDao.selectGoods(goodsId);
if (goods == null) {
// 商品不存在
throw new NullPointerException("编号是:" + goodsId + "的商品不存在!");
} else if (goods.getAmount() < nums) {
throw new NoEnoughException("编号是:" + goodsId + "的商品库存不足!");
}
// 修改库存
Goods buyGoods = new Goods();
buyGoods.setId(goodsId);
buyGoods.setAmount(nums);
goodsDao.updateGoods(buyGoods);
System.out.println("=====buy()方法的结束====");
}
5.4.2 注意事项
如果我们的spring代码没有问题,但还是会出现不会回滚事务,问题在于我们的数据库引擎,
MYISAM不支持事务,INNODB支持事务处理,Mysql版本从5.5.8开始,默认使用INNODB存储引擎。
修改涉及到的数据库表的引擎为InnoDB
1、查看此数据库中所使用的引擎
show variables like 'default_storage_engine'
2、查看指定表所使用的引擎
show table status where NAME ='表名'
3、 修改指定表的引擎
alter table 表名 engine=innodb;
*修改mysql默认的数据库引擎*
打开配置文件my.ini,将“default-storage-engine=MYISAM”改为你想设定的,然后重启即可。
5.5 总结spring事务
1、管理事务的是,事务管理和它的实现类。
2、spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能。
3)指定方法需要的隔离级别,传播行为,超时等。
你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。
5.6 使用 AspectJ 的 AOP 配置管理事务
适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectJ框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
5.6.1 要使用的是aspectJ框架,需要加入依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.14</version>
</dependency>
5.6.2 声明事务管理器对象
<bean id = "xx" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
5.6.3 声明方法需要的事务类型
配置方法的事务属性【隔离级别,传播行为,超时】
<!-- 2、声明业务方法它的事务属性(隔离级别,传播行为,超时时间)-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- tx:method:给具体的方法配置事务属性,method可以有多个分别给不同的方法设置事务属性。
name:方法名称:1)完整的方法名称,不带有包和类。
2)方法可以使用通配符,*表示任意字符。
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:你指定的异常类名,全限定类名。发生异常是一定回滚。
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,transaction_aspectJ.excep.NoEnoughException"/>
<!-- 使用通配符,指定很多方法-->
<tx:method name="add*" propagation="REQUIRES_NEW"/>
<!-- 指定修改方法-->
<tx:method name="modify*"/>
<!-- 删除方法-->
<tx:method name="remove*"/>
<!-- 查询方法。query ,search,find-->
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
5.6.4 配置aop
指定哪些类要创建代理。
<!--配置aop-->
<aop:config>
<!-- 配置切入点表达式,指定哪些包中的类,要使用事务
id:切入点表达式的名称,唯一值。
expression:切入点表达式,指定哪些类要使用事务,aspectJ会创建代理对象。
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!-- 配置增强器:关联advice和pointCut
advice-ref:通知,上面tx:advice那里的配置。
pointcut-ref:切入点表达式的id。
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
执行测试类
@Test
public void test01() {
String config = "com/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
BuyGoodsService service = (BuyGoodsService) context.getBean("buyService");
System.out.println("service.getClass() = " + service.getClass().getName());
// 调用方法
service.buy(1002,100);
}
第六章 Spring与web
在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指 Servlet)中获取到 Spring 容器的问题。
只要在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。
6.1 加入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- spring框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.14</version>
</dependency>
<!-- mybatis框架-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.3</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com</groupId>
<artifactId>Spring-Transaction</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- jsp依赖-->
<dependency>
<groupId>tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>5.5.23</version>
</dependency>
<!-- 使用监听器对象,加入依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.13</version>
</dependency>
</dependencies>
<!-- 插件 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
6.2 定义Servlet
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String strId = request.getParameter("id");
String strName = request.getParameter("name");
String strEmail = request.getParameter("email");
String strAge = request.getParameter("age");
// 第一种方法 创建spring的容器
String config = "applicationContext.xml";
// ApplicationContext context = new ClassPathXmlApplicationContext(config);
// 获取ServletContext中的容器对象,创建好的容器对象,拿来就用
// 第二种方法
/* WebApplicationContext context = null;
String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object obj = getServletContext().getAttribute(key);
if (obj != null) {
context = (WebApplicationContext) obj;
}*/
// 第三种方法 使用框架中的方法,获取容器对象
WebApplicationContext context = null;
ServletContext sc = getServletContext();
context = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
StudentService service = (StudentService) context.getBean("studentService");
System.out.println("容器的对象信息:context = " + context);
Student stu = new Student();
stu.setId(Integer.parseInt(strId));
stu.setName(strName);
stu.setEmail(strEmail);
stu.setAge(Integer.parseInt(strAge));
service.addStudent(stu);
// 给定一个页面
request.getRequestDispatcher("/result.jsp").forward(request, response);
}
6.3 使用 Spring 的监听器 ContextLoaderListener(掌握)
配置监听器:
目的是创建容器对象,创建了容器对象,就能把spring.xml配置文件中的所有对象都创建好。
用户发起请求就可以直接使用对象了。
<!-- 注册监听器 ContextLoaderListener
监听器被创建对象后,会读取/WEB-INF/applicationContext.xml
为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径。
可以修改默认的文件位置,使用context-param重新指定文件的位置。
-->
<context-param>
<!-- contextConfigLocation:表示配置文件的路径-->
<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>
<!-- 涉及依赖 -->
<!-- 使用监听器对象,加入依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.13</version>
</dependency>
第七章 SPring整合Junit5
7.1 整合Juni4
1、导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
2、编写测试类
//指定单元测试框架的版本
@RunWith(SpringJUnit4ClassRunner.class)
//加载配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class UserControllerTest extends TestCase {
@Autowired
private TestService testService;
@Autowired
private AccountService accountService;
@Test
public void testSelectUsers() {
accountService.moneyTransfer("0102030", "0103040", 1000);
}
@Test
public void test02() {
testService.test();
}
}
7.2 整合Junit5
1、导入依赖
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<version>2.7.1.Final</version>
<scope>test</scope>
</dependency>
2、测试类
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:applicationContext.xml")
以上两个注解效果等价于以下一个注解
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
注意:
其中的@Test需要重新进行选择指定包下的Test注解
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private TestService testService;
@Test
public void test02() {
testService.test();
}
}