Spring是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架
spring官网:http://spring.io/
1997年IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson(spring之父)
2017年9月份发布了spring的最新版本spring5.0通用版
框架的定义:拥有一整套解决方案
使用框架使得我们的程序开发变得更高效
1、spring是一个容器,这个容器会管理很多的组件(java bean)
2、spring可以很好的解决程序间的耦合
3、spring可以集成很多第三方框架,可以充当众多技术栈之间的一个粘合剂
方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合,用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于业务层上的应用
AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明方式灵活的进行事务的管理,提高开发效率和质量
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持
降低 JavaEE API 的使用难度
Spring 对 JavaEE API (如JDBC、JavaMail、远程调用等)进行了薄薄的封装,使这些API的使用难度大为降低
Java源码是经典学习范例
Spring 的源代码设计精妙,结构清晰,匠心独用,处处体现着大师对Java设计模式灵活运用以及对 Java 技术的高深造诣,它的源代码无疑是Java技术的最佳实践范例

Core Container (spring核心容器)
为其它模块提供支持
Data Access/Integration(持久层相关)
Test单元测试模块
工厂模式解耦
spring中工厂的类结构图

控制反转 IoC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度,其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)
还有一种方式叫依赖查找(Dependency Lookup),通过控制反转,对象在被创建的时候,依赖被注入到对象中
明确IoC的作用:削减计算机程序的耦合(解除代码中的依赖关系)
IOC是解耦的一种思想:解耦的途径有很多:比如依赖注入DI(只是解耦的一种解决方案)
程序中核心业务代码由主动创建对象变成被动的接收对象
★创建对象的控制权发生了转变(控制反转)
依赖注入:不在类中直接new对象,在外部需要使用本类时提供对象(可以通过工厂获取对象) (set注入,构造注入)
例如在业务层不直接提供持久层的实例
xxxxxxxxxx161public class TestServiceImpl implements TestService {2
3 private TestDao testDao;4
5 //提供一个构造方法覆盖掉原有构造6 public TestServiceImpl(TestDao testDao) {7 this.testDao = testDao;8 }9
10 11 public void findData() {12
13 testDao.findData();14
15 }16}
Inversion of Control
基于XML的配置
导入配置文件约束
xxxxxxxxxx61 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xsi:schemaLocation="http://www.springframework.org/schema/beans5 http://www.springframework.org/schema/beans/spring-beans.xsd">6</beans>创建一个xml文件,导入spring核心容器相关的约束 ==> 创建核心容器对象 ==> 从容器中去取出实例
1、spring是一个容器,里面存放了很多的实例
2、可以把spring理解成一个工厂,我们需要从工厂中获取实例
3、创建一个xml文件,导入spring核心容器相关的约束,可以使用spring提供的相关标签(标签里面是存放数据的)
4、spring会解析这个xml标签中的数据
ApplicationContext 接口的实现类:
ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext:
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
AnnotationConfigApplicationContext:
当我们使用注解配置容器对象时,需要使用此类来创建spring容器,它用来读取注解,需要提供核心配置类的字节码
在spring中,如何将一个类的实例装载到spring容器中?
基于xml的方式来创建spring核心容器对象:
bean标签的作用:将一个类的实例通过反射机制创建出来,装载进核心容器中,默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
id属性:唯一标识,不能重复的,给对象在容器中提供唯一标识,用于获取对象 class属性:指定类的全限定类名,用于反射创建对象,默认情况下调用无参构造函数
scope属性:可以去设置该实例的作用范围
xxxxxxxxxx11 <bean id="aaa" class="com.beans.User"></bean>
从容器中取出实例
xxxxxxxxxx201public static void main(String[] args) {2
3 //创建核心容器对象4 ApplicationContext applicationContext =5
6 new ClassPathXmlApplicationContext("xml/beans.xml");7
8 //要从容器中取出对应的实例9 Object bean = applicationContext.getBean("aaa");10
11 if (bean instanceof User){12
13 User user = (User)bean;14
15 //com.beans.User@42d80b7816 System.out.println(user);17
18 }19
20}sping在获取实例时的执行流程:
会解析xml文件,去读取每一个标签,如果发现有bean标签,那么会解析bean标签对应的属性,会去读取class属性的属性值,然后通过反射机制创建实例,保存到容器中,会将id属性作为该实例的标识
问:如果将一个实例存放进核心容器中,在程序中多次获取该实例,取得的实例是不是单例的?
默认情况下是单例的,但是我们可以将其修改为多例的
在标签下修改scope属性
singleton:默认值,单例的
prototype:多例的
request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
global session:WEB项目中,应用在Portlet环境,如果没有Porlet环境那么globalSession相当于session
xxxxxxxxxx51<!--和默认情况相同,是单例的-->2<bean id="user" class="com.beans.User" scope="singleton"></bean>3
4<!--修改为多例的-->5<bean id="user" class="com.beans.User" scope="prototype"></bean>
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中
依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台
通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现
property标签的作用:
要通过set方法在xml配置文件中实现依赖注入,在类中必须实现set方法
name属性:类的属性名
value属性:类的属性值
ref属性:可以去引入容器中别的实例,属性值就是需要传入的实例的唯一标识
xxxxxxxxxx81<bean id="user" class="com.beans.User" scope="prototype">2
3 <!--给id属性赋值-->4 <property name="id" value="1001"></property>5 <property name="name" value="root"></property>6 <property name="password" value="123456"></property>7 8</bean>
给数组赋值
xxxxxxxxxx81<!--给数组注入数据-->2<property name="objects">3 <array value-type="java.lang.String">4 <value>a</value>5 <value>b</value>6 <value>c</value>7 </array>8</property>
给自定义引用数据类型赋值
xxxxxxxxxx41<!--注入自定义的引用数据类型-->2<property name="student" ref="student"></property>3<!--将学生实例装载进核心容器中-->4<bean id="student" class="com.beans.Student"></bean>List
xxxxxxxxxx91<!--给list类型的属性注入数据-->2<property name="list">3 <list>4 <value>123456</value>5 <value>ture</value>6 <value>false</value>7 <value>1100</value>8 </list>9</property>Map
xxxxxxxxxx91<!--给map类型的属性注入数据-->2<property name="map">3 <map>4 <entry key="key1" value="value1"></entry>5 <entry key="key2" value="value2"></entry>6 <entry key="key3" value="value3"></entry>7 <entry key="key4" value="value4"></entry>8 </map>9</property>Date
xxxxxxxxxx61<!--注入日期类型数据-->2<property name="date" ref="date"></property>3
4<!--注册一个日期实例-->5<bean id="date" class="java.util.Date"></bean>6
基于构造方法完成对值的注入
constructor-arg
根据有参构造来创建实例
xxxxxxxxxx71<!--注册User实例-->2<bean id="user" class="com.os467.beans.User">3
4 <!--通过构造方法来为属性完成注入-->5 <constructor-arg name="username" value="zs001"></constructor-arg>6
7</bean>
多个参数构造创建实例
xxxxxxxxxx101<!--注册User实例-->2<bean id="user" class="com.os467.beans.User">3
4 <!--通过构造方法来为属性完成注入-->5 <constructor-arg name="username" value="zs001"></constructor-arg>6 <constructor-arg name="date" ref="date"></constructor-arg>7</bean>8
9<!--注册Date实例-->10<bean name="date" class="java.util.Date"></bean>
组件的作用范围是单例的:
init-method属性:对象在初始化时会执行的对应方法
destroy-method属性:对象在销毁时会执行的对应方法
xxxxxxxxxx11<bean id="user" class="com.os467.beans.User" scope="singleton" init-method="initMethod" destroy-method="destroyMethod">需要在类中定义方法
xxxxxxxxxx111public void initMethod(){2
3 System.out.println("初始化的方法执行了");4
5}6
7public void destroyMethod(){8
9 System.out.println("销毁的方法执行了");10
11}
关闭容器对象的方法
但是要用ClassPathXmlApplicationContext去接收才能调用此方法
xxxxxxxxxx21//关闭容器对象2applicationContext.close();关闭后,容器中的实例会被销毁
组件的作用范围是多例的:
BeanFactory才是Spring容器中的顶层接口
ApplicationContext是它的子接口
创建对象的时间点不一样
通过BeanFactory创建的容器,其中的实例在被用到的时候才会创建
xxxxxxxxxx51//通过BeanFactory创建容器2BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("xml/beans.xml"));3
4//此行执行User类实例的初始化函数5User user = beanFactory.getBean("user", User.class);
xxxxxxxxxx181<!--注册表现层实例-->2<bean id="accountServlet" class="com.os467.account.servlet.AccountServlet">3
4 <!--注入业务层的引用-->5 <property name="accountService" ref="accountService"></property>6
7</bean>8
9<!--注册业务层引用-->10<bean id="accountService" class="com.os467.account.service.AccountServiceImpl">11
12 <!--注入持久层的引用-->13 <property name="accountDao" ref="accountDao"></property>14
15</bean>16
17<!--注册持久层引用-->18<bean id="accountDao" class="com.os467.account.dao.AccountDaoImpl"></bean>
首先程序会去读取配置文件,然后获取文件中的全类名数据,根据反射创建对象(单例的),保存在容器里面,后面如果有用到实例的地方,我们从容器中去获取
xxxxxxxxxx931import java.io.FileReader;2import java.io.IOException;3import java.util.Enumeration;4import java.util.HashMap;5import java.util.Map;6import java.util.Properties;7
8/**9 * 工厂类10 */11public class BeanFactory {12
13 //模拟一个容器14 private static Map beansMap;15
16 private static Properties properties;17
18 //在静态代码块中去创建容器对象,为了保证存入容器中的实例是单例的,19 //所有的存放实例的步骤也要在类加载的时候去完成20 static {21
22 //实例化容器23 beansMap = new HashMap();24
25 //实例化properties集合26 properties = new Properties();27
28 FileReader fileReader = null;29
30 try {31
32 //创建流对象33 fileReader = new FileReader("src/beans");34
35 //读取数据到集合中36 properties.load(fileReader);37
38 //获取properties集合中所有的key39 Enumeration keys = properties.keys();40
41 //遍历所有的key42 while (keys.hasMoreElements()){43
44 //先获取对应key的字符串45 String key = keys.nextElement().toString();46
47 //根据key来获取value48 String propertyClass = properties.getProperty(key);49
50 //根据反射机制创建对象51 Object value = Class.forName(propertyClass).newInstance();52
53 //将实例存到容器里54 beansMap.put(key,value);55
56 }57
58 } catch (Exception e) {59
60 e.printStackTrace();61
62 }finally {63
64 try {65
66 if (fileReader != null){67
68 //关闭流对象69 fileReader.close();70
71 }72
73 } catch (IOException e) {74 e.printStackTrace();75 }76
77 }78
79
80 }81
82
83 /**84 * 定义一个获取实例的方法85 */86 public static Object getBean(String beanId){87
88 //从集合中获取实例89 return beansMap.get(beanId);90
91 }92
93}通过工厂获得业务层的实例
xxxxxxxxxx191package com.os467.factory.servlet;2
3import com.os467.factory.BeanFactory2;4import com.os467.factory.service.AccountService;5
6public class AccountServlet {7
8 //聚合业务层引用9 private AccountService accountService;10
11 public void saveAccount() {12
13 accountService = (AccountService) BeanFactory2.getBean("accountService");14
15 accountService.saveAccount();16
17 }18
19}
| 注解 | 作用 |
|---|---|
| @Component | 把资源让spring来管理,相当于在xml中配置一个bean |
| @Controller | 一般用于表现层的注解 |
| @Service | 一般用于业务层的注解 |
| @Repository | 一般用于持久层的注解 |
1、在配置文件中开启spring对注解的支持
2、直接在类上加对应的注解即可
在使用注解的IOC配置前需要先导入springAOP的jar包(底层需要)
配置文件约束
名称空间:xmlns:context
xxxxxxxxxx121 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:context="http://www.springframework.org/schema/context"5 xsi:schemaLocation="http://www.springframework.org/schema/beans6 http://www.springframework.org/schema/beans/spring-beans.xsd7 http://www.springframework.org/schema/context8 http://www.springframework.org/schema/context/spring-context.xsd">9
10 11
12</beans>开启spring对注解的支持,其实就是一个扫描包的过程
spring程序在执行的时候会去解析配置文件的数据,然后找到对应的包结构,并且扫描该包下对应的子包,然后会去检查每一个类上面有没有spring提供的注解,如果有,会将该类的实例装载进spring核心容器中
base-package:指定的包结构
xxxxxxxxxx21<!--开启spring对注解的支持-->2<context:component-scan base-package="com.os467"></context:component-scan>
在取出实例的时候,要注意,默认的标识是类的类名(首字符要小写)
xxxxxxxxxx71//创建核心容器对象2ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans02.xml");3
4//取出实例5Student student = applicationContext.getBean("student", Student.class);6
7System.out.println(student);类注解中的value值代表该类在获取时需要提供的id,给Student类自定义的提供一个id值
xxxxxxxxxx41("sss")2public class Student {3
4}
改变作用范围的注解
@Scope 指定bean 的作用范围
取值:
singleton
prototype
request
session
globalession
生命周期相关
@PostConstruct 用于指定初始化的方法
@PreDestroy 用于指定销毁的方法
xxxxxxxxxx171public class Student {2
3 4 public void initMethod(){5
6 System.out.println("初始化的方法执行了");7
8 }9
10 11 public void destroyMethod(){12
13 System.out.println("销毁的方法执行了");14
15 }16
17}
用于注入数据的注解
| 注解 | 作用 |
|---|---|
| @Autowired | 根据类型注入 |
| @Qualifier | 按名称注入,不能单独使用 |
| @Resource | 根据id注入的 |
| @Value | 用于注入基本数据类型和String类型 |
@Autowired注解
注入流程:首先spring在扫描到该属性上有Autowired注解之后,会去容器中找到该属性对应的类型与之注入
如果spring容器中与该属性类型匹配的实例有多个,那么究竟该注入哪个实例,这种情况下程序会报错
给这个类的实例起标识,起的标识一定要跟被注入属性的属性名称一致
@Qualifier注解
跟Autowired结合使用,指由Qualifier来决定究竟要注入哪个实例,需要提供注入实例的标识
Qualifier不能单独使用,只能和Autowried配合使用
@Resource注解
根据id去注入,相当于Autowired和Qualifier的结合
xxxxxxxxxx21//指定id2(name = "accountService")
@Value注解
给属性赋值,能赋值基本数据类型和String类型
xxxxxxxxxx21("jack")2String name;
通过设计模式+反射机制来模拟
1、spring的配置文件:beans.xml(开启扫描包的过程)spring会去扫描该包下所有子包的类,为了检查有没有spring提供的注解
2、要在指定的类上加注解,如果spring检查到该类上有注解的话,就会将该类的实例通过反射机制创建出来,装进spring中
3、检查该类中的属性上有没有spring提供的注解,如果有的话,会通过反射的方式去注入实例
修饰持久层的注解
xxxxxxxxxx121package com.os467.ioc.annotation;2
3import java.lang.annotation.ElementType;4import java.lang.annotation.Retention;5import java.lang.annotation.RetentionPolicy;6import java.lang.annotation.Target;7
8
9(ElementType.TYPE)10(RetentionPolicy.RUNTIME)11public @interface Repository {12}修饰业务层的注解
xxxxxxxxxx121package com.os467.ioc.annotation;2
3import java.lang.annotation.ElementType;4import java.lang.annotation.Retention;5import java.lang.annotation.RetentionPolicy;6import java.lang.annotation.Target;7
8
9(ElementType.TYPE)10(RetentionPolicy.RUNTIME)11public @interface Service {12}修饰表现层的注解
xxxxxxxxxx121package com.os467.ioc.annotation;2
3import java.lang.annotation.ElementType;4import java.lang.annotation.Retention;5import java.lang.annotation.RetentionPolicy;6import java.lang.annotation.Target;7
8
9(ElementType.TYPE)10(RetentionPolicy.RUNTIME)11public @interface Controller {12}根据属性类型注入属性实例的注解
xxxxxxxxxx111package com.os467.ioc.annotation;2
3import java.lang.annotation.ElementType;4import java.lang.annotation.Retention;5import java.lang.annotation.RetentionPolicy;6import java.lang.annotation.Target;7
8(ElementType.FIELD)9(RetentionPolicy.RUNTIME)10public @interface AutoWired {11}工厂类,用于储存实例
xxxxxxxxxx281package com.os467.ioc.factory;2
3import java.util.HashMap;4import java.util.Map;5
6/**7 * 工厂类,用于存放组件的实例(相当于容器)8 */9public class BeanFactory {10
11 //创建Map集合对象12 private static Map beansMap = new HashMap();13
14 public static Map getBeansMap() {15 return beansMap;16 }17
18 /**19 * 获取实例的方法20 */21
22 public static Object getBean(String beanId){23
24 return beansMap.get(beanId);25
26 }27
28}模拟spring底层基于注解Ioc解耦(扫描指定包结构下的类,创建所需要的实例)
xxxxxxxxxx2021package com.os467.ioc.scan;2
3import com.os467.ioc.annotation.AutoWired;4import com.os467.ioc.annotation.Controller;5import com.os467.ioc.annotation.Repository;6import com.os467.ioc.annotation.Service;7import com.os467.ioc.factory.BeanFactory;8
9import java.io.File;10import java.io.FileFilter;11import java.lang.annotation.Annotation;12import java.lang.reflect.Field;13import java.util.ArrayList;14import java.util.List;15import java.util.Map;16import java.util.Set;17
18/**19 * 模拟spring底层基于注解的Ioc解耦20 */21public class ComponentScan {22
23 //创建一个集合对象,用于存放全类名24 private static List<String> classNameList = new ArrayList();25
26 /**27 * 模拟装配实例,注入实例的过程28 * @param pathName29 */30 public static void getComponentScan(String pathName){31
32 //把"."替换成"/"33 pathName = pathName.replace(".","/");34
35 //获取当前工程的绝对路径36 String path = ClassLoader.getSystemResource("").getPath() + pathName;37
38 //创建一个File对象39 File file = new File(path);40
41 //过滤指定路径下的文件42 addFiles(file);43
44 //遍历集合45 for (String className : classNameList) {46
47 try {48
49 //创建字节码对象50 Class aClass = Class.forName(className);51
52 //根据反射检测类上有没有自定义的注解53 Controller controller = (Controller)aClass.getAnnotation(Controller.class);54 Service service = (Service)aClass.getAnnotation(Service.class);55 Repository repository = (Repository)aClass.getAnnotation(Repository.class);56
57 //如果类上有以上任意一个注解,我们就把该类添加到容器里面58 if (controller != null || service != null || repository != null){59
60 //根据反射创建实例61 Object obj = aClass.newInstance();62
63 //获取类的简类名64 String simpleName = aClass.getSimpleName();65
66 //将实例添加到工厂67 BeanFactory.getBeansMap().put(simpleName,obj);68
69 }70
71
72 } catch (ClassNotFoundException e) {73 e.printStackTrace();74 } catch (IllegalAccessException e) {75 e.printStackTrace();76 } catch (InstantiationException e) {77 e.printStackTrace();78 }79
80 }81
82 //继续遍历集合,将需要注入的属性进行注入83 for (String className : classNameList) {84
85 try {86
87 //创建字节码对象88 Class aClass = Class.forName(className);89
90 //根据反射检测类上有没有自定义的注解91 Controller controller = (Controller) aClass.getAnnotation(Controller.class);92 Service service = (Service) aClass.getAnnotation(Service.class);93 Repository repository = (Repository) aClass.getAnnotation(Repository.class);94
95 //如果类上有以上任意一个注解,继续检查类中的属性有没有自定义的注解96 if (controller != null || service != null || repository != null) {97
98 //获取属性字节码对象99 Field[] declaredFields = aClass.getDeclaredFields();100
101 //遍历属性数组102 for (Field declaredField : declaredFields) {103
104 //检查属性上有没有自定义的注解105 AutoWired autoWired = declaredField.getAnnotation(AutoWired.class);106
107 //如果这个注解不为空,就意味着该属性需要注入108 if (autoWired != null){109
110 //获取容器对象111 Map beansMap = BeanFactory.getBeansMap();112
113 //将容器对象转成set集合114 Set<Map.Entry> entrySet = beansMap.entrySet();115
116 //遍历set集合117 for (Map.Entry entry : entrySet) {118
119 //获取每个实例实现的接口120 Class[] interfaces = entry.getValue().getClass().getInterfaces();121
122 // 遍历接口数组,因为AutoWired是根据类型注入的,我们要保证注入的实例跟聚合的属性是同一类型123 for (Class anInterface : interfaces) {124
125 if (anInterface == declaredField.getType()){126
127 //打破封装128 declaredField.setAccessible(true);129
130 //给属性赋值131 declaredField.set(BeanFactory.getBean(aClass.getSimpleName()),entry.getValue());132
133 }134
135 }136
137 }138
139 }140
141 }142
143 }144
145
146 } catch (Exception e) {147 e.printStackTrace();148 }149
150 }151
152 }153
154 /**155 * 定义一个递归的方法,用于过滤符合条件的文件156 * file.listFiles():将所有的文件或者是文件夹切成一个数组157 * @param file158 */159 public static void addFiles(File file){160
161 //获取该文件夹下面所有的子文件夹,FileFilter接口实现类用于返回符合条件的文件162 File[] files = file.listFiles(new FileFilter() {163 164 public boolean accept(File pathname) {165
166 //判断文件的类型167 if (pathname.isDirectory()){168
169 //继续调用递归的方法170 addFiles(pathname);171
172 }173
174 //我们过滤的文件一般是以.class结尾的175 return pathname.getPath().endsWith(".class");176
177 }178 });179
180 //遍历文件的数组,将符合要求的文件路径切割成全类名的形式181 for (File f : files) {182
183 String path = f.getPath();184
185 //将所有的"\"替换成"."186
187 path = path.replace("\\",".");188
189 //将com前面的字符串删了190 path = path.substring(path.lastIndexOf("com"),path.length());191
192 //将".class"切割掉193 path = path.replace(".class","");194
195 //将切割好的全类名添加到集合里面196 classNameList.add(path);197
198 }199
200 }201
202}
1、之前创建核心容器对象都是基于xml的,必须得有配置文件
2、spring5.0版本之后也是支持基于注解的方式创建核心容器对象,这样我们的程序就可以脱离xml配置文件
sping5.0版本后新的注解:
@Configuration
声明当前的类是一个配置类,仅起到标识作用
@ComponentScan
传入一个指定的包结构路线,开启扫描包,如果被@Component或其它特定注解修饰的类就会被添加到核心容器中
@Bean
@Autowired是一样的,都是根据类型去匹配的
@Qualifier来修饰参数指定实例标识
注意:如果方法名称相同,即使用@Bean起了别名,有参的也会覆盖掉无参的
配置类
xxxxxxxxxx2212public class SpringConfig {3
4 5 public User getUser(Student student){6 7 User user = new User();8 9 user.setStudent(student);10
11 return user;12
13 }14
15 ("student")16 public Student getStudent(){17
18 return new Student();19
20 }21
22}测试类
xxxxxxxxxx161public static void main(String[] args) {2
3 //创建核心容器对象4 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);5
6 //从容器中取实例7 Object obj = applicationContext.getBean("getUser");8
9 if (obj instanceof User){10
11 User user = (User)obj;12
13
14 }15
16}
@PropertySource
引入配置文件,可以通过@Value注解读取数据
classpath: 读取的配置文件资源需要放在src文件下,可以通过el表达式取到值
xxxxxxxxxx2312("classpath:jdbc")3public class JdbcConfig {4
5 ("${name}")6 private String name;7
8 ("${password}")9 private String password;10
11 ("user")12 public User getUser(){13
14 User user = new User();15
16 user.setName(name);17 user.setPassword(password);18
19 return user;20
21 }22
23}配置文件jdbc
xxxxxxxxxx21name=root2password=root
@Import
可以让两个核心配置类之间存在继承的关系
引入其它核心配置类,需要提供一个配置类的字节码属性
xxxxxxxxxx11(SpringConfig.class)
基于核心配置类配置三层架构
xxxxxxxxxx601package com.os467.testRe.config;2
3
4import com.os467.Work.annotation.AutoWired;5import com.os467.testRe.dao.TestDao;6import com.os467.testRe.dao.TestDaoImpl;7import com.os467.testRe.service.TestServiceImpl;8import com.os467.testRe.servlet.TestServlet;9import org.springframework.beans.factory.annotation.Qualifier;10import org.springframework.context.annotation.Bean;11import org.springframework.context.annotation.ComponentScan;12import org.springframework.context.annotation.Configuration;13
14import javax.annotation.Resource;15
16("com.os467.testRe")1718public class SpringConfiguration2 {19
20 /**21 * 获取实例22 */23 (name = "TestServlet")24 private TestServlet testServlet;25
26 (name = "TestServiceImpl")27 private TestServiceImpl testService;28
29 (name = "TestDaoImpl")30 private TestDaoImpl testDao;31
32 /**33 * 为实例注入属性34 * @param testService35 * @return36 */37 38 public TestServlet getTestServlet(("getTestServiceImpl")TestServiceImpl testService) {39
40 testServlet.setTestService(testService);41
42 return testServlet;43
44 }45
46 47 public TestServiceImpl getTestServiceImpl(("getTestDaoImpl") TestDaoImpl testDao) {48
49 testService.setTestDao(testDao);50
51 return testService;52 }53
54 55 public TestDaoImpl getTestDaoImpl() {56
57 return testDao;58
59 }60}
总结
XML开发:
注解开发:
需要配置bean , @Bean与
需要扫描包,
AOP:全称是 Aspect Oriente Programming
(面向切面编程)
作用:
在程序运行期间,不修改源码对已有方法进行增强
优势:
Joinpoint(连接点):
连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,指的就是动态代理在底层可以访问到的所有方法
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义,就是要对哪些连接点(方法)进行增强,即需要被增强的方法
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知的类型:
前置通知:
后置通知:
异常通知:
最终通知:
环绕通知:
Target(目标对象):
代理的目标对象
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程(增强的过程)
spring采用动态代理织入,而AspectJ采用编译器织入和类装载器织入
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面):
是切入点(要增强的方法)和通知(怎么增强)之间的关系
场景:
之前学习jdbc的时候我们接触到了事务,A账户给B账户赚钱,我们需要加入事务的操作,要满足事务的一致性
如果用代理模式来完成对事务的织入,程序该如何去写?
使用JDK动态代理来为目标类创建代理类
xxxxxxxxxx971package com.os467.proxy;2
3import com.os467.account.service.AccountService;4import com.os467.utils.TransactionMangerUtils;5import org.springframework.beans.factory.annotation.Autowired;6import org.springframework.stereotype.Component;7
8import java.lang.reflect.InvocationHandler;9import java.lang.reflect.Method;10import java.lang.reflect.Proxy;11
12/**13 * 为业务层做代理14 */1516public class AccountProxy {17
18 //目标对象,需要被代理的对象19 20 private AccountService accountService;21
22 /**23 * 获取代理对象24 * @return25 */26 public Object getProxy(){27
28 return Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {29
30 /**31 * 完成对目标方法的增强32 *33 * 在springAop中:34 *35 * 前置通知:调用目标方法之前执行的内容36 * 后置通知:调用目标方法之后执行的内容37 * 异常通知:在catch语句块中织入的内容38 * 最终通知:在finally语句块中织入的内容39 * 环绕通知:整个invoke方法执行的过程叫做环绕通知40 *41 * @param proxy42 * @param method43 * @param args44 * @return45 * @throws Throwable46 */47 48 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {49
50 Object invoke = null;51
52 try {53
54 if (method.getName().equals("transfer")){55
56 //开启事务57 TransactionMangerUtils.beginTransaction();58
59 }60
61 //调用目标类方法62 invoke = method.invoke(accountService, args);63
64 if (method.getName().equals("saveAccount")){65
66 //提交事务67 TransactionMangerUtils.commitTransaction();68
69 }70
71 }catch (Exception e){72
73 //回滚事务74 TransactionMangerUtils.rollBackTransaction();75
76 //关闭事务77 TransactionMangerUtils.closeTransaction();78
79 e.printStackTrace();80 }finally {81
82 if (method.getName().equals("saveAccount")){83
84 //关闭事务85 TransactionMangerUtils.closeTransaction();86
87 }88
89 }90
91 return invoke;92
93 }94 });95
96 }97}测试类
xxxxxxxxxx181public class AopTest01 {2
3 private static ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);4
5
6 public static void main(String[] args) {7
8 //从容器中取出代理对象9 AccountProxy accountProxy = applicationContext.getBean("accountProxy", AccountProxy.class);10
11 AccountService proxy = (AccountService)accountProxy.getProxy();12
13 proxy.transfer();14 proxy.saveAccount();15
16 }17
18}
通过xml来配置aop(切面):我们配置的是切入点和通知之间的关系
我们想在项目中去加入收集日志的功能,但是不能在核心模块中出现,会通过aop配置切面的方式来完成对核心模块中核心方法的增强
引入约束
xxxxxxxxxx101 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:aop="http://www.springframework.org/schema/aop"5 xsi:schemaLocation="http://www.springframework.org/schema/beans6 http://www.springframework.org/schema/beans/spring-beans.xsd7 http://www.springframework.org/schema/aop8 http://www.springframework.org/schema/aop/spring-aop.xsd">9 10</beans>标签:
aop:config:
作用:用于声明开始aop的配置
aop:aspect:
作用:配置切面
aop:pointcut:
作用:配置切入点表达式
xxxxxxxxxx11<aop:before method="beforeAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:before>第一个*标识当前工程,后面是资源路径,类.*标识该类下的所有方法,(..)代表匹配所有无参,(String,int)表示匹配指定参数的方法
aop:before:
作用:用于配置前置通知
aop:after-returning:
作用:用于配置后置通知
配置切面
xxxxxxxxxx371<!--配置一个目标类的实例-->2<bean id="aopMethod" class="com.os467.aop.method.AopMethod"></bean>3
4<!--配置通知类的实例-->5<bean id="aopLogger" class="com.os467.aop.logger.Logger"></bean>6
7<!--开始配置Aop-->8<aop:config>9
10 <!--11 配置切面:12
13 要去引入一个通知类14
15 -->16 <aop:aspect id="idAspect" ref="aopLogger">17
18 <!--19 开始配置通知:20 pointcut:配置切入点表达式,就是要设置通知的作用范围21 -->22
23 <!--配置前置通知-->24 <aop:before method="beforeAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:before>25
26 <!--配置后置通知-->27 <aop:after-returning method="afterAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after-returning>28
29 <!--配置异常通知-->30 <aop:after-throwing method="exceptionAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after-throwing>31
32 <!--最终通知-->33 <aop:after method="endAdvice" pointcut="execution(* com.os467.aop.method.AopMethod.*(..))"></aop:after>34
35 </aop:aspect>36
37</aop:config>
外部声明切入点表达式
xxxxxxxxxx81<!--声明一个切入点表达式-->2<aop:pointcut id="pt01" expression="execution(* com.os467.aop.method.AopMethod.*(..))"/>3
4<!--配置前置通知-->5<aop:before method="beforeAdvice" pointcut-ref="pt01"></aop:before>6
7<!--配置后置通知-->8<aop:after-returning method="afterAdvice" pointcut-ref="pt01"></aop:after-returning>
通知类
xxxxxxxxxx441package com.os467.aop.logger;2
3/**4 * 日志管理类(通知类)5 */6public class Logger {7
8 /**9 * 前置通知10 */11 public void beforeAdvice(){12
13 System.out.println("前置通知开始记录日志了");14
15 }16
17 /**18 * 后置通知19 */20 public void afterAdvice(){21
22 System.out.println("后置通知开始记录日志了");23
24 }25
26 /**27 * 异常通知28 */29 public void exceptionAdvice(){30
31 System.out.println("异常通知开始记录日志了");32
33 }34
35 /**36 * 最终通知37 */38 public void endAdvice(){39
40 System.out.println("最终通知开始记录日志了");41
42 }43
44}
需要被增强的核心类
xxxxxxxxxx261package com.os467.aop.method;2
3/**4 * 需要加入收集日志功能的核心类5 */6public class AopMethod {7
8 public void getMethod01(){9
10 System.out.println("getMethod01方法正在执行");11
12 }13
14 public void getMethod02(){15
16 System.out.println("getMethod02方法正在执行");17
18 }19
20 public void getMethod03(){21
22 System.out.println("getMethod03方法正在执行");23
24 }25
26}
测试类
xxxxxxxxxx171public class AopTest {2
3 public static void main(String[] args) {4
5 //从容器中把目标类的实例取出来6 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans.xml");7
8 //取出实例9 AopMethod aopMethod = applicationContext.getBean("aopMethod", AopMethod.class);10
11 //调用需要增强的方法12 aopMethod.getMethod01();13
14 }15
16
17}
开启AOP对注解的支持
xxxxxxxxxx181 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:aop="http://www.springframework.org/schema/aop"5 xmlns:context="http://www.springframework.org/schema/context"6 xsi:schemaLocation="http://www.springframework.org/schema/beans7 http://www.springframework.org/schema/beans/spring-beans.xsd8 http://www.springframework.org/schema/aop9 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">10
11 <!--开启spring的扫描包-->12 <context:component-scan base-package="com.os467"></context:component-scan>13
14 <!--开启AOP对注解支持-->15 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>16 17 18</beans>
@Aspect
声明当前的类是一个切面类
@Pointcut
修饰一个方法,注解中的值为切入点表达式
@Before
前置通知
@AfterReturning
后置通知
@AfterThrowing
异常通知
@After
最终通知
通过注解配置通知类
xxxxxxxxxx541/**2 * 日志管理类(通知类)3 */4("logger")56public class Logger {7
8 /**9 * 声明一个切入点表达式10 */11 ("execution(* com.os467.aop.method.AopMethod.*(..))")12 public void pt01(){};13
14 /**15 * 前置通知16 */17 ("pt01()")18 public void beforeAdvice(){19
20 System.out.println("前置通知开始记录日志了");21
22 }23
24 /**25 * 后置通知26 */27 ("pt01()")28 public void afterAdvice(){29
30 System.out.println("后置通知开始记录日志了");31
32 }33
34 /**35 * 异常通知36 */37 ("pt01()")38 public void exceptionAdvice(){39
40 System.out.println("异常通知开始记录日志了");41
42 }43
44 /**45 * 最终通知46 */47 ("pt01()")48 public void endAdvice(){49
50 System.out.println("最终通知开始记录日志了");51
52 }53
54}
测试类
xxxxxxxxxx131public class AopTest02 {2
3 public static void main(String[] args) {4
5 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xml/beans02.xml");6
7 AopMethod aopMethod = applicationContext.getBean("aopMethod", AopMethod.class);8
9 aopMethod.getMethod01();10
11 }12 13}
环绕通知
在配置环绕通知的时候可以不去配置前置,后置,异常,结束这些通知,因为可以在环绕通知中定义这些通知
xml配置环绕通知
xxxxxxxxxx21<!--配置环绕通知-->2<aop:around method="aroundAdvice" pointcut-ref="pt01"></aop:around>注解配置环绕通知
xxxxxxxxxx21("pt01()")2public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){环绕通知具体内容}增强方法内容
xxxxxxxxxx311/**2 * 环绕通知,需要给它一个接口参数,spring会自动提供实例3 */4public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){5
6 //获取调用目标方法时所对应的参数7 Object[] args = proceedingJoinPoint.getArgs();8
9 //调用目标方法10 try {11
12 System.out.println("前置通知");13
14 //类似于invoke方法15 proceedingJoinPoint.proceed(args);16
17 System.out.println("后置通知");18
19 } catch (Throwable throwable) {20
21 System.out.println("异常通知");22
23 throwable.printStackTrace();24 }finally {25
26 System.out.println("结束通知");27
28 }29
30
31}
单元测试在底层是封装了main函数的,在不写主函数的情况下,可以去让程序跑起来
通过spring来集成单元测试(可以在不创建核心容器的情况下,取到容器中的实例)
@Test
在方法上加上Test注解,该方法就会变成像主函数一样可以运行
xxxxxxxxxx612public void test01(){3
4 System.out.println("成功运行了该方法");5
6}
在程序运行的时候动态的获取核心容器对象
@RunWith
这个注解可以替换运行器,我们选用spring提供的运行器
@ContextConfiguration
指定如何创建核心容器对象,需要提供xml路径或者是配置类class
xxxxxxxxxx231/**2 * @RunWith 这个注解可以替换运行器3 * SpringJUnit4ClassRunner 这个类实现了运行器接口,可以在spring程序运行的时候获取核心容器对象4 * @ContextConfiguration 以什么方式创建核心容器对象(xml/注解)5 */6(SpringJUnit4ClassRunner.class)7(locations = "classpath:xml/beans.xml")8public class JunitTest01 {9
10 11 private ApplicationContext applicationContext;12
13 14 public void test01(){15
16 //取出实例17 AccountServiceImpl service = applicationContext.getBean("service", AccountServiceImpl.class);18
19 service.saveAccount();20 21 }22 23}基于核心配置类创建容器(注解注册)
xxxxxxxxxx11(classes = SpringConfig.class)
jdbcTemplate对JDBC做了封装,相当于是一个工具类
xxxxxxxxxx251//创建jdbcTemplate模板对象2JdbcTemplate jdbcTemplate = new JdbcTemplate();3
4//创建数据源的示例5DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();6
7//设置连接地址8driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8");9
10//设置驱动全类名11driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");12
13//设置用户名14driverManagerDataSource.setUsername("root");15
16//设置密码17driverManagerDataSource.setPassword("root");18
19//给模板对象注入数据源的实例20jdbcTemplate.setDataSource(driverManagerDataSource);21
22//执行一个添加的功能23int num = jdbcTemplate.update("insert into tb_user(username,password)values('tom','10010011')");24
25System.out.println(num == 1 ?"添加成功":"添加失败");
| 表达式 | 符号 |
|---|---|
| < | < |
| > | > |
| & | & |
| ' | ' |
| " | " |
通过xml配置注入jdbcTemplate模板实例
xxxxxxxxxx351 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:aop="http://www.springframework.org/schema/aop"5 xmlns:context="http://www.springframework.org/schema/context"6 xsi:schemaLocation="http://www.springframework.org/schema/beans7 http://www.springframework.org/schema/beans/spring-beans.xsd8 http://www.springframework.org/schema/aop9 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">10
11 <!--开启对注解的支持-->12 <context:component-scan base-package="com.os467"></context:component-scan>13
14 <!--配置一个jdbcTemplate实例-->15 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">16
17 <!--注入数据源的实例-->18 <property name="dataSource" ref="dataSource"></property>19
20 </bean>21
22 <!--注入一个数据源的实例-->23 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">24
25 <!--注入源数据-->26 <property name="url" value="jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8"></property>27 <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>28 <property name="username" value="root"></property>29 <property name="password" value="root"></property>30
31 </bean>32
33
34
35</beans>junit测试类
xxxxxxxxxx971package com.os467.test;2
3import com.os467.account.service.AccountService;4import com.os467.account.service.AccountServiceImpl;5import com.os467.beans.User;6import com.os467.config.SpringConfig;7import com.os467.user_test.service.UserService;8import org.junit.Test;9import org.junit.runner.RunWith;10import org.springframework.beans.factory.annotation.Autowired;11import org.springframework.context.ApplicationContext;12import org.springframework.context.support.ClassPathXmlApplicationContext;13import org.springframework.jdbc.core.JdbcTemplate;14import org.springframework.jdbc.datasource.DriverManagerDataSource;15import org.springframework.test.context.ContextConfiguration;16import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;17
18import java.util.List;19
20/**21 * @RunWith 这个注解可以替换运行器22 * SpringJUnit4ClassRunner 这个类实现了运行器接口,可以在spring程序运行的时候获取核心容器对象23 * @ContextConfiguration 以什么方式创建核心容器对象(xml/注解)24 */25(SpringJUnit4ClassRunner.class)26(locations = "classpath:xml/beans.xml")27public class JunitTest01 {28
29 30 private AccountService accountService;31
32 33 private UserService userService;34
35 36 public void test01(){37
38 accountService.saveAccount();39
40 }41
42 /**43 * 通过jdbcTemplate模板连接数据库44 */45 46 /**47 * 测试添加的功能48 */49 public void test02(){50
51 int i = userService.addUser();52
53 System.out.println(i == 1 ?"添加成功":"添加失败");54
55 }56
57 /**58 * 测试删除功能59 */60 61 public void test03(){62
63 int i = userService.deleteUserById();64
65 System.out.println(i == 1 ?"删除成功":"删除失败");66
67 }68
69 /**70 * 测试修改功能71 */72 73 public void test04(){74
75 int i = userService.updateUserById();76
77 System.out.println(i == 1 ?"修改成功":"修改失败");78
79 }80
81 /**82 * 测试查询的功能83 */84 85 public void test05(){86
87 List<User> userList = userService.findAllByUsers();88
89 for (User user : userList) {90
91 System.out.println(user);92
93 }94
95 }96
97}持久层
xxxxxxxxxx581package com.os467.user_test.dao;2
3import com.os467.beans.User;4import org.springframework.beans.factory.annotation.Autowired;5import org.springframework.jdbc.core.JdbcTemplate;6import org.springframework.jdbc.core.RowMapper;7import org.springframework.stereotype.Repository;8
9import java.sql.ResultSet;10import java.sql.SQLException;11import java.util.List;12
1314public class UserDaoImpl implements UserDao {15
16 17 private JdbcTemplate jdbcTemplate;18
19 20 public List<User> findAllByUsers() {21 return jdbcTemplate.query("select * from tb_user", new RowMapper<User>() {22 23 public User mapRow(ResultSet resultSet, int i) throws SQLException {24
25 int id = resultSet.getInt("id");26
27 String username = resultSet.getString("username");28
29 String password = resultSet.getString("password");30
31 //对java对象的封装,创建User实例32 User user = new User(id, username, password);33
34 return user;35 }36 });37 }38
39 40 public int deleteUserById() {41 return jdbcTemplate.update("delete from tb_user where id = 7");42 }43
44 /**45 * 修改46 * @return47 */48 49 public int updateUserById() {50 return jdbcTemplate.update("update tb_user set username = ?,password = ? where id = ?","tom","123",6);51 }52
53 54 public int addUser() {55
56 return jdbcTemplate.update("insert into tb_user(username,password)values('tom','111')");57 }58}
此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法
管理事务的子类对象:
1.DataSourceTransactionManager使用SpringJDBC或iBatis进行持久化数据时使用
2.HibernateTransactionManager使用Hibernate版本进行持久化数据时使用
配置步骤:
1、配置事务管理器
2、配置事务的通知引用事务管理器
3、配置事务的属性
4、配置AOP切入点表达式
5、配置切入点表达式和事务通知的对应关系
引入xmlns:tx相关约束
xxxxxxxxxx111xmlns:tx="http://www.springframework.org/schema/tx"2http://www.springframework.org/schema/tx3http://www.springframework.org/schema/tx/spring-tx.xsd 4
5<!--配置一个事务管理器-->6<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">7
8 <!--注入数据源实例-->9 <property name="dataSource" ref="dataSource"></property>10
11</bean>场景:银行转账的场景
isolation:
获取事务的隔离级别
默认值是DEFAULT,数据库级别的
propagation:
获取事务的传播行为,表示是否开启事务,以什么样的策略开启
REQUIRED表示一定会开启事务,无论是增删改查都会开启
SUPPORTS表示有事务就会开启事务,没有事务就不会开启,会在增删改 的场景下开启事务,查询场景不会开启
timeout:
获取超时时间,默认值是-1,永不超时,如果是正数的话,可以以秒为单 位设置超时时间
read-only:
会影响事务是否开启
获取是否是只读事务(true/false),默认值是false,在查询的业务场景下,会把该属性设置成true
rollback-for:
是否开启回滚,默认值是true 自定义一个异常,除了该异常回滚,所有异常都不回滚 no-rollback-for:
自定义一个异常,除了该异常不回滚,所有异常都回滚
默认情况下,所有异常都回滚
只有在增删改的业务场景下,才会取开启事务,查询业务场景下不用开启事务,或者是只读就行
注意:spring中单个事务的生命周期只发生在在一个方法中(需要被AOP增强的每个方法)
如何配置事务管理器
xxxxxxxxxx71<!--配置一个事务管理器-->2<bean id="dataSourceManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">3
4 <!--注入数据源实例-->5 <property name="dataSource" ref="dataSource"></property>6
7</bean>如何配置事务的通知
xxxxxxxxxx171<!--配置一个事务的通知-->2<tx:advice transaction-manager="dataSourceManager">3
4 <!--配置事务的属性-->5 <tx:attributes>6
7 <!--8 <tx:method name=""/>9 name属性:用于匹配需要织入事务的方法10 设置匹配规则,匹配方法名称,*代表通配符11 isolation:事务的隔离级别12 -->13
14 <tx:method name="update*" isolation="DEFAULT" />15
16 </tx:attributes>17</tx:advice>
完整的spring事务织入xml配置
xxxxxxxxxx681 2<beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xmlns:aop="http://www.springframework.org/schema/aop"5 xmlns:context="http://www.springframework.org/schema/context"6 xmlns:tx="http://www.springframework.org/schema/tx"7 xsi:schemaLocation="http://www.springframework.org/schema/beans8 http://www.springframework.org/schema/beans/spring-beans.xsd9 http://www.springframework.org/schema/tx10 http://www.springframework.org/schema/tx/spring-tx.xsd11 http://www.springframework.org/schema/aop12 http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">13
14
15
16 <!--注册业务层实例-->17 <bean id="service" class="com.os467.tx.account.service.AccountServiceImpl"></bean>18
19 <!--注册持久层的实例-->20 <bean id="dao" class="com.os467.tx.account.dao.AccountDaoImpl"></bean>21
22 <!--注册jdbc模板对象-->23 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">24 <!--注入数据源实例-->25 <property name="dataSource" ref="dataSource"></property>26
27 </bean>28
29 <!--注册数据源实例-->30 <bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">31 <!--注入数据源信息-->32 <property name="url" value="jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8"></property>33 <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>34 <property name="username" value="root"></property>35 <property name="password" value="root"></property>36 </bean>37
38 <!--配置一个事务管理器-->39 <bean id="dataSourceManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">40
41 <!--注入数据源实例-->42 <property name="dataSource" ref="dataSource"></property>43
44 </bean>45
46 <!--配置一个事务的通知-->47 <tx:advice id="txAdvice" transaction-manager="dataSourceManager">48
49 <!--配置事务的属性-->50 <tx:attributes>51
52 <tx:method name="*" propagation="REQUIRED" read-only="false"/>53
54 </tx:attributes>55 </tx:advice>56
57 <!--配置aop-->58 <aop:config>59
60 <!--配置一个切入点表达式-->61 <aop:pointcut id="pt01" expression="execution(* com.os467.tx.account.service.*.*(..))"/>62
63 <!--配置切面-->64 <aop:advisor advice-ref="txAdvice" pointcut-ref="pt01"></aop:advisor>65
66 </aop:config>67
68</beans>
配置步骤:
1、配置事务管理器并注入数据源
2、在业务层使用@Transactional注解
3、在配置文件中开启spring对注解事务的支持
@Transactional
事务注解,可以修饰类,也可以修饰方法
修饰类:该类下所有的方法都会加入事务的支持
修饰方法:只有指定方法会加入事务支持,方法的优先级比类要高
注意:使用注解,事务管理器还需要在xml中配置,但是事务通知可以不用配置了
xxxxxxxxxx201package com.os467.account.service;2import com.os467.account.dao.AccountDao;3import org.springframework.beans.factory.annotation.Autowired;4import org.springframework.transaction.annotation.Propagation;5import org.springframework.transaction.annotation.Transactional;6
7(propagation = Propagation.REQUIRED)8public class AccountServiceImpl implements AccountService {9
10 11 private AccountDao accountDao;12
13 14 public void saveAccount() {15
16 accountDao.savAccount();17
18 }19}20