Spring学习笔记,初学菜鸟整理,希望各位大佬来补充,来提学习意见
- 第一章Spring概述
- 第二章 IOC控制反转
- 第三章 AOP 面向切面编程
- 第四章 Spring集成Mybatis
- 第五章 Spring事务
- 第六章 Spring 与 Web
- 附上思维导图
第一章Spring概述
1.1 Spring框架是什么?
Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。
Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。
Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。
总结归纳:
spring是一个框架,核心技术是ioc,aop,实现解耦合
spring是一个容器,容器中存放的是Java对象,需要做的是把Java对象放到容器中
1.2 Spring优点?
-
轻量
Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需
的 jar 总共在 3M 左右。 -
针对接口编程,解耦合
Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的
对象创建方式,现在由容器完成。对象之间的依赖解耦合。 -
AOP 编程的支持
通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付 -
方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。
1.3 Spring体系结构
第二章 IOC控制反转
2.1 IOC控制反转的介绍
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖注入。应用广泛。
依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA对 classB 有依赖。
Ioc 的实现:
➢ 依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。
依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。
Spring 框架使用依赖注入(DI )实现 IoC 。
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。
2.2 Spring实现步骤
1.创建maven项目
2.加入maven依赖
spring依赖,版本5.2.5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
junit依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
3.创建类(接口和实现类)和没有使用框架一样的普通类
//接口
public interface SomeService {
void doSome();
}
//实现类
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("doSome方法执行了——————————");
}
}
4.创建spring配置文件声明类的信息,这些类由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"
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会把创建好的对象存到map集合中,spring集合用map储存对象
springmap.put(id值,对象)
一个bean标签声明一个对象
-->
<bean id="someService" class="com.xiaojie.service.Impl.SomeServiceImpl"></bean>
</beans>
5.测试spring创建的对象
/**
* spring默认创建对象的时间:在创建spring容器时,会创建配置文件里的Java对象
* spring创建对象默认是通过无参构造方法
*/
@Test
public void test1(){
//使用spring容器创建对象
//1.指定配置文件
String config = "beans.xml";
//2.创建表示spring容器的对象,ApplicationContext
//ApplicationContext就是表示spring容器,通过容器获取对象
//ClassPathXmlApplactionContext:表示从类路径加载配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
SomeService ss = (SomeService) ac.getBean("someService");
ss.doSome();
}
2.3 使用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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
//spring 配置文件加入 java.util.Date 定义:
<bean id="mydate" class="java.util.Date"></bean>
</beans>
测试:
//使用spring创建非自定对象
@Test
public void test4(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Date date = (Date) ac.getBean("mydate");
System.out.println(date);
}
输出:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=53884:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test4
Tue Jul 13 15:24:13 CST 2021
Process finished with exit code 0
2.4 容器接口和实现类
ApplicationContext 接口
ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现类有两个。
A 、 配置文件在类路径下
若 Spring 配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现
类进行加载。
@Test
public void test1(){
//1.指定配置文件,配置文件在类路径下
String config = "beans.xml";
//2.创建表示spring容器的对象,ApplicationContext
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
SomeService ss = (SomeService) ac.getBean("someService");
ss.doSome();
}
B 、 ApplicationContext 容器中对象的装配时机
ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。
以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。
//ApplicationContext容器对对象的装配时机
@Test
public void test5(){
String config = "beans.xml";
//获取容器,此时对象已装配完毕
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
2.5 什么样的对象应该放在容器中?
放入容器中的对象:
dao类,service类,工具类,controller类
spring中的对象默认都是单列的,在容器中叫这个名称的对象只有一个
不放人容器中的对象:
1.实体类对象,实体类数据来自于数据库的
2.servlet,listener,filter
2.6 基于XML的DI
2.6.1 注入的分类
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。
1.set注入(掌握)
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
A.简单类型注入
//学生信息类
public class Student {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过set注入给对象属性赋值-->
<bean id="student" class="com.xiaojie.domian.Student" >
<property name="name" value="李四"> </property>
<property name="age" value="12"> </property>
</bean>
</beans>
测试代码
/**
* spring通过set主入方法给属性赋值
*/
@Test
public void test5(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student st = (Student) ac.getBean("student");
System.out.println("学生信息为:"+st);
}
输出信息:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=55214:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test5
学生信息为:Student{name='李四', age=12}
Process finished with exit code 0
B.引用属性注入
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。
public class Student {
private String name;
private Integer age;
//引用属性
private School school;
public void setSchool(School school) {
this.school = school;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过set注入给对象属性赋值-->
<bean id="student" class="com.xiaojie.domian.Student" >
<property name="name" value="李四"> </property>
<property name="age" value="12"> </property>
<!--通过ref 给引用属性赋值,其ref的值必须为bean的id-->
<property name="school" ref="school"/>
</bean>
<bean id="school" class="com.xiaojie.domian.School" >
<property name="name" value="北京大学" />
<property name="address" value="北京海淀区"/>
</bean>
</beans>
测试:
@Test
public void test5(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student st = (Student) ac.getBean("student");
System.out.println("学生信息为:"+st);
}
输出:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=64428:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test5
学生信息为:Student{name='李四', age=12, school=School{name='北京大学', address='北京海淀区'}}
Process finished with exit code 0
2.构造注入(理解)
public class School {
private String name;
private String address;
public School(){
}
public School(String name,String address){
this.address = address;
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过构造注入给属性赋值-->
<bean id="school" class="com.xiaojie.domian.School" >
<constructor-arg name="name" value="北京大学" />
<constructor-arg name="address" value="北京海淀区"/>
</bean>
</beans>
测试:
/**
* 通过构造注入给属性赋值
*/
@Test
public void test6(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
School s = (School) ac.getBean("school");
System.out.println("学校信息为:"+s);
}
输出:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=60239:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test6
学校信息为:School{name='北京大学', address='北京海淀区'}
Process finished with exit code 0
2.6.2 引用类型属性自动注入
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType: 根据类型自动注入
1.byName 方式自动注入(按名称注入)
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
举例:
//学生信息类
public class Student {
private String name;
private Integer age;
private School school;
//......set/get方法
}
//学校信息类
public class School {
private String name;
private String address;
//......set/get方法
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过byName自动赋值-->
<bean id="student" class="com.xiaojie.domian.Student" autowire="byName">
<property name="name" value="李四"> </property>
<property name="age" value="12"> </property>
</bean>
<!--声明school对象-->
<bean id="school" class="com.xiaojie.domian.School" >
<property name="name" value="北京大学" />
<property name="address" value="北京海淀区"/>
</bean>
</beans>
测试:
/**
* spring通过byName自动赋值
*/
@Test
public void test7(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student s = (Student) ac.getBean("student");
System.out.println("学生信息为:"+s);
}
输出:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=60776:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test7
学生信息为:Student{name='李四', age=12, school=School{name='北京大学', address='北京海淀区'}}
Process finished with exit code 0
2.byType 方式自动注入(按类型注入)
Java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系的,这样bean能够赋值给引用类型。
同源类型就是一类的意思:
- Java类中引用类型的数据类型和bean的class的值是一样的。
- Java类中引用类型的数据类型和bean的class的值父子类关系的。
- Java类中引用类型的数据类型和bean的class的值接口和实现类关系的。
例如:
//学生信息类
public class Student {
private String name;
private Integer age;
private School school;
//......set/get方法
}
//学校信息类
public class School {
private String name;
private String address;
//......set/get方法
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--此时spring它是拿到student里面的School属性名跟bean标签里的class路径一一比对,
直到找到相匹配的对象然后赋值给School属性-->
<bean id="student" class="com.xiaojie.domian.Student" autowire="byType">
<property name="name" value="李四"> </property>
<property name="age" value="12"> </property>
</bean>
<bean id="school" class="com.xiaojie.domian.School" >
<property name="name" value="清华大学" />
<property name="address" value="北京海淀区"/>
</bean>
</beans>
测试:
@Test
public void test8(){
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student s = (Student) ac.getBean("student");
System.out.println("学生信息为:"+s);
}
--------------------------------------------------------------
输出:"D:\Program Files\Java\jdk-11.0.4\bin\java.exe"
学生信息为:Student{name='李四', age=12, school=School{name='清华大学', address='北京海淀区'}}
Process finished with exit code 0
2.6.3 为应用指定多个 Spring 配置文件
在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring 配置文件分解成多个配置文件。包含关系的配置文件:多个配置文件中有一个总文件,总配置文件将各其它子文件通过import引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。
例如:
applictionContext.xml为父配置文件
spring父配置信息为:
//关键字:classpath表示类路径(class文件所在的目录P),在spring的配置文件中要指定其他文件的位置,需要使用classpath,告诉spring到哪去加载文件
<import resource="classpath*:test01/spring-student.xml"/>
<import resource="classpath*:test01/spring-school.xml"/>
测试:
@Test
public void test9(){
String config = "/test01/applictionContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student s = (Student) ac.getBean("student");
System.out.println("学生信息为:"+s);
}
----------------------------------------------------------------
输出:
学生信息为:Student{name='张三', age=20, school=School{name='北京大学', address='北京海淀区'}}
Process finished with exit code 0
也可使用通配符*。但,此时要求父配置文件名不能满足所能匹配的格式,否则将出现
循环递归包含。就本例而言,父配置文件不能匹配 spring-*.xml 的格式,即不能起名为
spring-total.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-*.xml格式
<import resource="classpath*:test01/spring-*.xml"/>
</beans>
2.7 基于注解的DI(掌握)
通过注解完成java对象创建,属性赋值。
2.7.1 使用注解的步骤:
- 加入maven的依赖,spring-context,在你加入spring-context的同时,间接加入了spring-aop的依赖,使用注解必须使用spring-aop依赖
- 在类中加入spring的注解(多个不同功能的注解)
- 在spring的配置文件中,加入一个组件扫描的标签,说明注解在你项目中的位置
2.7.2 定义 Bean 的注解@Component( 掌握)
需要在类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值。
例如:
/**
* @Component 创建对象的,等同于<bean>的功能
* 属性value,就是对象的名称,也就是bean的id
* value值是唯一的,创建的对象在整个spring容器中就一个
* 位置:在类的上面
*
* @Compotent(value="myStudent")等同于
* <bean id="myStudent" class="com.xiaojie.ba01.Student"></bean>
*
*/
@Component(value = "myStudent")
public class Student {
private String name;
private Integer age;
public Student(){
System.out.println("无参构造方法被调用,对象创建成功");
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
在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: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">
<!--声明组件扫描器(component-scan),组件就是java对象
base-package:指定注解在你的项目中的包名。
comonent-scan工作方式:spring会扫描遍历base-package指定的包,
把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。
加入了componenet-scan标签,配置文件的变化:
1.加入一个新的约束文件spring-context.xsd
2.给这个新的约束文件起个命名空间的名称
-->
<context:component-scan base-package="com.xiaojie.ba01"/>
</beans>
另外,Spring 还提供了 3 个创建对象的注解:
➢ @ReposiStory 用于对 DAO 实现类进行注解
➢ @Service 用于对 Service 实现类进行注解
➢ @Controller 用于对 Controller 实现类进行注解
这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义
@Service 创建业务层对象,业务层对象可以加入事务功能
@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository 放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。
指定多个包的3种形式:
<!--第一种,使用多个扫描器,指定不同的包-->
<context:component-scan base-package="com.xiaojie.ba01"/>
<context:component-scan base-package="com.xiaojie.ba02"/>
<!--第二种,多个包之间用;或者,隔开-->
<context:component-scan base-package="com.xiaojie.ba01;com.xiaojie.ba02"/>
<!--第三种,指定父包-->
<context:component-scan base-package="com.xiaojie"/>
2.7.3 @Value简单属性赋值
需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加
到 setter 上
@Component("myStudent")
public class Student {
/**
* @Value:简单属性类型赋值
* 属性:value是string类型的,表示简单类型的值
* 位置:在属性的上面定义,无需set方法(推荐使用)
*/
@Value("李四")
private String name;
@Value("30")
private Integer age;
public Student(){
}
}
2.7.4 byType 自动注入@Autowired( 掌握)
@Component("myStudent")
public class Student {
@Value("小小")
private String name;
@Value("20")
private Integer age;
/**
* 引用类型
* @Autowired:spring框架提供的注解,实现引用类型的赋值
* spring通过注解的形式给引用类型赋值,使用的是自动注解的原理,支持byName,byType
* @Autowired默认使用byType自动注入
*
*/
@Autowired
private School school;
2.7.4 byName 自动注入@Autowired 与@Qualifier( 掌握)
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用
于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
/**
* 如果要使用byName给属性赋值,需要做的是:
* 1.在属性上加@Autowired
* 2.在属性上面加@Qualifier(value=“bean的id”)
*/
@Qualifier("school")
@Autowired
private School school;
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运
行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
2.7.5 JDK 注解@Resource 自动注入( 掌握)
jdk11以上不支持@Resource,需要在pom文件里加入配置:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.1</version>
</dependency>
Spring提供了对jdk中@Resource注解的支持。@Resource注解既可以按名称匹配Bean,
也可以按类型匹配 Bean。 默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
@Resource 可在属性上,也可在 set 方法上。
- byType 注入引用类型 注入引用类型
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
@Resource
private School school;
- byName 注入引用类型 注入引用类型
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
@Resource(name="school")
private School school;
2.7.6 注解与 XML 的对比
注解优点是:
- 方便
- 直观
- 高效(代码少,没有配置文件的书写那么复杂)。
其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML 方式优点是:
- 配置和代码是分离的
- 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。
第三章 AOP 面向切面编程
3.1 AOP概述
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB
的动态代理。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。
3.2 动态代理(掌握)
实现方式:
- jdk动态代理:使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口
- cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的
动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。
3.3 面向切面编程
3.3.1 怎么理解面向切面编程(掌握)
- 需要在分析项目功能时,找出切面。
- 合理的安排切面 的执行时间(在目标方法前,还是目标方法后)
- 合理的安排切面的执行位置,在哪个类,哪个方法增加功能
3.3.2 面向切面编程有什么好处?
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
3.3 AOP编程术语(掌握)
(1 ) 切面(Aspect )
表示增强的功能,就是一堆代码,完成某个一个功能。非业务功能常见的切面功能由日志,事务,统计信息,参数检查,权限验证。
(2 ) 连接点(JoinPoint )
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3 ) 切入点(Pointcut )
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4 ) 目标对象(Target )
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(5 ) 通知(Advice )
通知表示切面的执行时间,Advice 也叫增强。切入点定义切入的位置,通知定义切入的时间。
3.4 AspectJ 对 AOP 的实现(掌握)
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 简介:
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
官网 地址:http://www.eclipse.org/aspectj/
3.4.1 AspectJ 的切入点表达式( 掌握)
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?-----表示可选的部分
以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就
是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可
以使用以下符号:
举例(最常用的五个,记住):
execution(public * *(…))
指定切入点为:任意公共方法。
execution(* set*(…))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service..(…))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service….(…))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* …service..*(…))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
3.4.2 AspectJ 基于注解的 AOP 实现(掌握)
1.新建maven项目
2.加入依赖
1)spring依赖
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2)aspectj依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
3)junit单元测试
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
3.创建目标类:接口和它的实现类。
要做的是给类中的方法增加功能
public interface SomeService {
void doSome(String name,String age);
}
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, String age) {
System.out.println("==================dosome方法执行=========================");
}
}
4.创建切面类:普通类
1)在类的上面加上 @Aspect
2)在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()
/**
* @Aspect:是aspectj框架中的注解。
* 作用:表示当前类是切面类。
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
*/
@Aspect
public class MyAspect {
/**
* 定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法可以有参数,也可以没有参数。
* 如果有参数,参数不是自定义的,有几个参数类型可以使用。
*/
/**
* @Before:前置通知注解
* 属性:value,是切入点表达是,表示切面的功能执行的位置。
* 位置:在方法的上面
* 特点:
* 1.在目标方法之前先执行的
* 2.不会改变目标方法的执行结果
* 3.不会影响目标方法的执行。
*/
@Before(value = "execution(public void com.xiaojie.ba01.SomeServiceImpl.doSome(String,String))")
public void doAspe(){
System.out.println("时间为:"+new Date());
}
}
5.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者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: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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把对象交给spring容器,由spring容器统一创建,管理对象-->
<!--声明目标类对象-->
<bean id="someService" class="com.xiaojie.ba01.SomeServiceImpl"></bean>
<!--声明切面类对象-->
<bean id="myAspect" class="com.xiaojie.ba01.MyAspect"></bean>
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象
所以目标对象就是被修改后的代理对象。
-->
<aop:aspectj-autoproxy />
</beans>
6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的功能增强。
@Test
public void test1(){
String config="applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService s = (SomeService) ac.getBean("someService");
//通过代理的对象执行方法,实现目标方法执行时,增强功能
s.doSome("zhangsan","20");
}
7.输出:
"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=63618:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java idea代码区\SpringTest\ch03aopaspectJ\target\test-classes;D:\Java idea代码区\SpringTest\ch03aopaspectJ\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aspects\5.2.5.RELEASE\spring-aspects-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.AppTest,test1
时间为:Wed Jul 14 17:04:17 CST 2021
==================dosome方法执行=========================
Process finished with exit code 0
1)@Before 前置通知- 方法有 JoinPoint 参数 [ 掌握]
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
/**
* @Aspect:是aspectj框架中的注解。
* 作用:表示当前类是切面类。
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
*/
@Aspect
public class MyAspect {
/**
* 定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法可以有参数,也可以没有参数。
* 如果有参数,参数不是自定义的,有几个参数类型可以使用。
*/
/**
* @Before:前置通知注解
* 属性:value,是切入点表达是,表示切面的功能执行的位置。
* 位置:在方法的上面
* 特点:
* 1.在目标方法之前先执行的
* 2.不会改变目标方法的执行结果
* 3.不会影响目标方法的执行。
*/
@Before(value = "execution(public void com.xiaojie.ba01.SomeServiceImpl.doSome(String,String))")
public void doAspe(){
System.out.println("时间为:"+new Date());
}
}
2)@AfterReturning 后置通知- 注解有 returning 属性[ 掌握]
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
//接口实现类方法,有返回值,
public class SomeServiceImpl implements SomeService {
public Student doOther2(String name, Integer age) {
Student st = new Student();
st.setAge(age);
st.setName(name);
return st;
}
}
@Aspect
public class MyAspect {
/**
* 后置通知定义方法,方法是实现切面功能的。
* 方法的定义要求:
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有参数,参数类型为Object,参数名自定义
*
*/
/**
* @AfterReturning:后置通知注解
* 属性:1.value,是切入点表达是,表示切面的功能执行的位置。
* 2.returning 自定义的变量,表示目标方法的返回值。
* 自定义变量名必须和通知方法的形参名一样。
* 位置:在方法的上面
* 特点:
* 1.在目标方法之后执行的
* 2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* 3.可以修改这个返回值
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning="res")
public void myAfterReturning(Object res){
//res 是目标方法执行后的返回值,根据返回值做你的切面的功能处理
System.out.println("返回值为:"+res);
System.out.println("事务提交");
}
3)@Around 环绕通知- 增强方法有 ProceedingJoinPoint[ 掌握]
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
@Aspect
public class MyAspect {
/**
*环绕通知定义格式
* 1.公共方法public
* 2.方法有返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法有参数,固定的参数ProceedingJoinpoint
*/
/**
* @Around:环绕通知
* 属性:value,是切入点表达是,表示切面的功能执行的位置。
* 位置:在方法的上面
* 特点:
* 1.它是功能最强的通知
* 2.在目标的前后都能增强功能
* 2.控制目标方法是否被执行调用
* 3.修改原来目标类执行的结果,影响最后调用结果
*
* 环绕通知等同于jdk动态代理,InvocationHandler接口
*
* 参数: ProceedingJoinPoint 就等同于 Method
* 作用:执行目标方法的
* 返回值:就是目标方法的执行结果,可以被修改。
*/
@Around(value = "execution(* *..SomeServiceImpl.doSome(String,String))")
public Object doAspe(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("输出日志信息----------------------");
Object obj=null;
//执行目标方法,等同于Method.invoke(traget,args)
obj = pjp.proceed();
System.out.println("提交事务-------------------------");
return obj;
}
}
4)@AfterThrowing 异常通知- 注解中有 throwing 属性 [ 了解]
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。
//接口实现类,
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, String age) {
System.out.println("==================dosome方法执行========================="+(10/0));
}
}
//异常通知类
@Aspect
public class MyAspect {
/**
*异常通知定义格式
* 1.公共方法public
* 2.没有返回值
* 3.方法名称自定义
* 4.方法有一个Exception,如果还有是JoinPoint
*/
/**
* @AfterThrowing:异常通知
* 属性:value,是切入点表达是,表示切面的功能执行的位置。
* 2.throwinng 自定义的变量,表示目标方法抛出的异常对象
* 变量名必须和方法参数名一样
* 位置:在方法的上面
* 特点:
* 1.在目标方法抛出异常时执行的
* 2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
* 如果有异常,可以发送邮件,短信进行通知
*/
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSome(String,String))",throwing = "ex")
public void myAfterThrowing(Exception ex) {
System.out.println("异常通知:"+ex.getMessage());
}
}
输出:
异常通知:/ by zero
java.lang.ArithmeticException: / by zero
at com.xiaojie.ba04.SomeServiceImpl.doSome(SomeServiceImpl.java:7)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
5)@After 最终通知 [了解]
无论目标方法是否抛出异常,该增强均会被执行。
增加方法:
方法实现:
定义切面:
6)@Pointcut 定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
第四章 Spring集成Mybatis
将 MyBatis与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。
4.1 MySQL 创建数据库 user, 新建表 Student
4.2 maven 依赖 pom.xml
<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.2.5.RELEASE</version>
</dependency>
<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>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring相关依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<!--mysql驱动依赖-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--阿里连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</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 定义实体类和接口
//实体类
public class Student {
private String id;
private String name;
private String sex;
private int age;
private String classname;
//dao层接口
public interface StudentDao {
int addStudent(Student student);
List<Student> seletStudent();
}
4.4 定义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.xiaojie.dao.StudentDao">
<insert id="saveStudent">
insert into student
values(#{id},#{name},#{sex},#{age},#{classname})
</insert>
<select id="seletStudent" resultType="Student">
select *from student order by id
</select>
</mapper>
4.5 定义 Service 接口和实现类
//接口
public interface StudentService {
int saveStudent(Student student);
List<Student> getStudent();
}
//实现类
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
//set方法,使用set注入赋值
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int saveStudent(Student student) {
int s = studentDao.addStudent(student);
return s;
}
@Override
public List<Student> getStudent() {
List<Student> list = studentDao.seletStudent();
return list;
}
}
4.6 定义 MyBatis 主配置文件
在 src 下定义 MyBatis 的主配置文件,命名为 mybatis.xml。
这里有两点需要注意:
(1)主配置文件中不再需要数据源的配置了。因为数据源要交给 Spring 容器来管理了。
(2)这里对 mapper 映射文件的注册,使用标签,即只需给出 mapper 映射文件所在的包即可。因为 mapper 的名称与 Dao 接口名相同,可以使用这种简单注册方式。这种方式的好处是,若有多个映射文件,这里的配置也是不用改变的。当然,也可使用原来的标签方式。
<?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:控制mybatis全局行为-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--起别名标签,在com.xiaojie.domian包下的类文件,所有的别名都为类名-->
<typeAliases>
<package name="com.xiaojie.domain"/>
</typeAliases>
<mappers>
<package name="com.xiaojie.dao"/>
</mappers>
</configuration>
4.6 修改 Spring 配置文件
4.6.1 数据源的配置 (掌握)
使用 JDBC 模板,首先需要配置好数据源,数据源直接以 Bean 的形式配置在 Spring 配置文件中。根据数据源的不同,其配置方式不同:
Druid 数据源 DruidDataSourceDruid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。Druid 与其他数据库连接池的最大区别是提供数据库的
官网:https://github.com/alibaba/druid
使用地址:https://github.com/alibaba/druid/wiki/常见问题
配置连接池:
Spring 配置文件:
<!--声明数据源,作用是连接数据库-->
<bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user" />
<property name="username" value="root" />
<property name="password" value="111" />
<property name="maxActive" value="20"/>
</bean>
**也可从属性配置文件中读取配置信息,例如:
属性文件名称自定义,但一般都是放在 src 下。
Spring 配置文件从属性文件中读取数据时,需要在的 value 属性中使用${ },
将在属性文件中定义的 key 括起来,以引用指定属性的值。
该属性文件若要被 Spring 配置文件读取,其必须在配置文件中进行注册。使用
标签。
context:property-placeholder/ 方式( 掌握)
该方式要求在 Spring 配置文件头部加入 spring-context.xsd 约束文件
context:property-placeholder/标签中有一个属性 location,用于指定属性文件的位置。
4.6.2 注册 SqlSessionFactoryBean
<!--声明的是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-config.xml"/>
</bean>
4.6.3 定义 Mapper 扫描配置器 MapperScannerConfigurer
Mapper 扫描配置器 MapperScannerConfigurer 会自动生成指定的基本包中 mapper 的代理对象。该 Bean 无需设置 id 属性。basePackage 使用分号或逗号设置多个包。
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
Mapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
-->
<property name="basePackage" value="com.xiaojie.dao"/>
</bean>
4.6.4 向 Service 注入接口名
<!--声明service-->
<bean id="studentService" class="com.xiaojie.service.Impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
4.6.5 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:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<!--声明数据源,作用是连接数据库-->
<bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user" />
<property name="username" value="root" />
<property name="password" value="111" />
<property name="maxActive" value="20"/>
</bean>
<!--声明的是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-config.xml"/>
</bean>
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
Mapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
-->
<property name="basePackage" value="com.xiaojie.dao"/>
</bean>
<!--声明service-->
<bean id="studentService" class="com.xiaojie.service.Impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
</beans>
第五章 Spring事务
5.1 Spring的事务管理
事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务
5.2 Spring 事务管理API
Spring 的事务管理,主要用到两个事务相关的接口。
(1) 事务管理器接口(重点)
事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
A 、 常用的两个实现类
PlatformTransactionManager 接口有两个常用的实现类:
➢ DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
➢ HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
B 、 Spring 的回滚方式(理解)
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。
C 、 回顾错误与异常( 理解)
Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)
的实例时,才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、ThreadDeath、NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出)的,JVM 一般会终止线程。程序在编译和运行时出现的另一类错误称之为异常,它是JVM通知程序员的一种方式。通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。异常分为运行时异常与受查异常。
运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。如,NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但,只要代码编写足够仔细,程序足够健壮,运行时异常是可以避免的。受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理,则无法通过编译。如 SQLException,ClassNotFoundException,IOException 等都属于受查异常。RuntimeException 及其子类以外的异常,均属于受查异常。当然,用户自定义的 Exception
的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的为 RuntimeException 的子类,那么定义的就是受查异常。
(2 ) 事务定义接口
事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
A 、 定义了五个事务隔离级别常量( 掌握)
这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。
➢ DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle
默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
B 、 定义了七个事务传播行为常量( 掌握)
所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
黄色标记为常用
(1) PROPAGATION_REQUIRED
指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。
(2) PROPAGATION_SUPPORTS
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
(3) PROPAGATION_REQUIRES_NEW
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
C 、 定义了默认事务超时时限
常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。
注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该
值一般就使用默认值即可
5.3 程序举例环境搭建
(1) 创建数据库表
创建两个数据库表 sale , goods
sale 销售表
goods 商品表
goods 表数据
(2) maven依赖pom.xml
<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.2.5.RELEASE</version>
</dependency>
<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>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring相关依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<!--mysql驱动依赖-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--阿里连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</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>
</build>
(3)定义 dao 接口
定义两个 dao 的接口 SaleDao , GoodsDao
(4)定义dao接口对应的sql映射文件
SaleDao.xml
GoodsDao.xml
(5)定义异常类
定义 service 层可能会抛出的异常类 NotEnoughException
(6)定义 Service 接口
定义 Service 接口 BuyGoodsService
(7)定义 service 的实现类
定义 service 层接口的实现类 BuyGoodsServiceImpl
-
类定义
-
Dao 属性
-
Buy 方法
(8)修改 Spring 配置文件内容
- 声明 Mybatis 对象
<!--声明数据源,作用是连接数据库-->
<bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user" />
<property name="username" value="root" />
<property name="password" value="111" />
<property name="maxActive" value="20"/>
</bean>
<!--声明的是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-config.xml"/>
</bean>
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
Mapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
-->
<property name="basePackage" value="com.xiaojie.dao"/>
</bean>
- 声明业务层对象
(9)定义测试类
定义测试类 MyTest。现在就可以在无事务代理的情况下运行了。
5.4 使用Spring的事务注解管理事务(掌握)
通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。
@Transactional 的所有可选属性如下所示:
➢ propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为
Propagation.REQUIRED。
➢ isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为
Isolation.DEFAULT。
➢ readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值
为 false。
➢ timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为
-1,即没有时限。
➢ rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有
一个异常类时,可以不使用数组。
➢ rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。
当然,若只有一个异常类时,可以不使用数组。
➢ noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若
只有一个异常类时,可以不使用数组。
➢ noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空
数组。当然,若只有一个异常类时,可以不使用数组。
需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
实现注解的事务步骤:
- 声明事务管理器
<!--使用spring的事务处理-->
<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mydataSource"/>
</bean>
- 开启注解驱动
<!--2.开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象-->
<tx:annotation-driven transaction-manager="transactionManager"/>
transaction-manager:事务管理器 bean 的 id
- 业务层 public 方法加入事务属性
5.5 使用 AspectJ 的 AOP 配置管理事务( 掌握)
使用 XML 配置事务代理的方式的不足是,每个目标类都需要配置事务代理。当目标类
较多,配置文件会变得非常臃肿。
使用 XML 配置顾问方式可以自动为每个符合切入点表达式的类生成事务代理。其用法
很简单,只需将前面代码中关于事务代理的配置删除,再替换为如下内容即可。
5.5.1 maven 依赖 pom.xml
新加入 aspectj 的依赖坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
5.5.2 在容器中添加事务管理器
<!--声明式事务处理:和源代码完全分离的-->
<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mydataSource"/>
</bean>
5.5.3 配置事务通知
<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间
id:自定义名称,表示<tx:advice> 和 </tx:advice>之间的配置内容的
transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
name:方法名称:1)完整的方法名称,不带有包名和类
2)方法可以使用通配符,* 表示任意字符
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:你指定的异常类名,全限定类名。发生异常一定回滚
-->
<tx:method name="saveStudent" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException"/>
<!--使用通配符,指定很多的方法-->
<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.5.4 配置增强器
<!--配置aop-->
<aop:config>
<!--配置切入点表达式:指定哪些包中类,要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
com.xiaojie.service
com.crm.service
com.service
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联adivce和pointcut
advice-ref:通知,上面tx:advice 哪里的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
第六章 Spring 与 Web
6.1 使用 Spring 的器监听器 ContextLoaderListener( 掌握)
对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将 Spring 容器的创建时机,放在 ServletContext 初始化时,就可以保证 Spring 容器的创建只会执行一次,也就保证了Spring 容器在整个应用中的唯一性。
当 Spring 容器创建好后,在整个应用的生命周期过程中,Spring 容器应该是随时可以被访问的。即,Spring 容器应具有全局性。而放入 ServletContext 对象的属性,就具有应用的全局性。所以,将创建好的 Spring 容器,以属性的形式放入到 ServletContext 的空间中,就保证了 Spring 容器的全局性。
Step1 :maven 依赖 pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
Step2 :注册监听器 ContextLoaderListener
若 要 在 ServletContext 初 始 化 时 创 建 Spring 容 器 , 就 需 要 使 用 监 听 器 接 口
ServletContextListener 对 ServletContext 进行监听。在 web.xml 中注册该监听器。
Step3 :指定 Spring 配置文件的位置
ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认
的 Spring 配置文件位置与名称为:WEB-INF/applicationContext.xml。但,一般会将该配置文件放置于项目的 classpath 下,即 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置及名称进行指定。
Step4 :获取 Spring 容器对象
在 Servlet 中获取容器对象的常用方式有两种:
- 直接从 ServletContext 中获
从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中存
放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。所以,可
以直接通过 ServletContext 的 getAttribute()方法,按照指定的 key 将容器对象获取到。
- 通过 WebApplicationContextUtils 获取
工具类 WebApplicationContextUtils 有一个方法专门用于从 ServletContext 中获取 Spring
容器对象:getRequiredWebApplicationContext(ServletContext sc)
调用 Spring 提供的方法获取容器对象: