这里续写上一章博客(107章博客,注意前缀Java,后面和前面就不多说了):
Spring 声明式事务的支持:
编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务
声明式事务:通过xml或者注解配置的方式达到事务控制的⽬的,叫做声明式事务
事务回顾:
事务之所以出现,还是需要归于mysql的原因,任何通过语句操作mysql的,都是相同的操作,无论是使用mysql独有的可视化窗口,还是语言,还是cmd窗口(命令行窗口),都是如此,都是给对应的表(或者说引擎)所操作的,但是sql的诞生通常也会留出一些接口,使得sql与编程语言进行互通和数据的操作,这也是java的jdbc的由来,那么在这个基础上,就存在连接成功,以及执行语句的操作(一般我们称为语句平台)
事务的概念:
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功,从而确保了数 据的准确与安全
例如:A向B转帐,对应于如下两条sql语句:
update account set money= money- 100 where name= 'a' ;
update account set money= money+ 100 where name= 'b' ;
对于事务来说,这两条语句的执行,要么全部成功,要么全部不成功,这种机制的原理在于:
MySQL在事务中执行的SQL操作结果会被记录在事务日志中,事务日志(Transaction Log)是MySQL中的一种机制,用于记录数据库的变更操作,包括插入、更新和删除等,一般来说事务的操作都是修改真正的表数据的,而之所以存在事务日志是为了可以进行回滚,以及其他的操作,主要是回滚的
当执行一个事务时,MySQL会将事务中的每个操作都记录在事务日志中,而不是立即将其应用到实际数据文件中,这个过程被称为"写日志"(write-ahead logging)
在事务提交之前,如果需要回滚事务,MySQL可以使用事务日志中的信息来撤销已经执行的操作,将数据恢复到事务开始前的状态,这意味着事务日志可以用来反向执行操作,实现事务的回滚
撤销日志记录(Undo Log Record):在执行一个事务中的更新或删除操作时,MySQL将相应的写入事务日志的消息拿取,由于事务日志包含了被修改的数据的原始值或修改前的状态,所以可以根据这些来进行回滚,具体操作可以阅读mysql源码
事务的四大特性:
原⼦性(Atomicity) :原⼦性是指事务是一个不可分割的工作单位,也就是说事务自身不能分割了,那么在事务里面的操作自然要么都发生,要么都 不发生,因为他们都是属于事务,这样才可以说事务是一个不可分割的,简单来说原子性就是事务的执行成功与否,所以从操作的⻆度来描述,事务中的各个操作要么都成功要么都失败
一致性(Consistency) :事务必须使数据库从一个一致性状态变换到另外一个一致性状态,例如转账前A有1000,B有1000,转账后A+B也得是2000,这个一致性只是代表对数据总量的不变,所以是从数据的⻆度来说的,即可以出现(1000,1000) (900,1100),而不应该出现(900,1000)
隔离性(Isolation) :事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务, 每个事务不能被其他事务的操作数据所⼲扰,多个并发事务之间要相互隔离
比如:事务1给员工涨工资2000,但是事务1尚未被提交,员工发起事务2查询工资,发现工资涨了2000块钱,读到了事务1尚未提交的数据(脏读)
持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响,简单来说,就是直接改变了硬盘,而不是内存
事务的隔离级别:
隔离级别本质上是锁的缘故以及某些操作,在表中通常存在如下的锁:
行锁:代表对该行数据的同步
表锁:代表是对该整个表数据的同步
一般还有其他方面的别称,如读锁和写锁
读锁(共享锁):使得拿取数据不能写但可以读
写锁(排他锁):使得写上数据不能写也不能读
但是针对自身所加的读锁和写锁,读锁确不能写,但是写锁确可以写和读(但是只是针对设置的会话),一般行锁和表锁的写锁和读锁都是同样的功能说明,或者都是同样的操作,他们之所以存在这样的操作,是因为底层代码就是这样设置的,并且按照惯例,锁只能是锁当前人操作(因为他在操作出现的),写锁自然只能自己操作写或者读取,而读锁谁都不能写,包括自己(因为自己也只有读),这是读锁和写锁在全部的领域中占用大多数的说明,一般在不考虑加上自身说明时,就是读操作时,写不能操作,但是读可以,而写操作读和写都不可以,若加上自身在操作的说明时,而不考虑锁自身的意思时,就是读操作时,只在读(自身和其他),写操作时,存在写和读(自身)
通常为了提高性能,我们只会操作行锁里面的读锁和写锁(简单来说就是对一行操作的读锁和写锁,我们统称为行锁),这里就以行锁的读锁和写锁来进行说明(大多数情况下,读锁操作我们一般并不会添加,因为读取数据实际上对数据没有什么破坏,除非需要避免某些问题,如幻读,这里的隔离级别级别都是写锁,具体的读锁和写锁的说明在96章博客有具体说明)
要说明锁的功能,首先说明四个隔离级别,对应与什么锁操作:
Read uncommitted(读未提交):最低级别,基本对与并发会发生的均⽆法保证不发生,(读未提交)最低
Read committed(读已提交):可避免脏读情况发生,不可重复读和幻读一定会发生, 第三
Repeatable read(可重复读):可避免脏读、不可重复读情况的发生,(幻读有可能发生)第二,该机制下会对要update的行进行加锁
Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生,(串行化)最⾼
默认mysql的隔离级别是Repeatable read(可重复读)
对与他们加上锁的说明:
Read uncommitted(读未提交):代表没有加上任何锁
Read committed(读已提交):代表添加了共享锁,即读锁,但是只是针对在最终读取表数据的加锁,实际上读已提交并不是利用锁的原理来解决其脏读问题,是因为判断形成的,具体原理是因为:数据库为每个事务分配一个唯一的事务ID,在读已提交隔离级别下,事务会记录自己开始的时刻的事务ID,并在读取数据时检查数据的修改者事务ID,只有已经提交的事务的修改会被认可,未提交的事务的修改会被忽略,所以我是可以忽略你的没有提交的,剩下的我进行读取最新的数据,这就是为什么Read committed(读已提交)可以解决脏读的原因,但是读取必然需要与表交互,那么他是保留日志的吗,实则不然,他一般利用了快照,快照读:在读已提交隔离级别下,数据库会为每个事务创建一个独立的读取版本(快照),当事务开始时,它会记录事务开始的时间点,并在读取数据时使用这个时间点的快照,由于忽略的原因,这意味着事务只能读取到在事务开始之前已经提交的数据版本,而不会读取到其他尚未提交的数据,综上所述,Read committed(读已提交)的确解决了脏读,并且单纯的查询虽然没有事务,但是他再这个隔离级别下,也只会考虑已经提交的事务,而非不考虑,这里要注意
Repeatable read(可重复读):代表在Read committed(读已提交)的基础上加上了行锁(行级锁)和间隙锁:
由于是在Read committed(读已提交)的基础上的,所以后面的问题是提交后的问题
行级锁(Row-level Locks):在可重复读隔离级别下,数据库会对读取的每一行数据进行加锁,以保证事务读取期间的数据一致性,行级锁可以防止其他事务修改或删除被锁定的行,从而确保读取的数据在事务结束之前保持不变,这样可以避免了其他事务对同一行数据的并发修改,保持了数据的一致性,当然一开始是没有进行添加该锁的,当你进行DML(增删改)时,就会对对应操作的行加上该锁,使得其他事务不能操作,除非事务提交释放锁了,很明显,对应的行是加上写锁的
间隙锁(Gap Locks):间隙锁用于防止其他事务在已有数据范围之间插入新数据,当事务在可重复读隔离级别下进行范围查询时,数据库会在扫描的范围内设置间隙锁,阻止其他事务在这个范围内插入新数据,这样可以保证事务读取期间,范围查询的结果保持一致,避免了查询期间幻读的问题,他虽然可以在读取期间,使得不被插入,但是读取后的查询可能由于对方提交使得出现幻读(两次查询结果不同,比如添加,修改,删除等等),具体为什么可以解决不可重复读看后面的说明
Serializable(串行化):代表加上表级锁,由于是对表进行添加的,那么他在事务开启时,其中一个事务操作后,基本上,查询都不能进行了,还有事务级锁
表级锁(Table-level Locks):在串行化隔离级别下,数据库会对事务涉及的表进行表级锁定,这意味着在事务执行期间,其他事务无法对这些表进行任何读取或写入操作,表级锁保证了在串行化隔离级别下,只有一个事务可以同时访问被锁定的表,从而避免了并发冲突
事务级锁(Transaction-level Locks):在串行化隔离级别下,数据库会对整个事务进行锁定,以确保只有一个事务能够执行,这种锁定方式可以防止其他事务在同一时间并发执行,并确保事务的串行执行顺序,事务级锁只是建立在表锁的说明而已,正是因为他什么都不能操作,使得事务看起来被锁住一样,所以也成为事务级锁,很明显,由于完全的隔离,所以自然可以解决幻读的问题,也就自然解决前面的所有的问题
不考虑隔离级别,会出现以下情况:(以下情况全是错误的),也即为隔离级别在解决事务并发问题
脏读:一个线程中的事务读到了另外一个线程中未提交的数据,没有加上任何锁的情况,那么你在操作事务时,是可以得到对方事务的数据的,因为对与sql的锁来说,可能只能允许一个事务的操作进入(读锁和写锁的性质),具体实现,可以认为是某种标志来操作的
不可重复读:一个线程中的事务读到了另外一个线程中已经提交的(如update)的数据(前后内容不一样)
场景: 员工A发起事务1,查询工资,工资为1w,此时事务1尚未关闭,财务⼈员发起了事务2,给员工A加了2000块钱,并且提交了事务,员工A通过事务1再次发起查询请求,发现工资为1.2w,原来读出来1w读不到了,叫做不可重复读
虚读(幻读):一个线程中的事务读到了另外一个线程中已经提交的insert或者delete的数据(前后条数不一样),一般也包括更新
场景: 事务1查询所有工资为1w的员工的总数,查询出来了10个⼈,此时事务尚未关闭,事务2财务⼈员发起,新来员工,工资1w,向表中插⼊了2条数据,并且提交了事务,事务1再次查询工资为1w的员工个数,发现有12个⼈,⻅了⻤了,这里很明显是第一次操作导致的
所以对应的四种隔离级别就是来对标这些情况的,并且我们可以发现,隔离级别只是利用了锁机制以及某些解决方式(如快照,事务ID等等)来解决的,只要记住:事务最终操作的就是本来的表,那么我们只需要对表进行一些同步操作,就可以解决,且一个锁或者对应部分(如行锁)只能被一个事务进行操作,这样就能理解四个隔离级别的原理了
当然,隔离级别都是建议在对方之上的,所以虽然隔离越好,但是效率也是越来越低,所以为了保证效率比较好,且也比较安全,mysql一般默认隔离级别就是Repeatable read(可重复读)
当然,最好还是建立在测试中来进行理解,测试说明在36章博客有简单的说明(当然可能是存在问题的)
sql相关语句:
select @@tx_isolation ;
set global transaction isolation level read committed ;
为了验证增删改查对隔离级别的影响,我们来操作如下(只是为了验证问题的出现,而不考虑全部增删改查操作),这里会操作大量的图片,这是保证测试的全面性:
首先是Read uncommitted:
我们先登录进来,查看隔离级别,可以发现的确默认是Repeatable read(可重复读)
设置隔离级别并查看:
设置的就是永久的,但不作用于当前窗口(会话,所以可以关闭窗口使得关闭会话,或者使用exit退出会话,然后重新登录也行),这是保证当前隔离级别不会半路变化的,所以我们新建一个dos窗口,我称为dos1
开了一个新的dos窗口了,发现改变了隔离级别,我们继续开一个,称为dos2,并关掉dos1之前的那一个:
现在我们创建一个数据库和对应的表,以及添加一些数据,sql语句如下:
CREATE DATABASE testgeli CHARACTER SET utf8;
USE testgeli;
CREATE TABLE test(
id INT ( 11 ) PRIMARY KEY ,
NAME VARCHAR ( 20 ) ,
pass VARCHAR ( 20 ) ,
url VARCHAR ( 20 )
) ;
INSERT INTO test VALUES ( 1 , '张三' , '123' , 'com.qi' ) ;
INSERT INTO test VALUES ( 2 , '李四' , '123' , 'com.qi' ) ;
INSERT INTO test VALUES ( 3 , '王五' , '123' , 'com.qi' ) ;
INSERT INTO test VALUES ( 4 , '赵六' , '123' , 'com.qi' ) ;
在dos2上执行如下:
dos1和dos2都进行查询:
dos1的查询:
dos2的查询:
至此,我们准备完毕 ,后续操作隔离级别时,最好都到这里的一步,现在我们来进行操作
首先dos1,和dos2开启事务(begin就是开始事务,记得dos2也开启),操作如下:
我们添加一条数据,看看dos2是否也有,这里就不给出图片了,实际上无论你是否开启事务,dos2都会查询的,因为是操作真的表的,可以发现,的确满足了脏读的问题出现,脏读:一个线程中的事务读到了另外一个线程中未提交的数据,实际上任何操作都是事务,查询也是(可能也不是),所以任何操作都会操作事务,只是是否自动提交与否而已,所以一个线程中的事务读到了另外一个线程中未提交的数据正确出现了
我们继续改变隔离级别,即变成Read committed,按照上面的操作弄好环境:
dos1如下:
dos2如下:
我们来看看他是否解决了脏读:
dos1如下:
dos2:
即解决了脏读,那么我们来验证他是否解决了不可重复读:一个线程中的事务读到了另外一个线程中已经提交的(如update)的数据(前后内容不一样)
现在我们对dos1进行提交commit;,然后再dos2中进行查询,可以发现,出现了数据,也就是说,他存在不可重复读,即他只解决了脏读,这也是为什么他会称为读已提交的意思,而之前的则是读未提交的意思,因为一个的确读取了已经提交的,而另外一个读取了没有提交的,这是代表他们没有解决的地方
我们继续设置隔离级别,Repeatable read(可重复读):
dos1如下:
dos2:
我们来看看他是否解决了不可重复读:
dos1操作如下:
dos2如下:
可以发现他还是读到提交的了,但是真的是这样吗,我们继续操作
我们再dos1继续操作如下:
然后dos2操作如下:
可以发现,解决了脏读,并且在一个事务中,另外一个dos1添加时,他并没有改变,即解决了不可重复读:一个线程中的事务读到了另外一个线程中已经提交的(如update)的数据(前后内容不一样),并且也可以发现,查询是没有操作事务的,这就是为什么没有开启事务时,查询是可以得到数据,而开启事务中的查询没有得到,这样更加验证了事务是一个操作,而非固定操作,大多数的隔离级别只是针对事务来操作的,所以没有在事务的查询必然是查询真的表,而没有操作隔离级别的忽略(事务ID的忽略,如读已提交,可重复读等等)
我们来验证他是否加上了锁,我们继续操作,将dos1和dos2都进行提交,操作如下,来看看他是否完全解决了
我们使得dos1如下(多余的可以选择通过工具删除):
dos2:
现在我们给dos1操作如下:
dos2操作如下:
dos1进行提交,看看dos2是否有数据,发现没有数据,这是正常的,因为他们是解决了解决了不可重复读的,但是我们来操作一下更新(你可能有点细心,在说明不可重复读时,有个括号,如"(如update)",他是代表这个更新是重要的,因为其他的我们由于查询的原因,不能直接的看到,虽然他也存在阻塞),dos1操作如下:
然后dos2操作如下:
你会发现卡住了,这是因为可重复读是加上锁的(读取是快照,所以并不操作锁哦,所以其他事务是存在读取的,这里就解释了写锁明明存在,其他事务为什么也可以进行读取的原因),当你操作真实的对应信息时,他是不能操作的,删除,添加(操作对应的id,虽然你不存在,但是锁还在,首先通过锁,才能到表,所以删除也会阻塞)也是如此,所以可以发现他的确是操作了锁,你可以选择操作改变id为4的值,其中一个操作后,另外一个不能操作,但是上面的说明为什么可以导致提交后不被操作读取呢,解释如下:我们将之前的版本进行改变,即快照进行改变,这个时候,只要你不是当前事务ID的,那么都进行忽略,而不是只看修改的,所以这就是为什么提交也得不到的原因,并且防止你对信息操作时,你的信息会影响到我,所以加上锁来防止这种情况,这就是为什么你更新后,我会阻塞的原因,因为我不能操作你的了,但是也要注意,操作不同隔离级别时,策略是不同的,因为隔离级别只是针对当前会话的操作,所以如果其中一个隔离级别是读未提交,那么他可能会读取到我这个可重复读的信息
至此可重复读保证解决了不可重复读的情况,但是在这里我们要考虑一个问题,如果其中一个事务改变数据,然后提交(释放锁),那么另外一个事务虽然他不会读取,但是若他操作对应的数据了,那么这个时候数据就会出现问题,所以就引入了串行化,也就是说,你不能进行任何操作,首先我们来显示出这个问题:
隔离级别首先并不改变,操作如下:
dos1:
dos2:
然后dos1操作如下:
提交了,那么就没有他造成的加锁了,那么dos2操作如下:
可以发现,数据发生了大改变,这就是可重读的还没有解决的问题,虽然他解决了提交后查询没有发生改变,但是并没有解决你操作后数据的改变,对与查询只是根据快照来操作的,然而对与数据的操作我们都是操作真正的表,所以这个问题我们并不能避免,所以引入了Serializable(串行化),我们来设置隔离级别,回到如下的dos1和dos2(修改成如下):
dos1:
dos2:
现在我们在dos1中进行开启事务,然后查询,这个时候,dos2虽然可以查询,但是不能增删改了,并且若dos1操作了增删改,那么dos2查询都不能操作,也就是说,这个时候,他没有快照的操作,单纯的就是看锁,当只有查询时,是加上读锁的,而增删改时是加上写锁的,并没有快照哦,所以这个时候查询都不能
至此,所有的隔离级别都已经说明完毕,若有错误,请忽略或者指出
事务的传播行为(虽然在66章有说明,这里我们可以进行回顾):
事务往往在service层进行控制,如果出现service层方法A调用了另外一个service层方法B,A和B方法本身都已经被添加了事务控制(准备加事务,但是需要考虑调用关系后来操作加事务,因为事务之间并没有真正的传播关系,即只能先考虑再传播),那么A调用B的时候,就需要进行事务的一些协商,这就叫做事务的传播行为
A调用B,我们站在B的⻆度来观察来定义事务的传播行为(如果当前:代表是如果当前A没有事务)
这个传播行为在后面的事务定义里面进行操作的,看后面的代码就知道了
在这之前,首先需要一个项目来进行理解:
创建的项目如下:
对应的Account类:
package com. lagou. domain ;
public class Account {
private Integer id;
private String name;
private Double money;
@Override
public String toString ( ) {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}' ;
}
public Integer getId ( ) {
return id;
}
public void setId ( Integer id) {
this . id = id;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public Double getMoney ( ) {
return money;
}
public void setMoney ( Double money) {
this . money = money;
}
}
对应的AccountDao接口及其实现类:
package com. lagou. dao ;
public interface AccountDao {
public void outMoney ( String outUser, double money) ;
public void inMoney ( String inUser, double money) ;
}
package com. lagou. dao. impl ;
import com. lagou. dao. AccountDao ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. jdbc. core. JdbcTemplate ;
import org. springframework. stereotype. Repository ;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void outMoney ( String outUser, double money) {
String sql = "update account set money = money - ? where name = ?" ;
jdbcTemplate. update ( sql, money, outUser) ;
}
@Override
public void inMoney ( String inUser, double money) {
String sql = "update account set money = money + ? where name = ?" ;
jdbcTemplate. update ( sql, money, inUser) ;
}
}
对应的AccountService接口及其实现类:
package com. lagou. service. impl ;
import com. lagou. dao. AccountDao ;
import com. lagou. service. AccountService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. stereotype. Service ;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer ( String outUser, String inUser, Double money) {
accountDao. outMoney ( outUser, money) ;
accountDao. inMoney ( inUser, money) ;
}
}
对应的AccountServiceTest类:
package com. lagou. test ;
import com. lagou. service. AccountService ;
import org. junit. Test ;
import org. junit. runner. RunWith ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. test. context. ContextConfiguration ;
import org. springframework. test. context. junit4. SpringJUnit4ClassRunner ;
@RunWith ( SpringJUnit4ClassRunner . class )
@ContextConfiguration ( "classpath:Spring.xml" )
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void textTransfer ( ) {
accountService. transfer ( "李四" , "jerry" , 100d ) ;
}
}
对应的application.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?characterEncoding=utf8&useSSL=false
# 加上useSSL=false防止使用注解时,出现的关闭错误,默认是true
jdbc.username=root
jdbc.password=123456
对应的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: 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.lagou" > </ context: component-scan>
< context: property-placeholder location = " classpath:application.properties" />
< bean id = " dataSource" class = " com.alibaba.druid.pool.DruidDataSource" >
< property name = " driverClassName" value = " ${jdbc.driverClassName}" > </ property>
< property name = " url" value = " ${jdbc.url}" > </ property>
< property name = " username" value = " ${jdbc.username}" > </ property>
< property name = " password" value = " ${jdbc.password}" > </ property>
</ bean>
< bean id = " jdbcTemplate" class = " org.springframework.jdbc.core.JdbcTemplate" >
< constructor-arg name = " dataSource" ref = " dataSource" > </ constructor-arg>
</ bean>
</ beans>
对应的sql:
CREATE DATABASE spring_db CHARACTER SET utf8;
USE spring_db;
CREATE TABLE account(
id INT ( 11 ) NOT NULL PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR ( 32 ) DEFAULT NULL ,
money DOUBLE DEFAULT NULL
) ;
INSERT INTO account ( NAME, money) VALUES ( '张三' , '300' ) ;
INSERT INTO account ( NAME, money) VALUES ( '李四' , '300' ) ;
INSERT INTO account ( NAME, money) VALUES ( '王五' , '300' ) ;
INSERT INTO account ( NAME, money) VALUES ( '赵六' , '300' ) ;
INSERT INTO account ( NAME, money) VALUES ( 'jerry' , '300' ) ;
我们执行测试,查看数据库信息,若数据发生改变,说明操作成功
编程式事务控制相关对象(在代码中的操作):
三个接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
PlatformTransactionManager:
public interface PlatformTransactionManager {
TransactionStatus getTransaction ( @Nullable TransactionDefinition var1) throws TransactionException ;
void commit ( TransactionStatus var1) throws TransactionException ;
void rollback ( TransactionStatus var1) throws TransactionException ;
}
实际上事务在代码层面上,只是多执行一次而已,比如之前的代码可以修改成这样:
@Override
public void outMoney ( String outUser, double money) {
String sql = "update account set money = money - ? where name = ?" ;
jdbcTemplate. update ( sql, money, outUser) ;
sql = "update account set money = money - ?+10 where name = ?" ;
jdbcTemplate. update ( sql, money, outUser) ;
}
所以他们也只是相当于在会话中执行一次,有些可以得到对应或者对应事务的结果,即是否快照,即认为他们是单纯的执行就行了
当然,他是一个接口,所以需要实现类,一般他有如下的操作:
TransactionDefinition:
public interface TransactionDefinition {
int getPropagationBehavior ( ) ;
int getIsolationLevel ( ) ;
int getTimeout ( ) ;
boolean isReadOnly ( ) ;
}
他的实现类一般是DefaultTransactionDefinition,这个接口和后面的TransactionStatus会在后面说明的
TransactionStatus:
public interface TransactionStatus extends SavepointManager , Flushable {
boolean isNewTransaction ( ) ;
boolean hasSavepoint ( ) ;
void setRollbackOnly ( ) ;
boolean isRollbackOnly ( ) ;
void flush ( ) ;
boolean isCompleted ( ) ;
}
PlatformTransactionManager接口,是spring的事务管理器
TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)
TransactionStatus 接口提供的是事务具体的运行状态
可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态
实现代码:
在对应的Spring.xml中加上如下:
< bean id = " transactionManager"
class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" />
</ bean>
修改对应的实现类AccountServiceImpl:
package com. lagou. service. impl ;
import com. lagou. dao. AccountDao ;
import com. lagou. service. AccountService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. stereotype. Service ;
import org. springframework. transaction. PlatformTransactionManager ;
import org. springframework. transaction. TransactionDefinition ;
import org. springframework. transaction. TransactionStatus ;
import org. springframework. transaction. support. DefaultTransactionDefinition ;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private PlatformTransactionManager transactionManager;
@Override
public void transfer ( String outUser, String inUser, Double money) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition ( ) ;
def. setReadOnly ( false ) ;
def. setIsolationLevel ( TransactionDefinition . ISOLATION_REPEATABLE_READ ) ;
def. setPropagationBehavior ( TransactionDefinition . PROPAGATION_REQUIRED ) ;
TransactionStatus status = transactionManager. getTransaction ( def) ;
try {
accountDao. outMoney ( outUser, money) ;
accountDao. inMoney ( inUser, money) ;
transactionManager. commit ( status) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
transactionManager. rollback ( status) ;
}
}
}
执行后,看看数据库的结果即可,若正确,说明操作完毕,这里只是回顾一下66章博客的内容,其中66章博客的内容操作了注解,实际上就是通过代理使得加上上面的信息的,即将上面的代码用注解以及xml来完成,具体就不回顾了,可以去看一看
Spring AOP源码深度剖析:
首先我们回到之前下载好的Spring源码,创建如下子模块:
对应的LagouAspect类:
package com. lagou ;
import org. aspectj. lang. annotation. Aspect ;
import org. aspectj. lang. annotation. Before ;
import org. aspectj. lang. annotation. Pointcut ;
import org. springframework. stereotype. Component ;
@Component
@Aspect
public class LagouAspect {
@Pointcut ( "execution(* com.*.*.*(..))" )
public void pointcut ( ) {
}
@Before ( "pointcut()" )
public void before ( ) {
System . out. println ( "before method ......" ) ;
}
}
对应的LagouBean类:
package com. lagou ;
import org. springframework. stereotype. Component ;
@Component
public class LagouBean {
public void tech ( ) {
System . out. println ( "java learning......" ) ;
}
}
对应的SpringConfig类:
package com. lagou ;
import org. springframework. context. annotation. ComponentScan ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. context. annotation. EnableAspectJAutoProxy ;
@Configuration
@ComponentScan ( "com.lagou" )
@EnableAspectJAutoProxy
public class SpringConfig {
}
对应的test类:
import com. lagou. LagouBean ;
import com. lagou. SpringConfig ;
import org. springframework. context. ApplicationContext ;
import org. springframework. context. annotation. AnnotationConfigApplicationContext ;
public class test {
public static void main ( String [ ] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext ( SpringConfig . class ) ;
LagouBean lagouBean = applicationContext. getBean ( LagouBean . class ) ;
lagouBean. tech ( ) ;
}
}
执行看看是否出现对应的结果,这里有个问题,为什么不使用@Test呢,实际上他并不支持,@Test是需要插件的,maven或者说idea一般是自带的,要不然单纯的依赖是怎么操作结构性的操作呢,要知道依赖只是对类进行操作而已,而非结构性的操作,当然,依赖也并非不能,具体可以百度,只是这里的@Test需要插件而已(junit的)
为了看看他的原理,我们需要进行改变,这里我们按照xml的方式来进行看看原理,因为注解只是建立在xml上的数据拿取方式的改变,即是演化来的(不好观察),所以需要来操作xml的方式来更加的容易理解,首先,将对应的LagouAspect修改成如下:
package com. lagou ;
public class LagouAspect {
public void before ( ) {
System . out. println ( "haha" ) ;
}
}
对应的LagouBean修改如下:
package com. lagou ;
public class LagouBean {
public void tech ( ) {
System . out. println ( "java learning......" ) ;
}
}
SpringConfig类进行删除
然后test类修改如下:
import com. lagou. LagouBean ;
import org. springframework. context. ApplicationContext ;
import org. springframework. context. support. ClassPathXmlApplicationContext ;
public class test {
public static void main ( String [ ] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext ( "app.xml" ) ;
LagouBean lagouBean = applicationContext. getBean ( LagouBean . class ) ;
lagouBean. tech ( ) ;
}
}
在资源文件夹下,加上app.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
http://www.springframework.org/schema/aop/spring-aop.xsd" >
< bean id = " lagouBean" class = " com.lagou.LagouBean" > </ bean>
< bean id = " lagouAspect" class = " com.lagou.LagouAspect" > </ bean>
< aop: config>
< aop: aspect ref = " lagouAspect" >
< aop: before method = " before"
pointcut = " execution(void com.lagou.LagouBean.tech(..))" />
</ aop: aspect>
</ aop: config>
</ beans>
执行看看结果,若出现结果,说明操作完毕,要看看他具体操作,自然还是需要从bean的创建入手,因为他要代理,自然是改变原来的对象,所以我们回到之前操作过的finishBeanFactoryInitialization(beanFactory);方法里面,给他加上断点,调试到如下(前面也给出过说明):
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
@Override
public Object applyBeanPostProcessorsAfterInitialization ( Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for ( BeanPostProcessor processor : getBeanPostProcessors ( ) ) {
Object current = processor. postProcessAfterInitialization ( result, beanName) ;
if ( current == null ) {
return result;
}
result = current;
}
return result;
}
protected Object doCreateBean ( String beanName, RootBeanDefinition mbd, @Nullable Object [ ] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null ;
if ( mbd. isSingleton ( ) ) {
instanceWrapper = this . factoryBeanInstanceCache. remove ( beanName) ;
}
if ( instanceWrapper == null ) {
instanceWrapper = createBeanInstance ( beanName, mbd, args) ;
}
Object bean = instanceWrapper. getWrappedInstance ( ) ;
Class < ? > beanType = instanceWrapper. getWrappedClass ( ) ;
if ( beanType != NullBean . class ) {
mbd. resolvedTargetType = beanType;
}
synchronized ( mbd. postProcessingLock) {
if ( ! mbd. postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors ( mbd, beanType, beanName) ;
}
catch ( Throwable ex) {
throw new BeanCreationException ( mbd. getResourceDescription ( ) , beanName,
"Post-processing of merged bean definition failed" , ex) ;
}
mbd. postProcessed = true ;
}
}
boolean earlySingletonExposure = ( mbd. isSingleton ( ) && this . allowCircularReferences &&
isSingletonCurrentlyInCreation ( beanName) ) ;
if ( earlySingletonExposure) {
if ( logger. isTraceEnabled ( ) ) {
logger. trace ( "Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references" ) ;
}
addSingletonFactory ( beanName, ( ) -> getEarlyBeanReference ( beanName, mbd, bean) ) ;
}
Object exposedObject = bean;
try {
populateBean ( beanName, mbd, instanceWrapper) ;
exposedObject = initializeBean ( beanName, exposedObject, mbd) ;
}
catch ( Throwable ex) {
protected Object initializeBean ( String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if ( System . getSecurityManager ( ) != null ) {
AccessController . doPrivileged ( ( PrivilegedAction < Object > ) ( ) -> {
invokeAwareMethods ( beanName, bean) ;
return null ;
} , getAccessControlContext ( ) ) ;
}
else {
invokeAwareMethods ( beanName, bean) ;
}
Object wrappedBean = bean;
if ( mbd == null || ! mbd. isSynthetic ( ) ) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization ( wrappedBean, beanName) ;
}
try {
invokeInitMethods ( beanName, wrappedBean, mbd) ;
}
catch ( Throwable ex) {
throw new BeanCreationException (
( mbd != null ? mbd. getResourceDescription ( ) : null ) ,
beanName, "Invocation of init method failed" , ex) ;
}
if ( mbd == null || ! mbd. isSynthetic ( ) ) {
wrappedBean = applyBeanPostProcessorsAfterInitialization ( wrappedBean, beanName) ;
}
return wrappedBean;
}
}
对应代理最终到的地方:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor , BeanFactoryAware {
@Override
public Object postProcessAfterInitialization ( @Nullable Object bean, String beanName) {
if ( bean != null ) {
Object cacheKey = getCacheKey ( bean. getClass ( ) , beanName) ;
if ( this . earlyProxyReferences. remove ( cacheKey) != bean) {
return wrapIfNecessary ( bean, beanName, cacheKey) ;
}
}
return bean;
}
protected Object wrapIfNecessary ( Object bean, String beanName, Object cacheKey) {
if ( StringUtils . hasLength ( beanName) && this . targetSourcedBeans. contains ( beanName) ) {
return bean;
}
if ( Boolean . FALSE . equals ( this . advisedBeans. get ( cacheKey) ) ) {
return bean;
}
if ( isInfrastructureClass ( bean. getClass ( ) ) || shouldSkip ( bean. getClass ( ) , beanName) ) {
this . advisedBeans. put ( cacheKey, Boolean . FALSE ) ;
return bean;
}
Object [ ] specificInterceptors = getAdvicesAndAdvisorsForBean ( bean. getClass ( ) , beanName, null ) ;
if ( specificInterceptors != DO_NOT_PROXY ) {
this . advisedBeans. put ( cacheKey, Boolean . TRUE ) ;
Object proxy = createProxy (
bean. getClass ( ) , beanName, specificInterceptors, new SingletonTargetSource ( bean) ) ;
this . proxyTypes. put ( cacheKey, proxy. getClass ( ) ) ;
return proxy;
}
this . advisedBeans. put ( cacheKey, Boolean . FALSE ) ;
return bean;
}
protected Object createProxy ( Class < ? > beanClass, @Nullable String beanName,
@Nullable Object [ ] specificInterceptors, TargetSource targetSource) {
if ( this . beanFactory instanceof ConfigurableListableBeanFactory ) {
AutoProxyUtils . exposeTargetClass ( ( ConfigurableListableBeanFactory ) this . beanFactory, beanName, beanClass) ;
}
ProxyFactory proxyFactory = new ProxyFactory ( ) ;
proxyFactory. copyFrom ( this ) ;
if ( ! proxyFactory. isProxyTargetClass ( ) ) {
if ( shouldProxyTargetClass ( beanClass, beanName) ) {
proxyFactory. setProxyTargetClass ( true ) ;
}
else {
evaluateProxyInterfaces ( beanClass, proxyFactory) ;
}
}
Advisor [ ] advisors = buildAdvisors ( beanName, specificInterceptors) ;
proxyFactory. addAdvisors ( advisors) ;
proxyFactory. setTargetSource ( targetSource) ;
customizeProxyFactory ( proxyFactory) ;
proxyFactory. setFrozen ( this . freezeProxy) ;
if ( advisorsPreFiltered ( ) ) {
proxyFactory. setPreFiltered ( true ) ;
}
return proxyFactory. getProxy ( getProxyClassLoader ( ) ) ;
}
}
public class ProxyFactory extends ProxyCreatorSupport {
public Object getProxy ( @Nullable ClassLoader classLoader) {
return createAopProxy ( ) . getProxy ( classLoader) ;
}
}
public class ProxyCreatorSupport extends AdvisedSupport {
protected final synchronized AopProxy createAopProxy ( ) {
if ( ! this . active) {
activate ( ) ;
}
return getAopProxyFactory ( ) . createAopProxy ( this ) ;
}
}
public class DefaultAopProxyFactory implements AopProxyFactory , Serializable {
@Override
public AopProxy createAopProxy ( AdvisedSupport config) throws AopConfigException {
if ( config. isOptimize ( ) || config. isProxyTargetClass ( ) || hasNoUserSuppliedProxyInterfaces ( config) ) {
Class < ? > targetClass = config. getTargetClass ( ) ;
if ( targetClass == null ) {
throw new AopConfigException ( "TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation." ) ;
}
if ( targetClass. isInterface ( ) || Proxy . isProxyClass ( targetClass) ) {
return new JdkDynamicAopProxy ( config) ;
}
return new ObjenesisCglibAopProxy ( config) ;
}
else {
return new JdkDynamicAopProxy ( config) ;
}
}
}
class CglibAopProxy implements AopProxy , Serializable {
@Override
public Object getProxy ( @Nullable ClassLoader classLoader) {
if ( logger. isTraceEnabled ( ) ) {
logger. trace ( "Creating CGLIB proxy: " + this . advised. getTargetSource ( ) ) ;
}
try {
Class < ? > rootClass = this . advised. getTargetClass ( ) ;
Assert . state ( rootClass != null , "Target class must be available for creating a CGLIB proxy" ) ;
Class < ? > proxySuperClass = rootClass;
if ( ClassUtils . isCglibProxyClass ( rootClass) ) {
proxySuperClass = rootClass. getSuperclass ( ) ;
Class < ? > [ ] additionalInterfaces = rootClass. getInterfaces ( ) ;
for ( Class < ? > additionalInterface : additionalInterfaces) {
this . advised. addInterface ( additionalInterface) ;
}
}
validateClassIfNecessary ( proxySuperClass, classLoader) ;
Enhancer enhancer = createEnhancer ( ) ;
if ( classLoader != null ) {
enhancer. setClassLoader ( classLoader) ;
if ( classLoader instanceof SmartClassLoader &&
( ( SmartClassLoader ) classLoader) . isClassReloadable ( proxySuperClass) ) {
enhancer. setUseCache ( false ) ;
}
}
enhancer. setSuperclass ( proxySuperClass) ;
enhancer. setInterfaces ( AopProxyUtils . completeProxiedInterfaces ( this . advised) ) ;
enhancer. setNamingPolicy ( SpringNamingPolicy . INSTANCE ) ;
enhancer. setStrategy ( new ClassLoaderAwareUndeclaredThrowableStrategy ( classLoader) ) ;
Callback [ ] callbacks = getCallbacks ( rootClass) ;
Class < ? > [ ] types = new Class < ? > [ callbacks. length] ;
for ( int x = 0 ; x < types. length; x++ ) {
types[ x] = callbacks[ x] . getClass ( ) ;
}
enhancer. setCallbackFilter ( new ProxyCallbackFilter (
this . advised. getConfigurationOnlyCopy ( ) , this . fixedInterceptorMap, this . fixedInterceptorOffset) ) ;
enhancer. setCallbackTypes ( types) ;
return createProxyClassAndInstance ( enhancer, callbacks) ;
}
catch ( CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException ( "Could not generate CGLIB subclass of " + this . advised. getTargetClass ( ) +
": Common causes of this problem include using a final class or a non-visible class" ,
ex) ;
}
catch ( Throwable ex) {
throw new AopConfigException ( "Unexpected AOP exception" , ex) ;
}
}
protected Object createProxyClassAndInstance ( Enhancer enhancer, Callback [ ] callbacks) {
enhancer. setInterceptDuringConstruction ( false ) ;
enhancer. setCallbacks ( callbacks) ;
return ( this . constructorArgs != null && this . constructorArgTypes != null ?
enhancer. create ( this . constructorArgTypes, this . constructorArgs) :
enhancer. create ( ) ) ;
}
}
所以可以看到,操作了对应的aop的xml代理时,是操作代理对象的,经过我的测试,aop事务增强也是这里,并且事务是先操作的,且要注意,事务是操作覆盖,而通知是补充(id相同的时候),至此,我们查看xml的方式可以明白他的确是解决增强的,因为操作代理的,到这里,可能并没有过对注解源码的说明,那么这里我们来以这个为例,因为前面有一个案例了,也作为第一次操作注解源码说明的例子,这里开一个开头:
要知道注解为什么可以,实际上与xml是一样的,只是获取数据不同而已,注解之所以简便是因为注解他所标注的位置有隐藏信息,比如对应所注解的类的信息,如全限定名等等,特别的,在扫描时,这是可以找到的,并且也由于这个名称存在,即可以得到该类的任何信息,这些信息足以完成xml的替换,因为xml就是根据类信息来的,既然注解可以得到,自然可以替换xml
当然,这里我们可以将原来注解方式变成xml的修改回去,即操作注解(xml可以保留,我们不使用就行了),虽然现在我们来说明事务了
Spring声明式事务控制(操作注解和xml,编程式是没有利用其他的如xml和注解来完成的):
声明式事务很方便,尤其纯注解模式,仅仅⼏个注解就能控制事务了
如@EnableTransactionManagement和@Transactional,其中@EnableTransactionManagement代表事务的扫描,而@Transactional代表配置属性和指定给谁加上的事务增强(这里再66章博客有具体的说明和使用)
@EnableTransactionManagement:
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Import ( TransactionManagementConfigurationSelector . class )
public @interface EnableTransactionManagement {
}
@EnableTransactionManagement 注解使用 @Import 标签(主要用来导入其他配置类,不是配置类也可以(有些对应版本必须要配置类,但现在基本可以不是配置类了,但因为这个存在,如果对方存在其他扫描的,那么会进行处理(通常这个为主,当然,可能会出现错误,但是一般没有,因为基本不会出现,即他们是覆盖的关系),即他也看成一个bean了,即通过@Import注解导入的类可以成为Spring中的bean,并且由于是导入,所以就算他不是配置类,也可以读取到@Bean))引⼊了TransactionManagementConfigurationSelector类,这个类⼜向容器中导⼊了两个重要的组件
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector < EnableTransactionManagement > {
@Override
protected String [ ] selectImports ( AdviceMode adviceMode) {
switch ( adviceMode) {
case PROXY :
return new String [ ] { AutoProxyRegistrar . class . getName ( ) ,
ProxyTransactionManagementConfiguration . class . getName ( ) } ;
case ASPECTJ :
return new String [ ] { determineTransactionAspectClass ( ) } ;
default :
return null ;
}
}
}
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory . getLog ( getClass ( ) ) ;
@Override
public void registerBeanDefinitions ( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false ;
Set < String > annTypes = importingClassMetadata. getAnnotationTypes ( ) ;
for ( String annType : annTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils . attributesFor ( importingClassMetadata, annType) ;
if ( candidate == null ) {
continue ;
}
Object mode = candidate. get ( "mode" ) ;
Object proxyTargetClass = candidate. get ( "proxyTargetClass" ) ;
if ( mode != null && proxyTargetClass != null && AdviceMode . class == mode. getClass ( ) &&
Boolean . class == proxyTargetClass. getClass ( ) ) {
candidateFound = true ;
if ( mode == AdviceMode . PROXY ) {
AopConfigUtils . registerAutoProxyCreatorIfNecessary ( registry) ;
if ( ( Boolean ) proxyTargetClass) {
AopConfigUtils . forceAutoProxyCreatorToUseClassProxying ( registry) ;
return ;
}
}
}
}
if ( ! candidateFound && logger. isInfoEnabled ( ) ) {
String name = getClass ( ) . getSimpleName ( ) ;
logger. info ( String . format ( "%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether." , name, name, name) ) ;
}
}
}
public abstract class AopConfigUtils {
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary ( BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary ( registry, null ) ;
}
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary (
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired ( InfrastructureAdvisorAutoProxyCreator . class , registry, source) ;
}
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired (
Class < ? > cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert . notNull ( registry, "BeanDefinitionRegistry must not be null" ) ;
if ( registry. containsBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME ) ) {
BeanDefinition apcDefinition = registry. getBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME ) ;
if ( ! cls. getName ( ) . equals ( apcDefinition. getBeanClassName ( ) ) ) {
int currentPriority = findPriorityForClass ( apcDefinition. getBeanClassName ( ) ) ;
int requiredPriority = findPriorityForClass ( cls) ;
if ( currentPriority < requiredPriority) {
apcDefinition. setBeanClassName ( cls. getName ( ) ) ;
}
}
return null ;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition ( cls) ;
beanDefinition. setSource ( source) ;
beanDefinition. getPropertyValues ( ) . add ( "order" , Ordered . HIGHEST_PRECEDENCE ) ;
beanDefinition. setRole ( BeanDefinition . ROLE_INFRASTRUCTURE ) ;
registry. registerBeanDefinition ( AUTO_PROXY_CREATOR_BEAN_NAME , beanDefinition) ;
return beanDefinition;
}
}
@Configuration
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE )
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean ( name = TransactionManagementConfigUtils . TRANSACTION_ADVISOR_BEAN_NAME )
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE )
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor ( ) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor ( ) ;
advisor. setTransactionAttributeSource ( transactionAttributeSource ( ) ) ;
advisor. setAdvice ( transactionInterceptor ( ) ) ;
if ( this . enableTx != null ) {
advisor. setOrder ( this . enableTx. < Integer > getNumber ( "order" ) ) ;
}
return advisor;
}
@Bean
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE )
public TransactionAttributeSource transactionAttributeSource ( ) {
return new AnnotationTransactionAttributeSource ( ) ;
}
@Bean
@Role ( BeanDefinition . ROLE_INFRASTRUCTURE )
public TransactionInterceptor transactionInterceptor ( ) {
TransactionInterceptor interceptor = new TransactionInterceptor ( ) ;
interceptor. setTransactionAttributeSource ( transactionAttributeSource ( ) ) ;
if ( this . txManager != null ) {
interceptor. setTransactionManager ( this . txManager) ;
}
return interceptor;
}
}
属性解析器:
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
implements Serializable {
private static final boolean jta12Present;
private static final boolean ejb3Present;
static {
ClassLoader classLoader = AnnotationTransactionAttributeSource . class . getClassLoader ( ) ;
jta12Present = ClassUtils . isPresent ( "javax.transaction.Transactional" , classLoader) ;
ejb3Present = ClassUtils . isPresent ( "javax.ejb.TransactionAttribute" , classLoader) ;
}
private final boolean publicMethodsOnly;
private final Set < TransactionAnnotationParser > annotationParsers;
}
public interface TransactionAnnotationParser {
}
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser , Serializable {
protected TransactionAttribute parseTransactionAnnotation ( AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute ( ) ;
Propagation propagation = attributes. getEnum ( "propagation" ) ;
rbta. setPropagationBehavior ( propagation. value ( ) ) ;
Isolation isolation = attributes. getEnum ( "isolation" ) ;
rbta. setIsolationLevel ( isolation. value ( ) ) ;
rbta. setTimeout ( attributes. getNumber ( "timeout" ) . intValue ( ) ) ;
rbta. setReadOnly ( attributes. getBoolean ( "readOnly" ) ) ;
rbta. setQualifier ( attributes. getString ( "value" ) ) ;
List < RollbackRuleAttribute > rollbackRules = new ArrayList < > ( ) ;
for ( Class < ? > rbRule : attributes. getClassArray ( "rollbackFor" ) ) {
rollbackRules. add ( new RollbackRuleAttribute ( rbRule) ) ;
}
for ( String rbRule : attributes. getStringArray ( "rollbackForClassName" ) ) {
rollbackRules. add ( new RollbackRuleAttribute ( rbRule) ) ;
}
for ( Class < ? > rbRule : attributes. getClassArray ( "noRollbackFor" ) ) {
rollbackRules. add ( new NoRollbackRuleAttribute ( rbRule) ) ;
}
for ( String rbRule : attributes. getStringArray ( "noRollbackForClassName" ) ) {
rollbackRules. add ( new NoRollbackRuleAttribute ( rbRule) ) ;
}
rbta. setRollbackRules ( rollbackRules) ;
return rbta;
}
}
很明显,对应的属性解析器变成bean后,存在的对应方法是操作对应的@Transactional的属性的,即实际上属性解析器是解析@Transactional的事务属性
事务拦截器:TransactionInterceptor interceptor = new TransactionInterceptor();
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor , Serializable {
@Override
@Nullable
public Object invoke ( MethodInvocation invocation) throws Throwable {
Class < ? > targetClass = ( invocation. getThis ( ) != null ? AopUtils . getTargetClass ( invocation. getThis ( ) ) : null ) ;
return invokeWithinTransaction ( invocation. getMethod ( ) , targetClass, invocation:: proceed ) ;
}
}
public abstract class TransactionAspectSupport implements BeanFactoryAware , InitializingBean {
@Nullable
protected Object invokeWithinTransaction ( Method method, @Nullable Class < ? > targetClass,
final InvocationCallback invocation) throws Throwable {
TransactionAttributeSource tas = getTransactionAttributeSource ( ) ;
final TransactionAttribute txAttr = ( tas != null ? tas. getTransactionAttribute ( method, targetClass) : null ) ;
final PlatformTransactionManager tm = determineTransactionManager ( txAttr) ;
final String joinpointIdentification = methodIdentification ( method, targetClass, txAttr) ;
if ( txAttr == null || ! ( tm instanceof CallbackPreferringPlatformTransactionManager ) ) {
TransactionInfo txInfo = createTransactionIfNecessary ( tm, txAttr, joinpointIdentification) ;
Object retVal;
try {
retVal = invocation. proceedWithInvocation ( ) ;
}
catch ( Throwable ex) {
completeTransactionAfterThrowing ( txInfo, ex) ;
throw ex;
}
finally {
cleanupTransactionInfo ( txInfo) ;
}
commitTransactionAfterReturning ( txInfo) ;
return retVal;
}
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder ( ) ;
try {
result = ( ( CallbackPreferringPlatformTransactionManager ) tm) . execute ( txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo ( tm, txAttr, joinpointIdentification, status) ;
try {
return invocation. proceedWithInvocation ( ) ;
}
catch ( Throwable ex) {
if ( txAttr. rollbackOn ( ex) ) {
if ( ex instanceof RuntimeException ) {
throw ( RuntimeException ) ex;
}
else {
throw new ThrowableHolderException ( ex) ;
}
}
else {
throwableHolder. throwable = ex;
return null ;
}
}
finally {
cleanupTransactionInfo ( txInfo) ;
}
} ) ;
}
catch ( ThrowableHolderException ex) {
throw ex. getCause ( ) ;
}
catch ( TransactionSystemException ex2) {
if ( throwableHolder. throwable != null ) {
logger. error ( "Application exception overridden by commit exception" , throwableHolder. throwable) ;
ex2. initApplicationException ( throwableHolder. throwable) ;
}
throw ex2;
}
catch ( Throwable ex2) {
if ( throwableHolder. throwable != null ) {
logger. error ( "Application exception overridden by commit exception" , throwableHolder. throwable) ;
}
throw ex2;
}
if ( throwableHolder. throwable != null ) {
throw throwableHolder. throwable;
}
return result;
}
}
}
至此,对应注解的操作的确操作了事务的增强,好了Spring的底层原理大致说明完毕,当然,还有很多,具体可以到网上学习
现在我们来通过注解自定义一个简单的Spring,完成对应的事务操作(前面我们通过代码已经完成了,虽然事务的我们并没有手写出对应的xml和对应的代码,这里我们就使用注解吧,总是使用xml还是不好了,需要注解的开发经验,也可以稍微使用一下xml来理解xml和注解的各自工作方式)
这里我们主要完成@Service、@Autowired、@Transactional注解类等等操作,现在操作如下:
由于这里需要操作事务,所以我们需要一个数据库和对应的表来完成一些需求,自然是转账的需求:
CREATE DATABASE testspring CHARACTER SET utf8;
USE testspring;
CREATE TABLE test(
id INT PRIMARY KEY ,
NAME VARCHAR ( 10 ) ,
money VARCHAR ( 10 )
) ;
INSERT INTO test VALUES ( 1 , '张三' , 1000 ) ;
INSERT INTO test VALUES ( 2 , '李四' , 1000 ) ;
我们在idea中创建一个项目,称为spring项目:
需要的依赖如下:
< dependencies>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 5.1.47</ version>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> druid</ artifactId>
< version> 1.1.15</ version>
</ dependency>
< dependency>
< groupId> commons-dbutils</ groupId>
< artifactId> commons-dbutils</ artifactId>
< version> 1.6</ version>
</ dependency>
</ dependencies>
项目如下:
现在我们先创建对应的三个注解:@Service、@Autowired、@Transactional,将他们放在com.she.Annotation下,代码如下:
package com. she. Annotation ;
import java. lang. annotation. ElementType ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
@Target ( ElementType . FIELD )
@Retention ( RetentionPolicy . RUNTIME )
public @interface Autowired {
}
package com. she. Annotation ;
import java. lang. annotation. ElementType ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
public @interface Service {
String value ( ) default "" ;
}
package com. she. Annotation ;
import java. lang. annotation. ElementType ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
public @interface Transactional {
}
这里我们选择操作半注解,扫描配置我们放在xml中,xml和注解结合操作,才能更好的理解注解和xml的工作方式
创建配置文件app.xml:
<?xml version="1.0" encoding="UTF-8" ?>
< beans>
< component-scan base-package = " com.she" > </ component-scan>
</ beans>
由于我们并不操作Spring,所以其对应的一些要求,或者约束等等是不需要进行操作的,其中最主要是因为我们读取时,并不会进行判断,而是直接的读取,综上所述,所以我们并不需要考虑
读取配置文件和扫描注解大多数都需要bean,而bean基本有我们工厂来操作,所以我们创建工厂包factory,在里面创建相应的类即可:
这里我们创建工厂对象BeanFactory(当然,并没有考虑非常多的细节,比如相同bean名称怎么办,具体在下一章博客操作mvc源码时,会给出非常多细节的,这里注意即可):
package com. she. factory ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import com. she. Annotation . Transactional ;
import org. dom4j. Document ;
import org. dom4j. Element ;
import org. dom4j. io. SAXReader ;
import java. io. File ;
import java. io. InputStream ;
import java. lang. reflect. Field ;
import java. util. ArrayList ;
import java. util. HashMap ;
import java. util. List ;
import java. util. Map ;
public class BeanFactory {
private static List < String > classNames = new ArrayList < > ( ) ;
private static Map < String , Object > map = new HashMap < > ( ) ;
private static List < String > fieldsAlreayProcessed = new ArrayList < > ( ) ;
static {
InputStream resourceAsStream = BeanFactory . class . getClassLoader ( ) . getResourceAsStream ( "app.xml" ) ;
SAXReader saxReader = new SAXReader ( ) ;
try {
Document document = saxReader. read ( resourceAsStream) ;
Element rootElement = document. getRootElement ( ) ;
Element element = ( Element ) rootElement. selectSingleNode ( "//component-scan" ) ;
String attribute = element. attributeValue ( "base-package" ) ;
System . out. println ( attribute) ;
doScan ( attribute) ;
doInstance ( ) ;
doAutoWired ( ) ;
doTransactional ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
private static void doScan ( String scanPackage) {
String scanPackagePath = Thread . currentThread ( ) . getContextClassLoader ( ) . getResource ( "" ) . getPath ( ) + scanPackage. replaceAll ( "\\." , "/" ) ;
try {
scanPackagePath = URLDecoder . decode ( scanPackagePath, StandardCharsets . UTF_8 . toString ( ) ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
File pack = new File ( scanPackagePath) ;
File [ ] files = pack. listFiles ( ) ;
for ( File file : files) {
if ( file. isDirectory ( ) ) {
doScan ( scanPackage + "." + file. getName ( ) ) ;
continue ;
}
if ( file. getName ( ) . endsWith ( ".class" ) ) {
String className = scanPackage + "." + file. getName ( ) . replaceAll ( ".class" , "" ) ;
classNames. add ( className) ;
}
}
}
private static void doInstance ( ) {
if ( classNames. size ( ) == 0 ) return ;
try {
for ( int i = 0 ; i < classNames. size ( ) ; i++ ) {
String className = classNames. get ( i) ;
Class < ? > aClass = Class . forName ( className) ;
if ( aClass. isAnnotationPresent ( Service . class ) ) {
String beanName = aClass. getAnnotation ( Service . class ) . value ( ) ;
Object o = aClass. newInstance ( ) ;
if ( "" . equals ( beanName. trim ( ) ) ) {
beanName = lowerFirst ( aClass. getSimpleName ( ) ) ;
}
map. put ( beanName, o) ;
Class < ? > [ ] interfaces = aClass. getInterfaces ( ) ;
if ( interfaces != null && interfaces. length > 0 ) {
for ( int j = 0 ; j < interfaces. length; j++ ) {
Class < ? > anInterface = interfaces[ j] ;
map. put ( anInterface. getName ( ) , aClass. newInstance ( ) ) ;
}
}
}
}
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
private static String lowerFirst ( String str) {
char [ ] chars = str. toCharArray ( ) ;
if ( 'A' <= chars[ 0 ] && chars[ 0 ] <= 'Z' ) {
chars[ 0 ] += 32 ;
}
return String . valueOf ( chars) ;
}
private static void doAutoWired ( ) {
if ( map. isEmpty ( ) ) {
return ;
}
for ( Map. Entry < String , Object > entry : map. entrySet ( ) ) {
try {
doObjectDependancy ( entry. getValue ( ) ) ;
} catch ( IllegalAccessException e) {
e. printStackTrace ( ) ;
}
}
}
private static void doObjectDependancy ( Object object) throws IllegalAccessException {
Field [ ] declaredFields = object. getClass ( ) . getDeclaredFields ( ) ;
if ( declaredFields == null || declaredFields. length == 0 ) {
return ;
}
for ( int i = 0 ; i < declaredFields. length; i++ ) {
Field declaredField = declaredFields[ i] ;
if ( ! declaredField. isAnnotationPresent ( Autowired . class ) ) {
continue ;
}
if ( fieldsAlreayProcessed. contains ( object. getClass ( ) . getName ( ) + "." + declaredField. getName ( ) ) ) {
continue ;
}
Object dependObject = null ;
dependObject = map. get ( declaredField. getType ( ) . getName ( ) ) ;
if ( dependObject == null ) {
dependObject = map. get ( lowerFirst ( declaredField. getType ( ) . getSimpleName ( ) ) ) ;
}
fieldsAlreayProcessed. add ( object. getClass ( ) . getName ( ) + "." + declaredField. getName ( ) ) ;
doObjectDependancy ( dependObject) ;
declaredField. setAccessible ( true ) ;
declaredField. set ( object, dependObject) ;
}
}
private static void doTransactional ( ) {
ProxyFactory proxyFactory = ( ProxyFactory ) map. get ( "proxyFactory" ) ;
for ( Map. Entry < String , Object > entry : map. entrySet ( ) ) {
String beanName = entry. getKey ( ) ;
Object o = entry. getValue ( ) ;
Class < ? > aClass = entry. getValue ( ) . getClass ( ) ;
if ( aClass. isAnnotationPresent ( Transactional . class ) ) {
Class < ? > [ ] interfaces = aClass. getInterfaces ( ) ;
if ( interfaces != null && interfaces. length > 0 ) {
map. put ( beanName, proxyFactory. getJdkProxy ( o) ) ;
} else {
map. put ( beanName, proxyFactory. getCglibProxy ( o) ) ;
}
}
}
}
public static Object getBean ( String id) {
return map. get ( id) ;
}
}
需要的测试类:
package com. she. factory ;
public class a {
public static void main ( String [ ] args) {
Thread thread = Thread . currentThread ( ) ;
System . out. println ( thread) ;
System . out. println ( thread. getContextClassLoader ( ) ) ;
System . out. println ( thread. getContextClassLoader ( ) . getResource ( "" ) ) ;
System . out. println ( thread. getContextClassLoader ( ) . getResource ( "" ) . getPath ( ) ) ;
String a = "com.jj.kk" ;
System . out. println ( a. replaceAll ( "\\." , "/" ) ) ;
System . out. println ( thread. getContextClassLoader ( ) . getResource ( "" ) . getPath ( ) + a. replaceAll ( "\\." , "/" ) ) ;
}
}
package com. she. factory ;
import java. lang. reflect. Field ;
public class bb {
public Integer i = 0 ;
public int ii = 0 ;
public static void main ( String [ ] args) {
Class a = bb. class ;
System . out. println ( a. getName ( ) ) ;
Field [ ] declaredFields = a. getDeclaredFields ( ) ;
for ( int i = 0 ; i < declaredFields. length; i++ ) {
Field declaredField = declaredFields[ i] ;
System . out. println ( declaredField) ;
System . out. println ( declaredField. getType ( ) . getName ( ) ) ;
System . out. println ( declaredField. getType ( ) . getSimpleName ( ) ) ;
System . out. println ( declaredField. getType ( ) ) ;
System . out. println ( declaredField. getName ( ) ) ;
}
}
}
然后继续创建ProxyFactory类:
package com. she. factory ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import com. she. utils. TransactionManager ;
import net. sf. cglib. proxy. Enhancer ;
import net. sf. cglib. proxy. MethodInterceptor ;
import net. sf. cglib. proxy. MethodProxy ;
import java. lang. reflect. InvocationHandler ;
import java. lang. reflect. Method ;
import java. lang. reflect. Proxy ;
@Service
public class ProxyFactory {
@Autowired
private TransactionManager transactionManager;
public Object getJdkProxy ( Object obj) {
return Proxy . newProxyInstance ( obj. getClass ( ) . getClassLoader ( ) , obj. getClass ( ) . getInterfaces ( ) ,
new InvocationHandler ( ) {
@Override
public Object invoke ( Object proxy, Method method, Object [ ] args) throws Throwable {
Object result = null ;
try {
transactionManager. beginTransaction ( ) ;
result = method. invoke ( obj, args) ;
transactionManager. commit ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
transactionManager. rollback ( ) ;
throw e;
} finally {
transactionManager. release ( ) ;
}
return result;
}
} ) ;
}
public Object getCglibProxy ( Object obj) {
return Enhancer . create ( obj. getClass ( ) , new MethodInterceptor ( ) {
@Override
public Object intercept ( Object o, Method method, Object [ ] objects, MethodProxy methodProxy) throws Throwable {
Object result = null ;
try {
transactionManager. beginTransaction ( ) ;
result = method. invoke ( obj, objects) ;
transactionManager. commit ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
transactionManager. rollback ( ) ;
throw e;
} finally {
transactionManager. release ( ) ;
}
return result;
}
} ) ;
}
}
然后再she包下,创建utils包,创建ConnectionUtils类:
package com. she. utils ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import java. sql. Connection ;
import java. sql. SQLException ;
@Service
public class ConnectionUtils {
private ThreadLocal < Connection > threadLocal = new ThreadLocal < > ( ) ;
@Autowired
private TransactionManager transactionManager;
public Connection getCurrentThreadConn ( ) throws SQLException {
Connection connection = threadLocal. get ( ) ;
if ( connection == null ) {
connection = DruidUtils . getInstance ( ) . getConnection ( ) ;
threadLocal. set ( connection) ;
}
return connection;
}
public void removeCurrentThreadConn ( ) {
threadLocal. remove ( ) ;
}
}
继续再改包下创建DruidUtils类:
package com. she. utils ;
import com. alibaba. druid. pool. DruidDataSource ;
public class DruidUtils {
private static DruidDataSource druidDataSource = new DruidDataSource ( ) ;
static {
druidDataSource. setDriverClassName ( "com.mysql.jdbc.Driver" ) ;
druidDataSource. setUrl ( "jdbc:mysql://localhost:3306/testspring" ) ;
druidDataSource. setUsername ( "root" ) ;
druidDataSource. setPassword ( "123456" ) ;
}
public static DruidDataSource getInstance ( ) {
return druidDataSource;
}
}
继续创建TransactionManager类:
package com. she. utils ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import java. sql. Connection ;
import java. sql. SQLException ;
@Service
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void beginTransaction ( ) throws SQLException {
connectionUtils. getCurrentThreadConn ( ) . setAutoCommit ( false ) ;
}
public void commit ( ) throws SQLException {
connectionUtils. getCurrentThreadConn ( ) . commit ( ) ;
}
public void rollback ( ) throws SQLException {
connectionUtils. getCurrentThreadConn ( ) . rollback ( ) ;
}
public void release ( ) throws SQLException {
Connection threadConnection = ( Connection ) connectionUtils. getCurrentThreadConn ( ) ;
try {
threadConnection. setAutoCommit ( true ) ;
connectionUtils. getCurrentThreadConn ( ) . close ( ) ;
connectionUtils. removeCurrentThreadConn ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
}
至此,我们基本操作完毕,现在我们来操作其他的包,创建pojo包,然后创建如下的类
package com. she. pojo ;
public class Test {
private Integer id;
private String name;
private Integer money;
public Integer getId ( ) {
return id;
}
public void setId ( Integer id) {
this . id = id;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public Integer getMoney ( ) {
return money;
}
public void setMoney ( Integer money) {
this . money = money;
}
@Override
public String toString ( ) {
return "Test{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}' ;
}
}
现在我们再she包里面创建dao包,然后创建TestDao接口:
package com. she. dao ;
import com. she. pojo. Test ;
public interface TestDao {
Test queryTestById ( Integer id) throws Exception ;
int updateTestById ( Test test) throws Exception ;
}
创建其实现类(记得对应的包也创建):
package com. she. dao. impl ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import com. she. dao. TestDao ;
import com. she. pojo. Test ;
import com. she. utils. ConnectionUtils ;
import java. sql. Connection ;
import java. sql. PreparedStatement ;
import java. sql. ResultSet ;
@Service
public class TestDaoImpl implements TestDao {
@Autowired
private ConnectionUtils connectionUtils;
@Override
public Test queryTestById ( Integer id) throws Exception {
Connection con = connectionUtils. getCurrentThreadConn ( ) ;
String sql = "select * from Test where id=?" ;
PreparedStatement preparedStatement = con. prepareStatement ( sql) ;
preparedStatement. setInt ( 1 , id) ;
ResultSet resultSet = preparedStatement. executeQuery ( ) ;
Test test = new Test ( ) ;
while ( resultSet. next ( ) ) {
test. setId ( Integer . valueOf ( resultSet. getString ( "id" ) ) ) ;
test. setName ( resultSet. getString ( "name" ) ) ;
test. setMoney ( resultSet. getInt ( "money" ) ) ;
}
resultSet. close ( ) ;
preparedStatement. close ( ) ;
return test;
}
@Override
public int updateTestById ( Test test) throws Exception {
Connection con = connectionUtils. getCurrentThreadConn ( ) ;
String sql = "update Test set money=? where id=?" ;
PreparedStatement preparedStatement = con. prepareStatement ( sql) ;
preparedStatement. setInt ( 1 , test. getMoney ( ) ) ;
preparedStatement. setInt ( 2 , test. getId ( ) ) ;
int i = preparedStatement. executeUpdate ( ) ;
preparedStatement. close ( ) ;
return i;
}
}
我们继续再she包下创建service包,然后创建TransferService接口:
package com. she. service ;
public interface TransferService {
void transfer ( Integer fromId, Integer toId, Integer money) throws Exception ;
}
对应的实现类:
package com. she. service. impl ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
import com. she. Annotation . Transactional ;
import com. she. dao. TestDao ;
import com. she. pojo. Test ;
import com. she. service. TransferService ;
@Service ( "transferService" )
@Transactional
public class TransferServiceImpl implements TransferService {
@Autowired
private TestDao testDao;
@Override
public void transfer ( Integer fromId, Integer toId, Integer money) throws Exception {
Test from = testDao. queryTestById ( fromId) ;
Test to = testDao. queryTestById ( toId) ;
from. setMoney ( from. getMoney ( ) - money) ;
to . setMoney ( to . getMoney ( ) + money) ;
testDao. updateTestById ( to ) ;
int c = 1 / 0 ;
testDao. updateTestById ( from) ;
}
}
在she包下创建测试包testspring,然后创建TestSpring类:
package com. she. testspring ;
import com. she. factory. BeanFactory ;
import com. she. service. TransferService ;
public class TestSpring {
public static void main ( String [ ] args) {
TransferService transferService = ( TransferService ) BeanFactory . getBean ( "transferService" ) ;
try {
transferService. transfer ( 1 , 2 , 100 ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
}
执行一下,若数据没有变化,那么我们将前面的int c = 1 / 0;去掉,继续执行,若数据发生改变,说明操作成功
当然,这里最主要的,就是Bean工厂的操作的,其他的都是比较容易的处理,因为工厂的存在,导致对应的类都进行了操作,我们直接的使用对应的类即可,但是我们还可以进行优化,我们知道,在spring中,存在四个一样的注解,只是名称不同而已,比如:@Component,@Controller,@Service,@Repository,实际上,我们只需要修改一个地方:
if ( aClass. isAnnotationPresent ( Service . class ) ) {
String beanName = aClass. getAnnotation ( Service . class ) . value ( ) ;
if ( aClass. isAnnotationPresent ( Service . class )
|| aClass. isAnnotationPresent ( Controller . class )
|| aClass. isAnnotationPresent ( Repository . class )
|| aClass. isAnnotationPresent ( Component . class ) ) {
String beanName = null ;
if ( aClass. isAnnotationPresent ( Service . class ) ) {
beanName = aClass. getAnnotation ( Service . class ) . value ( ) ;
} else if ( aClass. isAnnotationPresent ( Controller . class ) ) {
beanName = aClass. getAnnotation ( Controller . class ) . value ( ) ;
} else if ( aClass. isAnnotationPresent ( Repository . class ) ) {
beanName = aClass. getAnnotation ( Repository . class ) . value ( ) ;
} else if ( aClass. isAnnotationPresent ( Component . class ) ) {
beanName = aClass. getAnnotation ( Component . class ) . value ( ) ;
}
当然,这里有个重要的一个测试,在操作循环时,我们有对应的缓存来解决,但是其中也说明了在嵌套情况下,是不会操作赋值的,我们来验证这种情况,首先我们创建两个类,一个类是Test1,另外一个类是Test2,都放在she包下的test包下:
package com. she. test ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
@Service
public class Test1 {
@Autowired
private Test2 test2;
@Autowired
private Test1 test1;
@Autowired
private Test1 test3;
public Test2 getTest2 ( ) {
return test2;
}
public Test1 getTest1 ( ) {
return test1;
}
public Test1 getTest3 ( ) {
return test3;
}
}
package com. she. test ;
import com. she. Annotation . Autowired ;
import com. she. Annotation . Service ;
@Service
public class Test2 {
@Autowired
private Test1 test1;
public Test1 getTest1 ( ) {
return test1;
}
}
创建Test3类来操作:
package com. she. test ;
import com. she. factory. BeanFactory ;
public class Test3 {
public static void main ( String [ ] args) {
Test1 test1 = ( Test1 ) BeanFactory . getBean ( "test1" ) ;
Test2 test2 = test1. getTest2 ( ) ;
System . out. println ( test1) ;
System . out. println ( test1. getTest2 ( ) ) ;
System . out. println ( test2) ;
System . out. println ( test2. getTest1 ( ) ) ;
System . out. println ( test1. getTest1 ( ) ) ;
System . out. println ( test1. getTest3 ( ) ) ;
}
}
执行看看结果,可以发现,都有数据,且没有死循环,也就是说,没有操作赋值,而是直接退出了
至此,我们的注解操作就操作完毕了,总结:
通过读取xml的依赖,首先读取对应的属性信息,然后通过类加载器拿取当前项目classes的路径,拼接这个信息,找寻对应的文件,然后通过文件操作,得到所有的相关class的全限定名称,并保存,最终将这些名称创建Class来创建对象,并且利用Class的结构操作找到对应的被注解的类,将对应的对象放入map中,然后我们通过操作map拿取这些对象,经过遍历将找到对应的成员变量的注解来进行操作赋值,通过变量对应的类型来获得其对象,使得赋值,利用缓存技术,使得都进行赋值,然后我们继续进行操作遍历,找到被事务注解注解的类,将他操作代理,覆盖原来的类,若实现接口,那么jdk代理,否则cglib代理,这是我们的通用操作,至此,整个Bean工厂操作完毕,当这个工厂操作完毕后,像一些事务什么的,都是一些小问题,如连接池,线程绑定连接,事务的封装(提交,回滚等等),都很容易实现了,至此,我们手动实现Spring注解的功能就操作完毕了
通过上面的操作,不难发现,xml和注解都只是读取信息的不同而已,这也是为什么xml和注解都可以存在的原因,并且注解由于有隐藏的信息,所以在一定程度上比较简便,只是他虽然简便,但是需要大量的遍历,无论是扫描还是什么都基本上遍历的操作比xml要多,也就是说,虽然你比较简便,但是执行时间需要更多,而注解,拿取信息就可以操作了,自然不需要遍历了,最简单的案例就是文件哪里,如果com.she变成com呢,是否要加大遍历范围呢,而且,如果是注解的话,那么我们是直接写好全限定名的,自然不需要遍历,只需要拿取对应的信息即可,所以,当然,我们还会考虑读取xml的信息与读取类结构来的信息的差距,一般来说他们的差距我们忽略不计,所以如果方便,使用xml,如果需要时间,使用xml,但是现在大多少都是方便,所以注解使用的多,当然,我们也可以看到,注解和xml是可以互相替换的,自然可以考虑xml和注解的同时操作,一般情况下,xml覆盖注解,但是也可能会出现冲突,使得不生效或者报错,具体看框架自身的处理方式,这里了解即可