什么是Spring?
- Spring 是轻量级的开源的 JavaEE 框架
- Spring 可以解决企业应用开发的复杂性
- Spring 有两个核心部分:IOC 和 Aop
- IOC:控制反转,把创建对象过程交给 Spring 进行管理
- Aop:面向切面,不修改源代码进行功能增强
Spring 特点:
- 方便解耦,简化开发
- Aop 编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低 API 开发难度
Spring架构图:
Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器(Core Container) 、 AOP(Aspect Oriented Programming)和设备支持(Instrmentation) 、数据访问与集成(Data Access/Integeration) 、 Web、 消息(Messaging) 、 Test等 6 个模块中。
入门案例
1.导入基础的包
还需要junit及其依赖hamcrest-core
2.创建java实体类
1 2 3 4 5 6 7 8
| package com.me.spring5;
public class User { public void add(){ System.out.println("add...."); } }
|
3.创建Bean.xml配置文件
1 2 3 4 5 6 7
| <?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="user" class="com.me.spring5.User"></bean> </beans>
|
4.编写测试类测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.me.spring5.test;
import com.me.spring5.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring5 {
@Test public void test(){ ApplicationContext context= new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("user", User.class); System.out.println(user); user.add(); } }
|
IOC概念与原理
1.什么是IOC?
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则
Spring IOC 负责创建对象,管理对象,通过依赖注入,装配对象,配置对象,并且管理这些对象的整个生命周期。
控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
使用 IOC 目的:为了耦合度降低
做入门案例就是 IOC 实现
———摘自百度百科
2.IOC底层原理
- IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂,Spring提供的IOC容器实现的两种方式(两个接口)
- BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
- ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
- ApplicationContext有两个实现类
- FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean配置文件的全路径名必须提供给它的构造函数。(系统路径,提供盘符的全路径)
- ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。(项目路径,提供对应类的包路径)
IOC操作(Bean管理)
什么是Bean管理?
Bean 管理指的是两个操作 :
Bean管理操作的方式
1.基于 xml 配置文件方式实现
创建对象
1
| <bean id="user" class="com.me.spring5.User"></bean>
|
注入属性
DI:依赖注入,就是注入属性
使用 set 方法进行注入
1.创建类,定义属性和对应的 set 方法
1 2 3 4 5 6 7 8 9 10 11 12
| public class Book { private String bname; private String bauthor; public void setBname(String bname) { this.bname = bname; } public void setBauthor(String bauthor) { this.bauthor = bauthor; } }
|
2.在 spring 配置文件配置对象创建,配置属性注入
1 2 3 4 5 6 7 8
| <bean id="book" class="com.atguigu.spring5.Book">
<property name="bname" value="易筋经"></property> <property name="bauthor" value="达摩老祖"></property> </bean>
|
使用有参数构造进行注入
1.创建类,定义属性,创建属性对应有参数构造方法
1 2 3 4 5 6 7 8 9 10 11
| public class Orders { private String oname; private String address; public Orders(String oname,String address) { this.oname = oname; this.address = address; } }
|
2.在 spring 配置文件中进行配置
1 2 3 4
| <bean id="orders" class="com.atguigu.spring5.Orders"> <constructor-arg name="oname" value="电脑"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
|
xml注入其他类型属性
字面量
字面量
字面量(literal)是用于表达源代码中一个固定值的表示法
1.null值
1 2 3 4 5
| <bean id="user" class="com.me.spring5.User"> <property name="id"> <null></null> </property> </bean>
|
2.特殊符号
列如出现了<<>>
符号,会被解析为标签符号。
1 2 3 4 5 6 7 8
| <bean id="user" class="com.me.spring5.User"> <constructor-arg name="id" value="1"></constructor-arg> <property name="id"> <value> <![CDATA[value]]> </value> </property> </bean>
|
注入属性-外部bean
- 注入属性-外部bean
- 创建两个类 service 类和 dao 类 (service 含有属性是dao类)
- 在 service 调用 dao 里面的方法
- 在 spring 配置文件中进行配置
Dao:
1 2 3 4 5 6 7 8 9 10 11
| package com.me.spring5;
public class Dao { public Dao() { }
public void add(){ System.out.println("dao is adding...."); } }
|
Service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.me.spring5;
import com.me.spring5.Dao;
public class Service { private Dao dao;
public Service() { }
public Service(Dao dao) { this.dao = dao; }
public void setDao(Dao dao) { this.dao = dao; }
public void using(){ System.out.println("service is using...."); dao.add(); } }
|
1 2 3 4 5
| <bean name="service" class="com.me.spring5.Service"> <property name="dao" ref="dao"></property> </bean>
<bean name="dao" class="com.me.spring5.Dao"></bean>
|
name 属性:类里面属性名称
ref 属性:创建 userDao 对象 bean 标签 id 值
注入属性-内部 bean
1 2 3 4 5 6 7
| public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Emp { private String ename; private String gender; private Dept dept; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <property name="dept"> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="安保部"></property> </bean> </property> </bean>
|
- 可以理解为匿名内部类赋值
- 因此这种类是IOC容器无法拿到的类
注入属性-级联赋值
第一种写法:
1 2 3 4 5 6 7 8 9 10 11
| <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="财务部"></property> </bean>
|
第二种写法:
1 2 3 4 5 6 7 8 9 10 11 12
| <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技术部"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="财务部"></property> </bean>
|
- 第二种写法需要dept的set方法,因为是基于反射得到的对象进行赋值
注入集合属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Stu { private String[] courses; private List<String> list; private Map<String,String> maps; private Set<String> sets; public void setSets(Set<String> sets) { this.sets = sets; } public void setCourses(String[] courses) { this.courses = courses; } public void setList(List<String> list) { this.list = list; } public void setMaps(Map<String, String> maps) { this.maps = maps; } }
|
1 2 3 4 5 6 7
| <property name="courses"> <list> <value>语文</value> <value>数学</value> </list> </property>
|
1 2 3 4 5 6 7
| <property name="list"> <list> <value>aaa</value> <value>bbb</value> </list> </property>
|
1 2 3 4 5 6 7
| <property name="maps"> <map> <entry key="key1" value="v1"></entry> <entry key="key2" value="v2"></entry> </map> </property>
|
1 2 3 4 5 6 7
| <property name="sets"> <set> <value>java</value> <value>c++</value> </set> </property>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <bean id="course1" class="com.atguigu.spring5.collectiontype.Course"> <property name="cname" value="Spring5 框架"></property> </bean> <bean id="course2" class="com.atguigu.spring5.collectiontype.Course"> <property name="cname" value="MyBatis 框架"></property> </bean> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property>
|
- list抽取
- 在 spring配置文件中引入名称空间 util
1 2 3 4 5 6
| <?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <util:list id="booklist"> <value>Spring</value> <value>Java</value> <value>JVM Machine</value> </util:list>
<bean id="book" class="com.micah.spring.collection.Book" scope="prototype"> <property name="list" ref="booklist"/> </bean> </beans>
|
工厂Bean
- Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
- 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean (注意泛型)
- 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Mybean implements FactoryBean<User>{
@Override public boolean isSingleton() { return false; }
@Override public User getObject() throws Exception { User user = new User(1); return user; }
@Override public Class<?> getObjectType() { return null; } }
|
1
| <bean id="myBean" class="com.me.spring5.Mybean"></bean>
|
1 2 3 4 5
| public void test4(){ ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml"); User user = context.getBean("myBean", User.class); System.out.println(user); }
|
Bean的作用域
- 在Spring里面,你怎样定义类的作用域?
- 它可以通过bean标签中的scope属性来定义。
- 如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。
- 另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
- 解释Spring支持的几种bean的作用域
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:将单个bean定义的范围限定为单个HTTP请求的生命周期; 也就是说,每个HTTP请求都有一个自己的bean实例,它是在单个bean定义的后面创建的。 仅在基于Web的Spring ApplicationContext上下文中有效。
- session:将单个bean定义的作用域限定为HTTP会话的生命周期。 仅在web的Spring ApplicationContext上下文中有效。
- application:将单个bean定义的作用域限定为ServletContext的生命周期。 仅在基于web的Spring ApplicationContext上下文中有效。
- websocket:将单个bean定义的作用域限定为WebSocket的生命周期。 仅在基于web的Spring ApplicationContext上下文中有效。
Bean的生命周期
- 生命周期
- Bean的生命周期
- 1.通过构造器创建 bean 实例(无参数构造)
- 2.为 bean 的属性设置值和对其他 bean 引用(调用 set 方法,注入属性)
- 3.把 bean 实例传递 bean 前置处理器的方法 postProcessBeforeInitialization
- 4.调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 5.把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- 6.bean 可以使用了(对象获取到了)
- 7.当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Orders { public Orders() { System.out.println("第一步 执行无参数构造创建 bean 实例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 调用 set 方法设置属性值"); } public void initMethod() { System.out.println("第三步 执行初始化的方法"); } public void destroyMethod() { System.out.println("第五步 执行销毁的方法"); } }
|
1 2 3 4 5 6 7 8 9 10
| @Test public void testBean3() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 获取创建 bean 实例对象"); System.out.println(orders); context.close(); }
|
1 2 3
| <bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean>
|
xml自动装配
- 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
1 2 3 4 5 6 7 8 9 10 11
|
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName">
</bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
|
1 2 3 4
| <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
</bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
|
外部属性文件
1 2 3 4 5 6 7 8 9 10 11
| <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
1 2 3 4 5 6 7 8
| <context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
|
- 在 spring 配置文件使用标签引入外部属性文件
2.基于注解方式实现
上面四个注解功能是一样的,都可以用来创建 bean 实例
基于注解方式实现对象创建
spring-aop-5.2.6.RELEASE.jar
1 2 3 4 5 6 7 8 9
| <?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 http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.me"></context:component-scan> </beans>
|
1 2 3 4 5 6
| @Component(value = "web") public class Web { public void add(){ System.out.println("add...."); } }
|
基于注解的形式进行属性注入
- @Autowired:根据属性类型进行自动装配
- 第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
- 第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
1 2 3
| public interface Dao { void add(); }
|
1 2 3 4 5 6 7
| @Repository public class DaoImpl implements Dao { @Override public void add() { System.out.println("dao add..."); } }
|
1 2 3 4 5 6 7 8 9 10 11
| @Service public class UserService {
@Autowired private Dao dao;
public void add(){ dao.add(); System.out.println("service adding"); } }
|
完全注解开发
1 2 3 4
| @Configuration @ComponentScan(basePackages = {"com.atguigu"}) public class SpringConfig { }
|
AOP(面向切面编程)
- 什么是AOP?
AOP(Aspect Oriented Programming),即面向切面编程,是OOP的补充,它也提供了模块化。在面向对象编程中,关键的单元是对象,AOP的关键单元是切面,或者说关注点(可以简单地理解为你程序中的独立模块)。一些切面可能有集中的代码,但是有些可能被分散或者混杂在一起,例如日志或者事务。这些分散的切面被称为横切关注点。一个横切关注点是一个可以影响到整个应用的关注点,而且应该被尽量地集中到代码的一个地方,例如事务管理、权限、日志、安全等。
AOP让你可以使用简单可插拔的配置,在实际逻辑执行之前、之后或周围动态添加横切关注点。这让代码在当下和将来都变得易于维护。如果你是使用XML来使用切面的话,要添加或删除关注点,你不用重新编译完整的源代码,而仅仅需要修改配置文件就可以了。
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总而言之,AOP就是使用不修改源代码的方式,在主干功能里添加新的功能。
AOP底层原理
有接口情况:
- 使用 JDK动态代理创建接口实现类代理对象,增强类的方法
没有接口情况:
- 使用CGLIB动态代理创建子类的代理对象,增强类的方法
使用JDK动态代理
三个参数:
ClassLoder
,类加载器
类<?>[ ] interfaces
,增强方法所在的类,这个类实现的接口,支持多个接口
InvocationHandler
,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
1 2 3 4 5 6 7
| public interface StuDao {
public int add(int x,int y);
public String update(String id);
}
|
1 2 3 4 5 6 7 8 9 10 11
| public class StuDaoImpl implements StuDao{ @Override public int add(int x, int y) { return x+y; }
@Override public String update(String id) { return id; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class JDKProxy {
public static void main(String[] args) { Class[] interfaces={StuDao.class}; StuDao stuDao=new StuDaoImpl(); StuDao dao = (StuDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new StuDaoTmp(stuDao)); int result = dao.add(1, 2); System.out.println(result);
} }
class StuDaoTmp implements InvocationHandler{
private Object obj; public StuDaoTmp(Object obj){ this.obj=obj; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before:"+method.getName()+"arg:"+ Arrays.toString(args));
Object res = method.invoke(obj, args);
System.out.println("after:"+obj);
return res; } }
|
AOP术语
AOP操作
1.准备工作
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-5.2.6.RELEASE.jar
切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称([参数列表]) )
权限修饰符可以省略,返回类型必须写
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
- execution(* com.atguigu.dao.BookDao.add(..))
举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
- execution(* com.atguigu.dao.BookDao.* (..))
举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
- execution(* com.atguigu.dao.. (..))
2.AOP 操作(AspectJ 注解)
1.创建类与增强类
1 2 3 4 5 6 7
| public class Book {
public void add(){
System.out.println("add!"); } }
|
1 2 3 4 5 6
| public class BookProxy { public void before() { System.out.println("before........."); } }
|
- 在增强类里面,创建方法,让不同方法代表不同通知类型
2.在xml中配置
1 2 3 4 5 6 7 8 9 10 11 12
| <?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" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.me.spring5.aopo"></context:component-scan> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
|
3.使用注解创建类,并在增强类上加入@aspect注解
1 2 3 4 5 6 7 8
| @Component public class Book {
public void add(){
System.out.println("add!"); } }
|
1 2 3 4 5 6 7 8
| @Component @Aspect public class BookProxy { public void before() { System.out.println("before........."); } }
|
4、配置不同类型的通知
- 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Component @Aspect public class BookProxy {
@Before(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void before() { System.out.println("before........."); } @AfterReturning(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void afterReturning() { System.out.println("afterReturning........."); } @After(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void after() { System.out.println("after........."); } @AfterThrowing(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void afterThrowing() { System.out.println("afterThrowing........."); } @Around(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕之前........."); proceedingJoinPoint.proceed(); System.out.println("环绕之后........."); } }
|
1 2 3 4 5 6 7 8 9 10
| @Pointcut(value = "execution(* com.me.spring5.aopo.Book.add(..))") public void pointdemo(){
} @Before(value = "pointdemo()") public void before() { System.out.println("before........."); }
|
- 设置增强类优先级
- 有多个增强类多同一个方法进行增强,设置增强类优先级
- 在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
1 2 3 4
| @Component @Aspect @Order(1) public class PersonProxy
|
AOP操作(配置文件)
1 2
| <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
|
1 2 3 4 5 6 7 8 9 10
| <aop:config> <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/> <aop:aspect ref="bookProxy"> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
|
Spring事务管理
什么是事务?
就是把多个要做的操作组合成一个整体,利用事务的特性来保证操作的安全性。
1、事务的定义
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败。
(2)案例分析
- 如上述例子,Micah给Maruko转账,只有在转账成功的情况下,Micah的账户余额才会减少,Maruko的账户余额增加,不存在Micah账户的余额减少了,而Maruko的账户余额却不变。要么转账成功,2边余额都改变;要么转账失败,2边余额都保持不变。
2、事务的四大特性
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
事务操作
操作步骤
注解式事务管理操作
(1)在Spring配置文件中,添加事务管理器,并开启事务注解这里,需要注意,开启事务注解需要使用名称空间tx
1
| xmlns:tx="http://www.springframework.org/schema/tx"
|
1 2 3 4 5 6 7
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
|
(2)在service类或者类方法上中添加transactional注解
- @Transactional,这个注解添加到类上面,也可以添加方法上面
- 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
- 如果把这个注解添加方法上面,为这个方法添加事务
1 2 3 4 5 6
| @Service @Transactional public class UserService { @Autowired private UserDao userDao; }
|
事务的传播行为
事务的传播行为有以下七种:
PROPAGATION_REQUIRED:Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
PROPAGATION_REQUES_NEW:该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
PROPAGATION_SUPPORT:如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NOT_SUPPORT该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
PROPAGATION_NEVER:该传播机制不支持外层事务,即如果外层有事务就抛出异常
PROPAGATION_MANDATORY:与NEVER相反,如果外层没有事务,则抛出异常
PROPAGATION_NESTED该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。
事务的隔离级别
1、事务的隔离级别定义
- 一个事务可能受其他并发务活动活动影响的程度,可以把事务的隔离级别想象为这个事务对于事物处理数据的自私程度。
2、三个读问题
3、隔离级别
- 在理想状态下,事务之间将完全隔离,从而可以防止这些问题发生。然而,完全隔离会影响性能,因为隔离经常涉及到锁定在数据库中的记录(甚至有时是锁表)。完全隔离要求事务相互等待来完成工作,会阻碍并发。因此,可以根据业务场景选择不同的隔离级别。
隔离级别 |
含义 |
ISOLATION_DEFAULT |
数据库默认的事务隔离级别,另外四个与JDBC的隔离级别相对应。 |
ISOLATION_READ_UNCOMMITTED |
允许读取尚未提交的更改,可能会导致脏读,幻读和不可重复读 |
ISOLATION_READ_COMMITTED |
(Oracle默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。 |
ISOLATION_REPEATABLE_READ |
(MySQL默认级别)对相同字段的多次读取结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。 |
ISOLATION_SERIALIZABLE |
完全服从ACID原则的隔离级别,确保不发生脏读,幻读,不可重复读。(在所有隔离级别中是最慢的,因为是通过完全锁定当前事务所涉及的数据表来完成的) |
隔离级别 |
脏读 |
不可重复读 |
幻读 |
ISOLATION_READ_UNCOMMITTED |
有 |
有 |
有 |
ISOLATION_READ_COMMITTED |
无 |
有 |
有 |
ISOLATION_REPEATABLE_READ |
无 |
无 |
有 |
ISOLATION_SERIALIZABLE |
无 |
无 |
无 |
4、timeout:超时时间
事务需要在一定时间内进行提交,如果不提交进行回滚
默认值是 -1 ,设置时间以秒单位进行计算
5、readOnly:是否只读
6、rollbackFor:回滚
7、noRollbackFor:不回滚
XML声明式事务管理
(1)配置事务管理器
(2)配置通知
(3)配置切入点和切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<tx:advice id = "tx_advice"> <tx:attributes> <tx:method name="account*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
<aop:config> <aop:pointcut id="pt" expression="execution(* com.micah.spring.service.UserService.*(..))"/> <aop:advisor advice-ref="tx_advice" pointcut-ref="pt"/> </aop:config>
|
Spring Cache
人人都说好的Spring Cache!