Java:108-Spring的底层原理(下篇)

这里续写上一章博客(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相关语句:
-- 查看隔离级别	MySql默认隔离级别  repeatable read
select @@tx_isolation;

-- 设置隔离级别为 读已提交 set global transaction isolation level xxx(xxx代表隔离级别)
set global transaction isolation level read committed; -- Read committed(一般来说,对应的可以首字母小写)
-- 设置的是当前mysql连接会话的,并不是永久改变的,注意:实际上是永久的,只是由于并不会给已经存在的连接或者会话来说是不永久的,所以真实情况需要进行分开说明,属性值改变:永久,当前改变:不永久,需要新建会话
为了验证增删改查对隔离级别的影响,我们来操作如下(只是为了验证问题的出现,而不考虑全部增删改查操作),这里会操作大量的图片,这是保证测试的全面性:
首先是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")
//这两个注解是一起的,当然了main方法也是可以执行的
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);
    }
所以他们也只是相当于在会话中执行一次,有些可以得到对应或者对应事务的结果,即是否快照,即认为他们是单纯的执行就行了
当然,他是一个接口,所以需要实现类,一般他有如下的操作:
/*
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类
Dao层技术是jdbcTemplate或mybatis(一般mybatis可能并不存在这个类,所以通常只是操作jdbcTemplate,因为他通常属于Spring框架的,所以这里的mybatis通常只是说是整合Spring的mybatis)时:
 DataSourceTransactionManager 也是需要对应数据源的,是为了可以创建新的事务
 
Dao层技术是hibernate时:
 HibernateTransactionManager
 
Dao层技术是JPA时:
 JpaTransactionManager
*/
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中加上如下:
 <!--事务管理器交给IOC,注意:这个id会检查,也就是说,名称必须是transactionManager,否则报错-->
    <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是TransactionDefinition 的实现类
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置是否只读,false支持事务
        def.setReadOnly(false);
        // 设置事务隔离级别,可重复读mysql默认级别
        /*
            ISOLATION_DEFAULT 使用数据库默认级别
            ISOLATION_READ_UNCOMMITTED 读未提交
            ISOLATION_READ_COMMITTED 读已提交
            ISOLATION_REPEATABLE_READ 可重复读
            ISOLATION_SERIALIZABLE 串行化
         */
        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">

	<!--目标类放入IOC容器-->
	<bean id="lagouBean" class="com.lagou.LagouBean"></bean>

	<!--通知类放入IOC容器-->
	<bean id="lagouAspect" class="com.lagou.LagouAspect"></bean>


	<!--AOP配置-->
	<aop:config>
		<!--配置切面:切入点+通知-->
		<aop:aspect ref="lagouAspect"> <!--ref指定通知的类名位置,即上面的id-->
			<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()) {
            //经过大量的测试,只要你存在<aop:config>标签,都会给一个代理类,也就是说,代理每个人都是操作的,只是可能只对一些方法进行监听(大量的判断),当对应的current是一个代理时,那么我们进入对应的postProcessAfterInitialization里面看看(看后面)
			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 {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            //创建bean实例,仅仅调用构造方法,但是并没有设置属性
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
        //开始考虑三级缓存
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		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;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
        //到这里
		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));
		}

		// Initialize the bean instance.
        //初始化bean实例
		Object exposedObject = bean;
		try {
            
            //前面是创建bean,这里是填充属性(bean),现在我们进入了(之前没有进入)
			populateBean(beanName, mbd, instanceWrapper);
            //调用初始化方法,应用BeanPostProcessor后置处理器,我们进入这里
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
            
            //后面的省略了
 //..   
            
            //到这里,在前面我们分析过这里,操作后置的,现在他其实在操作后置之后,会操作一下代理,使得我们的aop的bean对象是代理对象了,前提是对应的aop是看到有这个方法,否则对应的类可能并不会变成代理类,这是因为bean的产生是在原来的后置方法后执行完毕后,bean才会考虑变成实例,而操作代理,必然是在bean的构造方法后进行操作,而在构造方法后的拦截,自然就是后置方法了,所以代理的操作也必然在后置方法中进行处理(后置方法的返回值就是其bean的实例值,最终被赋值的,前置方法也是如此,但是其为了保证不为null,事先进行的保留,也就是说,如果是null,结果不变,但是如果不是,那么改变,所以这里必然是代理的最终操作,即代理就是后置方法的对bean的操作),现在我们来看看后面的操作
            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()) {
            //进入这里面就是操作后置方法的(是bean的后置,而不是一开始的后置(应该还记得"后后"吧),前面我们可是测试过案例的)
			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;
		}

		// Create proxy if we have advice.
        //查找出和当前bean匹配的Advisor(存在,则返回保留了他的数组),看看是否存在对应的方法匹配于bean,若有,那么考虑变成代理,否则直接的返回bean,而不是变成代理(事务管理也是这个地方,所以当aop和他操作的事务一起时,是同时操作的,只是可能会分先后,但是为了保证事务在前面,所以当对应的aspect在advisor前面时,会报错,所以若存在advisor和aspect一起,那么必然是事务先操作,在事务之中操作aop的通知(不是事务的),然后操作方法)
		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 proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

            //判断是否要设置ProxyTargetClass为true
		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

            //把增强和通用拦截器对象合并,都适配成Advisor
		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);
	}

    //..
    
}

