Spring
第一部分:Spring框架的概述以及spring中基于xml的ioc配置
ioc:inverse of control:反转控制
aop:aspect oriented programming:面向切面编程
1.spring的第一要义在于解耦,耦合主要是有类的依赖和方法的依赖。解耦的思路,创建对象的时候使用反射来创建对象,而避免使用new关键字
public static void main (String[] args) throws Exception{
//1.注册驱动
//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/books?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT", "root", "meroot");
//3.获取操作数据的预处理对象
PreparedStatement pstm = conn.prepareStatement("select * from account");
//4.执行sql,得到结果集
ResultSet rs = pstm.executeQuery();
//5.遍历结构集
while(rs.next()){
System.out.println(rs.getString("name"));
}
//6.释放资源
rs.close();
pstm.close();
conn.close();
}
虽然可以通过反射来写程序,但是是写死的,所以可以通过读取配置文件来获取创建对象的全限定类名,然后使用反射来创建对象。
工厂:创建bean对象,bean有可重建组件的含义
那么应该怎么创建配置文件呢,在maven工程中,在resource目录下,创建properties文件或者yum文件
public class BeanFactory{
//定义一个Properties对象
private static Propertie props;
//使用静态代码块为Properties对象赋值
static {
try{
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader()
.getResourceAsStream("bean.properties");
props.load(in);
}catch(Exception e){
throw new ExceptionInInitializerError("初始化失败!");
}
}
public Object getBean(String beanName){
Object bean;
try{
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();
//每次都会调用默认构造函数创建对象
}catch(Exception e){
e.printStackTrace();
}
return bean;
}
}
但是单一对象就可以了,不需要每次new对象,所以定义一个Map,用于存放需要创建的对象,称之为容器
一.IOC
1.1 spring的ioc的几大依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQOz1EZr-1575804880569)(C:\Users\00\AppData\Roaming\Typora\typora-user-images\image-20191204142422793.png)]
1.2 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</bean>
1.3 实际使用
public class Client {
/**
* 获取spring的ioc核心容器并根据id获取对象
* Application的三个常用实现类
* (1) ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下
* (2) FileSystemXmlApplicationXml:可以加载磁盘任意路径下的配置文件(必须有访问全系)
* (3)AnnotationConfigApplicationContext:用于读取注解创建容器
* 核心容器的两个接口引发的问题
* (1)ApplicationContext 单例
* 它在构建核心容器的时候,构建对象采用的策略是立即加载的方式,也就是说,只要一读取配置文件,马上就创建配置文件中配置的对象
* (2)BeanFactory 多例对象
* 它在创建核心容器时候,创建对象采用的是延迟加载的方法,也就是说,根据id获取对象的时候才创建对象
* @param args
*/
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据id获取bean对象
IAccountService iAccountService = (IAccountService) ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao", IAccountDao.class);
System.out.println(iAccountService);
System.out.println(adao);
}
}
1.4 创建对象的三种方式
1.使用默认构造函数创建
在spring中的配置文件中使用bean标签,配以id和class属性之后,且没有其它属性和标签时候,采用的是此模式,此时如果没有默认构造函数,则不能创建
2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)上面的方法返回new的对象
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
3.使用静态工厂中的静态方法创建对象(使用某个类中的静态方法创建对象并存入spring容器)
<bean id="accountService" class="com.itheima.factory.InstanceFactory" factory-method="getAccountService"></bean>
1.5 bean的作用范围
取值:
(1)singleton:单例,也是默认值
(2)prototype:多例的
(3)request:作用域web请求的请求范围
(4)session:作用域web应用的会话范围
(5)globalsession:作用域集群环境的会话范围(全局会话范围),当不是集群时候,它就是session,从spring5.0版本时候,global-session已经移除
nginx负载均衡
1.6 bean对象的生命周期
(1)单例:
创建:当容器创建
存活:容器还在就在
销毁:容器销毁
单例和容器生命周期相同
(2)多例:
当使用对象时候spring框架创建
活着:一直活着
死亡:当对象长时间不用,且没有其它对象引用,由java垃圾回收机制回收
第二部分:Spring中基于注解的ioc和ioc的案例
2.1 能注入的数据
1.基本数据类型
2.其它的bean类型
3.复杂类型,集合类型
<bean id="accountService" class="service.impl.AccountServiceImpl">
<!-- 结构相同,标签可以互换-->
<property name="myStrs">
<array>
<value>arr1</value>
<value>arr2</value>
</array>
</property>
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
</map>
</property>
<property name="myProperties">
<props>
<prop key="prop1">prop1value</prop>
<prop key="prop2">prop2value</prop>
</props>
</property>
</bean>
bean的结构
public class AccountServiceImpl implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProperties;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProperties(Properties myProperties) {
this.myProperties = myProperties;
}
public void saveAccount() {
System.out.println("saveAccount方法执行了"+Arrays.toString(myStrs));
System.out.println("myList:"+myList);
System.out.println("mySet:"+mySet);
System.out.println("myMap:"+myMap);
System.out.println("myProperties"+myProperties);
}
}
2.2 注入的方式
1.使用构造函数
<bean id="accountService" class="service.impl.AccountServiceImpl">
<!--由于默认构造函数没有,所以这种方式不能创建,需要在bean的内部添加一个新的标签constructor-arg:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,从0开始
name:
value:用于指定基本数据类型和string
ref:其它类型-->
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name, Integer age, Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("saveAccount方法执行了"+name+age+birthday);
}
}
2.使用set方法
<bean id="accountService2" class="service.impl.AccountServiceImpl2">
<!--涉及的标签property,出现的位置,bean标签的内部
name:名称是set方法后面的名字,而不是属性名,而且要去掉大写
value:用于指定基本数据类型和string
ref:其它类型-->
<property name="age" value="18"></property>
<property name="name" value="test"></property>
<property name="birthday" ref="now"></property>
</bean>
public class AccountServiceImpl2 implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("saveAccount方法执行了"+name+age+birthday);
}
}
set和get的优势和弊端在于是否是默认构造函数和创建对象时候有没有可能忘记了必须初始化的属性
3.使用注解提供
(1)项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMoEaks3-1575804880570)(C:\Users\00\AppData\Roaming\Typora\typora-user-images\image-20191205142615285.png)]
(2)pom(主要用到了spring-context)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i41Cn3MN-1575804880571)(C:\Users\00\AppData\Roaming\Typora\typora-user-images\image-20191205142656487.png)]
(3)applicationContext.xml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dE2C99UI-1575804880571)(C:\Users\00\AppData\Roaming\Typora\typora-user-images\image-20191205142756051.png)]
主要在于约束和扫描哪些包
(4)注解
dao的注解:注解是在实现类上面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKiMr3it-1575804880572)(C:\Users\00\AppData\Roaming\Typora\typora-user-images\image-20191205142900331.png)]
业务层:
/**
* 账户的业务层实现类
* <bean id="accountService" class="service.impl.AccountServiceImpl" scope="" init-method="" destroy-method="">
* <property>
* name=""
* value=""
* </property>
* </bean>
* 1.用于创建对象的
* @Component:用于把当前类对象存入spring容器中
* 属性:value,用于指定bean的id,默认是当前类名,且首字母改小写
* @Controller:表现层
* @Service:业务层
* @Repository:持久层
* 2.用于注入数据的
* @Autowired:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功,可以是变量上,也可以是方法上
* 如果有多个类型匹配,则首先按照类型,然后按照变量名称private IAccountDao iAccountDao1;
* @Qualifier:作用,在按照类中注入的基础之上再按照名称注入,它在给类成员注入时候不能单独使用,但是在给方法参数注入时候可以
* 属性:value,用于指定注入bean的id
* @Resource:可以独立使用,但是它的属性是value
* 以上三个注解只能注入bean,而基本数据类型,String类型不能使用以上注解,而集合类型注入只能使用xml
* @Value:用于注入基本类型和String类型的数据,属性:value用于指定值,可以使用Spring中的spel(也就是spring中的el表达式,写法也是${表达式})
* 3.用于改变作用范围的
* @scope:用于指定bean的作用范围,属性也只有一个value,注解在类上
* 4.和生命周期相关的,注解在方法上
* @preDestroy:用于指定销毁方法
* @PostConstruct:用于指定初始化方法
*/
@Component(value = "accountService")//如果只有一个属性,value可以省略
public class AccountServiceImpl implements IAccountService {
@Autowired
@Qualifier(value = "accountDao")
private IAccountDao iAccountDao;
public void saveAccount() {
iAccountDao.saveAccount();
}
}
控制层:
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据id获取bean对象
IAccountService iAccountService = (IAccountService) ac.getBean("accountService");
iAccountService.saveAccount();
}
2.3 spring的常用注解
1.Configuration
2.ComponentScan
/**
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring的新注解:
* Configuration:表明该类是一个配置类
* 细节:当Configration作为AnnotationConfigApplicationContext的参数时候,可以不写这个注解
* ComponentScan:用于通过注解指定spring在创建容器时候要扫描的包,
* 属性value和basePackages的作用是一样的,都是用于指定创建容器要扫描的包
*/
@Configuration
@ComponentScan(basePackages = "com.itheima")
public class SpringConfiguration {
}
3.Bean 把返回值放进容器
属性:name,用于指定bean的id,默认是当前方法的名称
当使用注解配置方法时候,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找的方法和Autowired注解的作用是一样的
package com.itheima.config;
@Configuration
@ComponentScan(basePackages = "com.itheima")
public class SpringConfiguration {
@Bean("dataSource")
public DataSource createDataSource(){
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass();
ds.setJdbcUrl();
ds.setUser();
ds.setPassword();
}
}
此时,测试
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class)
4.import
主配置类引入其它配置类,不用指定扫描其它分配置类的包
@Import("JdbcConfig.Class")
5.PropertySource
写在配置类上面,用于指定properties的文件的位置,属性value指定文件的名称和路径,关键字classpath,表示类路径下,用el表达式读取@Value("${jdbc.url}")
@PropertySoource("classpath:jdbcConfig.properties")
2.4 Junit
Junit可以写个初始化方法
public void init(){
}
可以简化
junit集成了main方法,可以判断哪些有@Test方法
整合步骤:
1.导入Spring整合junit的jar包spring-test
2.使用junit提供的一个注解将原来的main方法替换成spring提供的,@Runwith
3.告知spring的运行器,spring的ioc创建是基于xml还是注解的,并且说明位置,注解@ContextConfiguration
location:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类所在位置
当使用spring5.x版本的时候,要求junit的jar包必须是4.1.2以及以上
第三部分:spring中的aop和基于xml以及注解的aop配置
3.1 事务:
1.需要ThreadLocal对象把Connection和当前线程绑定
事务控制应该都在业务层
(1)开启事务,执行操作
(2)提交事务,返回结果
(3)回滚事务
(4)释放连接
3.2 动态代理
字节码随用随创建,随用随加,不使用源码的基础上对方法增强
(1)基于接口的动态代理:
-要求接口必须有一个没实现类
(2)基于子类的动态代理:
-要求类不能是最终类
-涉及的类:proxy
-提供者:jdk官方
-如何创建代理对象:使用proxy的newProxyInstance方法
-创建代理对象的要求:被dialing类最少实现一个接口,如果没有则不能使用
3.3 使用动态代理实现事务控制
joinpoint:连接点,是指那些被拦截的点,这些点指的是方法,以为spring只支持方法类型的连接点
pointcut:切入点,是指对那些joinpoint进行拦截
advice(通知):所谓通知就是指拦截到之后要做的事情,分为前置通知(执行前),(执行后)后置通知,(catch)异常通知,(finally)最终通知,环绕通知
introduction(引介):是一种特殊的通知,在不修改代码的前提下,introduction可以在运行器为类动态地添加一些method or filed
target(目标对象):代理的目标对象
weaving(织入):把增强应用到目标对象来创建的代理对选哪个的过程,Spring采用动态代理织入,而aspectj采用编译器织入和类装载期织入
proxy:一个类被aop织入增强后,就产生一个结果代理类
aspect:pointcut和advice(通知,引介)的结合
3.4 spring基于xml的aop
1.引入包:spring-context和aspectjweaver
2.
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置spring的ioc,把service对象配置-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!--spring中基于xml的aop配置步骤-->
<!--1.把通知的bean也交给Spring管理-->
<bean id="logger" class="com.itheima.utils.Logger"></bean>
<!--2.使用aop:config表明开始aop的配置-->
<!--3.使用aop:aspect配置切面
id属性,给切面一个唯一属性
ref属性:指定通知类的bean的id,就是Logger,-->
<!--4. 在aop:aspect标签的内部使用对应的标签来配置通知的类型,现在的事例是想让logger方法在切入点方法之前执行,表示前置通知
method竖向表示Logger中的哪个方法,pointcut用于切入点表达式,对哪些方法增强,表达式访问修饰符 返回值 包名.包名...类名.方法名(参数列表)
全通配写法:* *..*.*(..)
访问修饰符可以省略
包名可以用 ..表示当前包表示子包
*.*..AccountServiceImpl
类名和方法名都可以用*实现通配,可以使用通配符表示任意类型,但是必须有参数,可以使用..表示任意参数类型
实际开发中切入点表达式通常写法,切到业务层实现类的所有方法
* com.itheima.service.impl.*.*(..)-->
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
<aop:after-returning method="afterExecuteReturnLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:after-returning>
<aop:after-throwing method="afterThrowingLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:after-throwing>
<aop:after method="finallyLog" pointcut-ref="pt1"></aop:after>
<!--配置切入点表达式,id属性用于表达式唯一标识,此id写在内部,只能自己用,还可以写在aop:aspect外面,但是必须在aop:aspect之前-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:aspect>
</aop:aspect>
</aop:config>
</beans>
3.环绕通知:
1.概述
问题:
当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
分析:
通过对比动态代理的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用.而我们的没有
解决:
Spring框架为我们提供了一个接口,proceedingJoinPoint.该接口有一个方法proceed()此方法就相当于明确调用切入点方法.
该接口可以作为环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的实现类供我们使用
Spring中的环绕通知:
它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
2. 使用
2.1 基于注解:
@Component("logger")
@Aspect
public class AnnotationAudienceAround{
//使用@Pointcut注解声明切入点表达式
@Pointcut("execution(* com.qin.util.*.*(..))")
public void pt1(){}
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
注意:要在配置文件中
<context:component-scan basepackage="com.itheima"></context:component:scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.2 基于XML配置文件:
1. 通知类.
//去掉了所有的注解
public class XmlAudienceAround{
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
}
2. 在xml文件中配置
<!--声明bean-->
<bean name="xmlAudienceAround" class="com.qin.util.XmlAudienceAround"/>
<!--配置切面及通知-->
<aop:config>
<aop:aspect ref="xmlAudienceAround">
<aop:pointcut id="pt1" expression="execution(* com.qin.util.*.*(..))"/>
<aop:around method="aroundPringLog" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
第四部分:Spring中的jdbctemplate
第五部分:spring事务控制
导入包:spring-tx
5.1 spring中事务控制的一组api
PlatFormTransactionManager
声明式的事务支持:通过配置的形式
1.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置业务层-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
2.注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 配置JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- spring中基于注解 的声明式事务控制配置步骤
1、配置事务管理器
2、开启spring对注解事务的支持
3、在需要事务支持的地方使用@Transactional注解
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
注解
package com.itheima.service.impl;
import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 账户的业务层实现类
*
* 事务控制应该都是在业务层
*/
@Service("accountService")
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只读型事务的配置
public class AccountServiceImpl implements IAccountService{
@Autowired
private IAccountDao accountDao;
@Override
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
//需要的是读写型事务配置
@Transactional(propagation= Propagation.REQUIRED,readOnly=false)
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
Integer accountId) {
return accountDao.findAccountById(accountId);
}
//需要的是读写型事务配置
@Transactional(propagation= Propagation.REQUIRED,readOnly=false)
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i=1/0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
## 问题:spring的事务控制和基于aop的事务控制