日后还会更新很多关于手写框架的文章,手写完成后,会使用真正的框架改造,然后剖析源码,希望大家给个关注、点赞、收藏,谢谢!
文章目录一、Spring 概念复习
1. 基本概念2. IoC编程思想和DI3. AOP编程思想 二、手写IoC容器
1. 通过xml文件配置bean实例2. 通过工厂生产实例 三、手写AOP
1. 使用JDBC *** 作数据库模拟银行转账,解决事务问题
1. 创建数据库和实体类,引入相关依赖2. 将连接与当前线程绑定,多个JDBC *** 作使用同一个连接3. service层添加事务控制 2. 面向切面,事务管理
1. TransactionManager事务管理器2. 动态代理改造3. 交给IoC管理 四、定义@Service、@AutoWired、@Transaction注解类五、高级使用和源码
一、Spring 概念复习另外,IoC和AOP两种思想,不是Spring提出,在Spring之前就提出了,只不过更偏向于理论化,而Spring在技术层次,把两个思想用Java语言做了非常好的实现
分层(Controller,Service,Mapper)的full-stack(全栈)轻量级开源框架(发展越来越好,已经可以提供全套的功能支持)以IoC和AOP为内核,提供展现层Spring MVC和业务层事务管理等众多企业级应用技术(Spring可以用在各层提供服务)可以整合开源世界众多著名的第三方框架和类库,已经是使用最多的Java EE企业应用开源框架(站在巨人的肩膀上,提供整合接口,可以整合其它很多优秀框架)
方便解耦合,简化开发
通过IoC容器,可以将对象间依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例设计模式类、属性文件解析等这些很底层的需求编写代码,可以更注重与上层的应用实现。
AOP编程的支持
方便进行面向切面的编程,许多用传统OOP不容易实现的功能,可以通过AOP轻松应付
声明式事务的支持(事务和业务代码没有耦合到一块)
一般我们只需要方法上加个@Transactional注解就可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式的方式灵活的进行事务的控制,提高开发效率和质量
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的 *** 作,随手就可以做
方便继承各种优秀框架
Spring 可以降低各种框架使用难度,提供了对各种优秀框架的直接支持(Struts、Hibernate、Hessian、Quartz等等)
降低JavaEE API的使用难度
Spring对JavaEE API(例如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
2. IoC编程思想和DISpring是一个分层非常清晰并且依赖关系、职责定位非常明确的轻量级框架,主要包括数据处理、Web、AOP/Aspects、Core Container、Test模块Spring依靠这些基本模块,实现了一个令人愉悦的融合了现有解决方案的零入侵的人轻量级框架,并且模块化的思想,我们需要什么模块,引用什么就可以了,一个模块包含多个jar包,如下图:
IoC是一个思想,不是技术实现,IoC思想下,是将对象,统一交给IoC容器管理我们不需要考虑对象的创建、销毁等管理问题,这些全部交给IoC容器去做IoC容器会帮我们实例化对象并管理它们,我们要用,就去和IoC容器要
控制:指对象的创建(实例化,管理)的权利反转:控制权交给外部环境(Spring框架、IoC容器)
一般我们Service(逻辑业务层)会使用dao(持久层)的功能,如果我们使用传统new对象的话,我们就需要和具体实现类耦合(一般我们都使用接口,但是new对象,需要用具体实现类)
//假设我们有一开始的实现类是UserDaoImpl UserDao userDao = new UserDaoImpl(); //当我们想要将实现类换为UserDaoInplNew,那么所有Service层的代码都需要修改 UserDao userDao = new UserDaoInplNew();
但使用Spring,我们只需要接口,不需要在Service层new实现类,此时无论我们怎么变换接口的实现类,都不需要修改Service层代码
@Autowired private UserDao userDao;
和IoC描述的是同一件事情,但是角度不一样IoC站在对象的角度,对象实例化及管理权利交(反转)给了容器DI站在容器的角度,会把对象依赖的其它对象注入,比如IoC容器中有A类和B类。A类依赖B类,那么我们实例化A类的时候,需要将B类对象注入给A对象
3. AOP编程思想假设有A类和B类,A类中有这样一段代码B b = new B();那么我们说A类依赖B类
下图中,每个动物都有吃和跑方法,那么就可以让他们继承公共父类Animal,而不用每个动物都写重复代码
假设我们要在每个方法执行前后做一些额外 *** 作,比如记录每个动物,什么时候开始吃,什么时候吃完,什么时候开始跑,什么时候跑完那么我们就需要在每个方法(eat()和run())的逻辑代码前后添加额外的代码,并且这些代码是重复的,当然我们要避免这种情况,这时就可以使用切面编程,下图中,可以看出,不使用切面编程,有很多重复代码
当然我们可以使用动态代理设计模式,而AOP就是动态代理的实现
上图中,可以发现,我们将每个方法(假设有100个方法)前后执行的额外动作,抽离出去,然后向一把刀一样,对着每个方法的两个点(一堆点,就组成了一个面),切了上去,就像一个切面,所以我们叫面向切面编程这样呢,我们将横切代码,和业务逻辑代码分开了(解耦合),而且,我们不用每个方法中都写横切代码,只需要写一份可以理解为,原来我们将横切代码做成点,在每一个方法进行横切,而AOP,是直接做成一把刀,一刀下去所有点都一起切了
二、手写IoC容器不改变原有业务逻辑的情况下,增强功能(横切逻辑代码),根本上解耦合,避免横切逻辑代码重复
为了避免使用接口,new实现类这种耦合问题,我们需要换一种实例化对象的方式除了new关键字实例化对象以外,我们还可以通过反射来获取对象实例只需要提供这个类的全限定类名(完整的类名例如:java.lang.String)然后通过Class.forName(“全限定类名”);就可以拿到这个类的对象但是这又有新的耦合问题,全限定类名如果写到类中,就会耦合,日后我们想要改,又得去改所有用到的类所以我们可以让它和类解耦合,将全限定类名配置到xml文件中
1. 通过xml文件配置bean实例使用工厂设计模式,通过反射技术生产对象工厂模式是解耦合的一种非常好用的方式
2. 通过工厂生产实例我们实例化的是特定的实现类 的全限定类名 class我们要给每一个实例配置一个唯一标识 id
读取xml文件,根据class全限定类名,反射实例化对象,然后以id为key,以实例化对象为value,存储到map中对外提供获取实例对象的接口(根据id)
虽然不用new实现类了,但是我们把工厂写死到代码里面了,也有一定程度的耦合理想的状态应该是
private AccountDao accountDao;
那实例对象从哪来呢,我们可以通过set方法来设置
xml中给bean实例配置属性,我们根据属性值AccountDao,调用setAccountDao方法,传入的参数用ref来指定(反射好的AccountDaoImpl映射,就是上面配置的内个)
工厂实例化对象完成后,维护对象依赖关系,根据属性将其注入
三、手写AOP给两个接口提供test()方法,两个实现类进行实现
写个测试类测试
当用户a给用户b转账5元后,程序正常情况下,最终A有5元,B有15元但是如果用户a转账5元后,程序出错,将前转给B的代码没有运行,最终就会发生用户A失去了5元,但是用户B没有收到钱的情况
如果换一下,B先+5元,然后A再-5元,那么程序出错后,B有15元,但是A没有-5元,依然有10元,这样银行就亏钱了那么我们希望什么结果呢?当程序出错后,及时回滚,假设a-5后,程序报错,我们要立即回滚,让a的余额重新变为10,然后通知转账失败
首先数据库事务,其实是Connection(连接)的事务,默认是自动提交事务
connection.setAutoCommit(false);//设置为不自动提交 connection.commit();//提交事务 connection.rollback();//回滚事务
那么两次update使用两个数据库连接Connection,这样的话,肯定不属于同一个事务控制而且事务控制我们要统一放到service层控制(一个service可能涉及到很多dao层 *** 作)
1. 使用JDBC *** 作数据库模拟银行转账,解决事务问题 1. 创建数据库和实体类,引入相关依赖两次update使用同一个connection连接
首先,一个请求过来肯定在一个线程中执行,无论多少次update,都在这个线程中,所以将connection和当前线程绑定,当前线程有关系的数据库 *** 作都使用这个Connection
把事务控制添加在service层
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for account -- ---------------------------- DROP TABLE IF EXISTS `account`; CREATE TABLE `account` ( `id` char(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL, `money` int(20) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of account -- ---------------------------- INSERT INTO `account` VALUES ('1', 'A', 10); INSERT INTO `account` VALUES ('2', 'B', 10); SET FOREIGN_KEY_CHECKS = 1;
2. 将连接与当前线程绑定,多个JDBC *** 作使用同一个连接
3. service层添加事务控制
如果JDBC *** 作使用不同连接,这些 *** 作不在一个事务中,是没办法控制的想要事务控制,就必须实现共用一个连接
2. 面向切面,事务管理
静态代理,是针对特定的一件事代理,比如你是租房中介代理,那么你需要单独的一个代理类处理租房这件事,租车代理,又需要单独写租车代理显然,使用spring的千千万,没办法为每一件事都写一个静态代理类,有办法,也太多了动态代理,由反射实现,不需要为每一个事件,都写一个代理类,它可以动态的为很多事进行代理,也就是根据委托对象的不同,生成不同的代理人
我们做一个代理工厂,根据我们的需要,选择不同的代理通过JDK(需要提供接口)或CGLIB(不需要接口)生产代理对象CGLIB需要引入额外jar包
3. 交给IoC管理
四、定义@Service、@AutoWired、@Transaction注解类配置beans.xml
ConnectionUtils类,有IoC管理,我们不用自己实现单例设计模式了
TransactionManager,不用自己实现单例,并且依赖ConnectionUtils,解耦合,让IoC依赖注入,不new对象
ProxyFactory,不自己实现单例,不new对象,让IoC帮忙注入
使用代理工厂时(AOP),和IoC要
我们要考虑注解有无value属性值(例如@Service(value=""))添加事务时,service层是否有接口,选择使用JDK还是CGLIB版本的代理对象
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)