//@SuppressWarnings("serial"),在说明源码时这样的注解(或者注释)可能忽略了,因为并不需要特别的了解
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    //最终到这里
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            /*
当bean实现接口时,会用JDK代理模式
当bean没有实现接口,用cglib实现
那么很明显,我们测试的是没有实现接口的,那么应该在return new ObjenesisCglibAopProxy(config);里面
    */
		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);
		}
	}
    
    //..
    
}

//看看ObjenesisCglibAopProxy的方法getProxy,因为最终返回的是return createAopProxy().getProxy(classLoader);
//class ObjenesisCglibAopProxy extends CglibAopProxy {
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);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
            //最终创建的代理(记得CGLIB(cglib)的创建吗:Enhancer.create(accountService.getClass(), new MethodInterceptor() {)
			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();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
            //到这里
			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) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
        //是否眼熟:Enhancer.create(accountService.getClass(), new MethodInterceptor() {,虽然他是另外一种方式,但是都是创建对应的cglib代理对象
		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> {
    
    //一般他最终会操作的,被@Import导入的会变成bean,他通常返回一些全限定名称来操作对应的bean
@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
                //两个重要的组件,即AutoProxyRegistrar和ProxyTransactionManagementConfiguration
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}
    //..
    
}

//进入AutoProxyRegistrar(加载事务控制组件)
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    private final Log logger = LogFactory.getLog(getClass());

	/**
	 * Register, escalate, and configure the standard auto proxy creator (APC) against the
	 * given registry. Works by finding the nearest annotation declared on the importing
	 * {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
	 * attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
	 * {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
	 * subclass (CGLIB) proxying.
	 * <p>Several {@code @Enable*} annotations expose both {@code mode} and
	 * {@code proxyTargetClass} attributes. It is important to note that most of these
	 * capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
	 * single APC}. For this reason, this implementation doesn't "care" exactly which
	 * annotation it finds -- as long as it exposes the right {@code mode} and
	 * {@code proxyTargetClass} attributes, the APC can be registered and configured all
	 * the same.
	 */
	@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) {

        //我们看看InfrastructureAdvisorAutoProxyCreator类:
        /*
        public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
你全局搜索一下AbstractAutoProxyCreator,看看与后置通知有什么联系呢,自己全局搜索看看就知道了
        */
		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;
	}
    
    //..
}


