IoC

什么是IoC?

IoC是Inversion of Control(控制反转)的简称,注意它是一个技术思想。描述的是对象创建、管理的事情。

  • 传统开发方式:比如类A依赖类B,往往会在类A里面new一个B的对象。

  • IoC开发方式:我们不用去new对象,由IoC容器帮我们实例化对象并进行管理。我们需要B对象,就问IoC容器要即可。

控制反转就是说将对象创建、管理的权力交给了外部环境(IoC容器)。

IoC的作用:解决了对象之间的耦合问题。

什么是DI?

DI是Dependancy Injection(依赖注入)的简称,指容器会把对象依赖的其他对象注入。比如A对象里声明了一个B的属性,那么就需要容器把B对象注入给A。

什么是AOP?

AOP是 Aspect oriented Programming(⾯向切⾯编程)的简称。

在上面的代码中,多个方法都出现了相同的代码(可以称之为横切逻辑代码)。这部分代码不仅重复,而且跟业务逻辑没有关系但是混杂在一起。这时AOP出现了,它提供了横向抽取机制,将这部分横切代码和业务逻辑代码分开。

AOP的作用:在不改变原有业务逻辑的情况下,增强横切逻辑代码,解耦合。

手写IOC

首先我们看一下在没有Spring之前,我们是怎么开发一个web程序的呢?

那么针对上面的两个问题,我们如何进行解决呢?

  • 我们除了用new实例化对象外,还可以用反射的技术。
  • 另外项目中往往有很多对象需要实例化,那么可以使用工厂模式来进行优化。

综上,我们可以用工厂模式+反射技术把对象都实例化好,放在一个map里面,如果需要某个对象,就可以直接从这个map里面取。

除此之外,我们还需要一个xml文件,里面来定义对象的全类名(反射需要),如果有依赖,还需要定义类与类之间的依赖关系。

<beans>

    <bean id="accountDao" class="com.mmc.ioc.dao.impl.AccountDaoImpl"></bean>
<bean id="transferService" class="com.mmc.ioc.service.impl.TransferServiceImpl">
<!--这里的name默认为set+name就是方法名-->
<property name="AccountDao" ref="accountDao"></property>
</bean>
</beans>

核心代码:

public class BeanFactory {

    private static Map<String,Object> beanMap=new HashMap<>();

    static {
InputStream inputStream=BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader=new SAXReader();
try {
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
List<Element> beans = rootElement.selectNodes("//bean");
for (Element element:beans){
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
Object instance = Class.forName(clazz).newInstance();
beanMap.put(id,instance);
} //实例完后填充对象的依赖
List<Element> propertys = rootElement.selectNodes("//property");
for (Element element:propertys){
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
Element parent = element.getParent();
String parentId = parent.attributeValue("id");
Object instance = beanMap.get(parentId);
Object refInstance = beanMap.get(ref);
Method setMethod = instance.getClass().getDeclaredMethod("set" + name,refInstance.getClass().getInterfaces());
setMethod.invoke(instance,beanMap.get(ref));
} } catch (Exception e) {
e.printStackTrace();
}
} public static Object getBean(String name){
return beanMap.get(name);
}
}

那么接下来我们想要使用对象的时候,就不用new了,而是从beanFactory里面去拿。

这样一个简易的AOP就完成了。

手写AOP实现

我们解决了上面的问题1,那么问题2事务控制如何解决呢?

分析:数据库事务归根结底是Connection的事务,connection.commit()提交事务,connection.rollback()回滚事务。

  • 我们是想保证service里的方法里面执行的众多数据库操作要么都成功,要么都失败。
  • 同一个service方法里面的dao层必须要使用的是同一个connection,也就是说同一个线程内要是同一个connection,所以可以使用ThreadLocal实现

改造完成:

  @Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
//关闭自动提交
connectionUtils.getThreadConn().setAutoCommit(false); try {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo); from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money); accountDao.updateAccountByCardNo(to);
int i=10/0;
accountDao.updateAccountByCardNo(from);
//提交事务
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滚事务
connectionUtils.getThreadConn().rollback();
throw e;
}
}

在两次update语句中间手动加了个异常,可以发现数据库两条数据都没变,说明事务控制成功。

但是如果多个方法都需要加事务控制的话,我们需要给多个方法加上下面这一套重复的代码

connectionUtils.getThreadConn().setAutoCommit(false);

try {
//省略部分代码
// -----
//提交事务
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滚事务
connectionUtils.getThreadConn().rollback();
throw e;
}

怎么解决呢?

我们可以通过代理模式给每个方法代理