//我们进入ProxyTransactionManagementConfiguration
@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();
        //向事务增强器中注入属性解析器transactionAttributeSource
		advisor.setTransactionAttributeSource(transactionAttributeSource());
        //向事务增强器中注入事务拦截器transactionInterceptor
		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 {
//..
    
    //看到下面的参数是否感觉有点熟悉呢,如isolation是否与隔离级别相关呢,如@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ,timeout = -1,readOnly = false)
    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 {
    //其中,他实现的MethodInterceptor接口是否与cglib对应的那个参数一样呢(并不一样,所以这里是invoke),且可以在前面看到:
    /*
      //把增强和通用拦截器(MethodInterceptor)对象合并,都适配成Advisor(具体怎么得到和生成,这里就不说明了,具体可以百度)
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		当然,有很多方法会操作,这里只是给出主要的,一般都是与xml类似操作的,只是读取方式不同,这里给出主要的操作(主要的扫描,到配置,都没有说明)

    */

 //..   
    
    	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
        //会触发原有业务逻辑调用,在调用时会增强事务(即对应的找到方法操作就在这里,当然,可能有其他操作使得确定的,这里了解即可),这样在属性解析后,我们进行增强,现在我们进入
		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 {

		// If the transaction attribute is null, the method is non-transactional.
         //获取属性解析器
		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)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
            //开启(创建)事务
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
                //如果目标方法抛出异常,会执行这个方法(获取事务管理器,执行回滚操作)
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
            //执行正常,那么提交(获取事务管理器,执行提交操作)
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			Object result;
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			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)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							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;
			}

			// Check result state: It might indicate a Throwable to rethrow.
			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>
            <!--mysql驱动-->
            <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) //保留到程序运行的时候,它会被加载进入到JVM中
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) //保留到程序运行的时候,它会被加载进入到JVM中
public @interface Service {
    String value() default ""; //加上默认值后,是可以选择不加参数value的,否则必须写,那么我们需要判断id名称了
    //注解默认访问权限是public
}

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) //保留到程序运行的时候,它会被加载进入到JVM中
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<>();
    //需要一个bean的存放
    private static Map<String, Object> map = new HashMap<>();

    //缓存已经进行过依赖注入的信息
    private static List<String> fieldsAlreayProcessed = new ArrayList<>();

    //在spring中,一般我们并不会固定的操作一个文件,但是这里由于是测试编写,所以这里来一个静态块
    static {
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("app.xml");

        //使用读取xml的依赖来进行处理
        //获取XML解析对象
        SAXReader saxReader = new SAXReader();
        try {
            //解析XML,获取文档对象document
            Document document = saxReader.read(resourceAsStream);
            //getRootElement():获得根元素
            Element rootElement = document.getRootElement();
            //直接找到这个标签,得到他的信息对象,但是其父类只能操作标签自身信息,所以这里需要强转
            Element element = (Element) rootElement.selectSingleNode("//component-scan");
            //获得对应属性的值
            String attribute = element.attributeValue("base-package");
            System.out.println(attribute);
            //拿到这个值后,那么我们就能只能要扫描的包路径了
            //但是操作是有很多的,所以我们需要进行封装

            //扫描,拿取所有的全限定名放入classNames中
            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("\\.", "/");
        //获取对应的对象(目录和文件都可以,这里是目录)
        //防止中文乱码,因为上面在获取绝对路径时是操作了URL类的,在URL中对中文是有特别处理的
        try {
            scanPackagePath = URLDecoder.decode(scanPackagePath, StandardCharsets.UTF_8.toString());
        }catch (Exception e){
            e.printStackTrace();
        }
        File pack = new File(scanPackagePath);
        //File[] listFiles(),获取该目录下的所有内容(文件的对象)
        File[] files = pack.listFiles();
        //进行遍历
        for (File file : files) {
            //看看是否是目录
            if (file.isDirectory()) {
                // 递归,拿取文件名称进行封装,也就形成了递归,假设之前的是file:/C:/dd,dd里面有hh,那么执行这个就会变成file:/C:/dd/hh
                doScan(scanPackage + "." + file.getName());
                continue;

            }
            //boolean endsWith(String suffix),判断字符串是否以参数字符串结尾
            //如果是.class结尾了,那么进入
            if (file.getName().endsWith(".class")) {
                //如果包里面是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对象
                Class<?> aClass = Class.forName(className);
                //我们既然得到Class,那么就可以操作其结构,那么我们就可以判断他是否操作了对应的注解
                //如果是Class调用isAnnotationPresent,那么是判断对应的类上是否存在对应的注解
                //如果是Method 调用,那么是查看方法上是否存在该注解
                //如果是Field 调用,那么就是查看字段上是否存在该注解
                //当然还有很多情况,如方法的参数,局部变量等等,一般他们都存在这个方法
                if (aClass.isAnnotationPresent(Service.class)) {
                    //既然存在对应的注解,那么进入
                    //获取对应注解的value,这里需要写上Class<?>,当他不是?时,那么默认是extends右边的,也就没有value()了
                    //value()代表value的值,如果对应的注解存在e这个变量,那么就是e(),这是注解的操作方式
                    String beanName = aClass.getAnnotation(Service.class).value();


                    //创建实例
                    Object o = aClass.newInstance();

                    //String trim(),返回去掉前导和后继空白的字符串
                    //如果你都是空,那么就是空,即"   "与""等价
                    if ("".equals(beanName.trim())) {
                        //如进入这里,那么说明对应注解我们没有进行设置value,那么默认是"",操作首字母小写
                        //Class的getSimpleName方法是获取类名称
                        beanName = lowerFirst(aClass.getSimpleName());
                    }
                    //放入map,因为id(beanName)有了,对应的实例也有了,自然放入
                    //id作为类名称首字母小写或者指定的类名称id
                    map.put(beanName, o);

                    //当然,你也可以这样,由于我们的类通常有接口,所以在一定程度上是可以给接口id的,虽然spring并没有这样的操作(可能需要看版本),但并不意味着我们不能
                    //操作如下:
                    //Class<?>[] getInterfaces(),获取实现的所有接口
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if (interfaces != null && interfaces.length > 0) {
                        for (int j = 0; j < interfaces.length; j++) {
                            //如果你实现的是java.io.Serializable接口,那么打印这个anInterface时,结果是:interface java.io.Serializable
                            //如果接口是java.io.Serializable接口,那么对应的getName就是java.io.Serializable
                            Class<?> anInterface = interfaces[j];

                            // 以接口的全限定类名作为id放入,这里我们继续创建一个吧,在map中我们通常是不会指向同一个的
                            map.put(anInterface.getName(), aClass.newInstance());
                        }
                    }

                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //将str首字母进行小写
    private static String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if ('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32; //在ASCII中a是97,A是65,相差32
        }
        return String.valueOf(chars); //将字符变成String
    }

    //维护依赖注入,这里主要是前置操作,一个方法是存在前置维护(即参数),执行,后置维护(返回值),但是大多数返回值不会进行维护,所以一个方法的调用通常会存在另外一个方法进行前置维护
    //所以维护依赖注入一般需要两个方法
    private static void doAutoWired() {
        if (map.isEmpty()) { //如果map为空,自然直接的退出
            return;
        }

        // 遍历map中所有对象,查看对象中的字段,是否有@Autowired注解,如果有需要操作依赖注入关系
        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[] getDeclaredFields(),用于获取此Class对象所表示类中所有成员变量信息
        Field[] declaredFields = object.getClass().getDeclaredFields();
        //没有成员,那么退出
        if (declaredFields == null || declaredFields.length == 0) {
            return;
        }
        for (int i = 0; i < declaredFields.length; i++) {

            //拿取第一个成员变量的信息,如成员变量是public int i = 0;,那么得到public int com.she.factory.bb.i
            //其中值不会操作,在结构中,我们并不能操作其值,最多只能通过这个结构去设置创建的对象的值
            Field declaredField = declaredFields[i];

            //判断是否存在对应的注解,如果不存在,那么结束当前循环,而不是结束循环,看看下一个成员变量
            if (!declaredField.isAnnotationPresent(Autowired.class)) {
                continue;
            }
            //判断当前字段是否处理过,如果已经处理过则continue,避免嵌套处理死循环,这里我们的实现是与spring是不同的
            //在spring中,我们创建一个类的实例时,会顺便在注入时判断对方是否存在而创建对方的实例(是否可以创建实例,而不是直接创建,我们基本都处理了判断的(如配置或者注解)),从而考虑循环依赖
            //但是这里我们首先统一创建实例了,然后在得到对应的实例后,继续看看该实例里面是否也存在对应的注解,以此类推,直到都进行设置
            //但是如果存在对应的实例中,操作时是自身或者已经操作的实例(这个自身代表的是自己,而不是其他类的自己类的类型),那么就会出现死循环(如果我中有你,你中有我,且你已经操作的我不退出,这不是死循环是什么),所以就会直接的退出(退出当前循环,即continue;)
            //这里与Spring是极为不同的,Spring是报错,而这里是不会赋值,即退出,使得不会出现死循环(虽然我们也可以使得报错),即也就不会报错了,所以这里不会出现循环依赖的问题,因为我们对应的类就已经创建好了,就与你的三级缓存一样,虽然三级缓存是在需要的时候也保存了对应的实例,使得赋值,只是我首先统一操作的,那么我这里的缓存与三级缓存的本质还是一样的,都是保存实例,只是这里是保存id,因为实例都是创建好的,就不需要三级缓存将实例移动了
            //但是由于我们是统一处理的,而不是与spring一样,所以在一定程度上需要更多的空间,如果项目非常的大,那么这可能是一个不好的情况,要不然为什么spring是使用时创建呢,而由于这里是测试,所以我们使用这种方式,就不用考虑三级缓存的处理了

            //boolean contains(Object o),判断是否包含指定对象
            //判断当前类的全限定名加上该变量名称组成的字符串是否存在
            if (fieldsAlreayProcessed.contains(object.getClass().getName() + "." + declaredField.getName())) {
                continue;
            }

            Object dependObject = null;
            //先按照声明的是接口去获取,如果获取不到再按照首字母小写

            //拿取对应变量类型的全限定名,若是基本类型,那么就是本身,如int就是int
            dependObject = map.get(declaredField.getType().getName());

            //如果没有获取,那么根据当前变量类型的首字母小写去获取
            if (dependObject == null) {
                dependObject = map.get(lowerFirst(declaredField.getType().getSimpleName()));
            }
            //上面正好对应之前操作的接口和类的操作,当然,在Spring中,是查询全部,来找到对应类型的实例(getBean的),这里我们是自定义的,自然不会相同

            // 记录下给哪个对象的哪个属性设置过,避免死循环(递归的死循环)
            fieldsAlreayProcessed.add(object.getClass().getName() + "." + declaredField.getName());

            //递归
            doObjectDependancy(dependObject);

            //全部设置好后,我们进行设置
            //设置可以访问private变量的变量值,在jdk8之前可能不用设置
            //但是之后(包括jdk8)不能直接的访问私有属性了(可能随着时间的推移,也会改变),因为需要进行设置这个,所以不能直接访问私有属性了
            declaredField.setAccessible(true);
            //给对应的对象的该成员变量设置这个值
            declaredField.set(object, dependObject);


        }

    }


    //实现事务管理,为添加了@Transactional注解的对象创建代理对象,并覆盖原容器中的对象
    private static void doTransactional() {
        //拿取代理类的对象来操作代理
        ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            //得到key
            String beanName = entry.getKey();
            //得到value
            Object o = entry.getValue();
            //得到实例的Class对象
            Class<?> aClass = entry.getValue().getClass();
            //判断是否加上了对应的注解
            if (aClass.isAnnotationPresent(Transactional.class)) {
                // 需要进行事务控制
                // 有实现接口
                Class<?>[] interfaces = aClass.getInterfaces();
                if (interfaces != null && interfaces.length > 0) {
                    // 使用jdk动态代理
                    map.put(beanName, proxyFactory.getJdkProxy(o));
                } else {
                    // 使用cglib动态代理
                    map.put(beanName, proxyFactory.getCglibProxy(o));
                }

                //通过上面的代理就能将原来类变成代理类了,使得操作增强
            }
        }

    }
    
    //对外提供指定的类,只需要给出其id即可
    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());
        //获取当前类所在的总包的绝对路径(就到存在字节码的classes),当然类加载器都可以操作,但是为了严谨,这里使用线程保证是对的
        System.out.println(thread.getContextClassLoader().getResource(""));
        //单独的获取路径,比如上面的是file:/C:/dd,那么这里就是/C:/dd
        System.out.println(thread.getContextClassLoader().getResource("").getPath());
        String a = "com.jj.kk";
        //String replaceAll(String regex,String replacement)
        //将字符串中匹配正则表达式regex的字符串替换成replacement
        //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());
        }
    }
    /*
    com.she.factory.bb

    类类型是这样的
    public java.lang.Integer com.she.factory.bb.i
java.lang.Integer
Integer
class java.lang.Integer
i

基本类型是这样的
public int com.she.factory.bb.ii
int
int
int
ii


     */
}

然后继续创建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) {

        // 获取jdk代理对象
        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;
                    }
                });
    }

    //获取cglib代理对象
    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();

                    // 抛出异常便于上层servlet捕获,虽然这可能导致打印两次(因为,上面存在e.printStackTrace();,删除上面的也可以)
                    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;
    }

    //删除对应的key
    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();

            //解除线程绑定,就是删除对应key
            connectionUtils.removeCurrentThreadConn();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//实际上释放资源和删除key,我们一般会操作,主要是因为当你的线程操作完毕后,如果你操作完毕,那么对应的key还是存在,并且连接也没有归还,所以这里是不好的情况的,虽然大多数连接或者连接池都会判断你是否操作来确定进行自动释放,当然,这通常是连接池来操作的,否则单纯的连接,要么由垃圾回收来操作,或者你程序退出使得整体关闭回收(这里也可以认为是自动回收机制),否则一般会一直保留,因为他不操作连接池的话,即是直接的创建连接而不是复用

至此,我们基本操作完毕,现在我们来操作其他的包,创建pojo包,然后创建如下的类
package com.she.pojo;

/**
 *
 */
public class Test {

    //其实,有时候将所有的成员变量设成String也是可行的,只是并没有一些约束了,就如表我们也不加约束一样
    //也就是说,再极致的观察下(保证不会出现一些数据错误,比如他只能是id的号,自然是操作数字,而不是字母或者汉字),设置成String就是可行
    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 {
        //通过id查找出他们
        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); //com.she.test.Test1@29176cc1
        System.out.println(test1.getTest2()); //com.she.test.Test2@2f177a4b
        System.out.println(test2); //com.she.test.Test2@2f177a4b
        System.out.println(test2.getTest1()); //com.she.test.Test1@29176cc1
        System.out.println(test1.getTest1()); //com.she.test.Test1@29176cc1
        System.out.println(test1.getTest3()); //com.she.test.Test1@29176cc1