代码如下:

public class ProxyFactory {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
} public Object getJdkProxy(Object object){
return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//关闭自动提交
connectionUtils.getThreadConn().setAutoCommit(false);
Object result;
try {
result= method.invoke(object,args);
//提交事务
connectionUtils.getThreadConn().commit();
} catch (Exception e) {
//回滚事务
connectionUtils.getThreadConn().rollback();
throw e;
}
return result;
}
});
} }

每个需要加事务的对象,只要调用getJdkProxy方法获取到代理对象,再使用代理对象执行方法,就能实现事务控制了。

使用方法如下:

private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
private TransferService transferService= (TransferService) proxyFactory.getJdkProxy(BeanFactory.getBean("transferService"));

这样就相当于把横切逻辑代码提取出来了,如果把这套机制抽出来就是AOP的实现了。

手撸一个IOC容器的更多相关文章

  1. 五分钟,手撸一个Spring容器!

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  2. 手写一个IOC容器

    链接:https://pan.baidu.com/s/1MhKJYamBY1ejjjhz3BKoWQ 提取码:e8on 明白什么是IOC容器: IOC(Inversion of Control,控制反 ...

  3. 曹工说Tomcat4:利用 Digester 手撸一个轻量的 Spring IOC容器

    一.前言 一共8个类,撸一个IOC容器.当然,我们是很轻量级的,但能够满足基本需求.想想典型的 Spring 项目,是不是就是各种Service/DAO/Controller,大家互相注入,就组装成了 ...

  4. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  5. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  6. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  7. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  8. 第二篇-用Flutter手撸一个抖音国内版,看看有多炫

    前言 继上一篇使用Flutter开发的抖音国际版 后再次撸一个国内版抖音,大部分功能已完成,主要是Flutter开发APP速度很爽,  先看下图 项目主要结构介绍 这次主要的改动在api.dart 及 ...

  9. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

随机推荐

  1. kibana操作

    一些KIBANA的操作,记录下,免下次重复写 #创建索引名为kb_question的索引,并添加mapping,即各字段属性 PUT kb_question { "mappings" ...

  2. 华为云计算IE面试笔记-eBackup有哪几种备份组网方式,各备份组网方式主要的应用场景及备份流程?

    应用场景: LAN-Base一般用于备份数据量小,且对备份窗口没有特殊要求的场景,此类场景下备份服务器和备份代理一般是虚拟机部署. LAN-Free一般用于备份数据量较大,且对备份窗口要求比较严格的场 ...

  3. YbtOJ#893-带权的图【高斯消元,结论】

    正题 题目链接:https://www.ybtoj.com.cn/problem/893 题目大意 给出一张\(n\)个点\(m\)条边的无向联通图,每条边正反向各有\(A,B,C\)三种边权. 保证 ...

  4. 一凡老师亲录视频,Python从零基础到高级进阶带你飞

    如需Q群交流 群:893694563 不定时更新2-3节视频 零基础学生请点击 Python基础入门视频 如果你刚初入测试行业 如果你刚转入到测试行业 如果你想学习Python,学习自动化,搭建自动化 ...

  5. LaTeX Vscode 配置

    安装:https://www.latexstudio.net/archives/51801.html LaTeX 安装 & 宏包升级 & 入门:https://blog.csdn.ne ...

  6. 前端快闪三:多环境灵活配置react

    你已经使用Create React App脚手架搭建了React应用,现在该部署了. 一般会使用npm run build或者yarn build构建出静态资源, 由web服务器承载. 您会体验到 多 ...

  7. 使用Dom4j、反射自定义实现xml与java对象互转

    一.前言 国庆假期临近,工作动力不强.所以写几篇之前项目中自己用到的一些可能有用的东西分享出来. 今天分享的是Xml与javaBean互转的自定义实现. 先说几种我知道的Xml与javaBean互转的 ...

  8. 模仿ToDoList

    1.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  9. Unity——对象池管理

    Unity对象池管理 一.Demo展示 二.逻辑 在游戏中会出现大量重复的物体需要频繁的创建和销毁:比如子弹,敌人,成就列表的格子等: 频繁的创建删除物体会造成很大的开销,像这种大量创建重复且非持续性 ...

  10. 找出某名珍贵药材的生长区域(ArcPy实现)

    一.背景 某种珍贵药材生长于山区,通过研究了解到这种药材生长具有严格的生长条件.为了能更好地保护该药材的生长环境,现在需要使用GIS空间分析方法,将药材适合生长区域找出来,以便为该物种保护提供依据. ...