        //相同类型都是一样,因为我们是根据类型来的
        //并且没有死循环,所以他们都进行了操作
    }
}

执行看看结果,可以发现,都有数据,且没有死循环,也就是说,没有操作赋值,而是直接退出了
至此,我们的注解操作就操作完毕了,总结:
通过读取xml的依赖,首先读取对应的属性信息,然后通过类加载器拿取当前项目classes的路径,拼接这个信息,找寻对应的文件,然后通过文件操作,得到所有的相关class的全限定名称,并保存,最终将这些名称创建Class来创建对象,并且利用Class的结构操作找到对应的被注解的类,将对应的对象放入map中,然后我们通过操作map拿取这些对象,经过遍历将找到对应的成员变量的注解来进行操作赋值,通过变量对应的类型来获得其对象,使得赋值,利用缓存技术,使得都进行赋值,然后我们继续进行操作遍历,找到被事务注解注解的类,将他操作代理,覆盖原来的类,若实现接口,那么jdk代理,否则cglib代理,这是我们的通用操作,至此,整个Bean工厂操作完毕,当这个工厂操作完毕后,像一些事务什么的,都是一些小问题,如连接池,线程绑定连接,事务的封装(提交,回滚等等),都很容易实现了,至此,我们手动实现Spring注解的功能就操作完毕了
通过上面的操作,不难发现,xml和注解都只是读取信息的不同而已,这也是为什么xml和注解都可以存在的原因,并且注解由于有隐藏的信息,所以在一定程度上比较简便,只是他虽然简便,但是需要大量的遍历,无论是扫描还是什么都基本上遍历的操作比xml要多,也就是说,虽然你比较简便,但是执行时间需要更多,而注解,拿取信息就可以操作了,自然不需要遍历了,最简单的案例就是文件哪里,如果com.she变成com呢,是否要加大遍历范围呢,而且,如果是注解的话,那么我们是直接写好全限定名的,自然不需要遍历,只需要拿取对应的信息即可,所以,当然,我们还会考虑读取xml的信息与读取类结构来的信息的差距,一般来说他们的差距我们忽略不计,所以如果方便,使用xml,如果需要时间,使用xml,但是现在大多少都是方便,所以注解使用的多,当然,我们也可以看到,注解和xml是可以互相替换的,自然可以考虑xml和注解的同时操作,一般情况下,xml覆盖注解,但是也可能会出现冲突,使得不生效或者报错,具体看框架自身的处理方式,这里了解即可
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值