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. iSCSI 服务器搭建

    一.简介 SCSI(Small Computer System Interface),小型计算机系统接口,是一种用于计算机及其周边设备之间(硬盘.软驱.光驱.打印机.扫描仪等)系统级接口的独立处理器标 ...

  2. Ybt#452-序列合并【期望dp】

    正题 题目链接:https://www.ybtoj.com.cn/contest/113/problem/2 题目大意 一个空序列,每次往末尾加入一个\([1,m]\)中的随机一个数.如果末尾两个数相 ...

  3. 升级sudo版本

    1.查看sudo版本 sudo -V 2.下载sudo最新安装文件 sudo官方地址: https://www.sudo.ws/ 下载地址:https://www.sudo.ws/dist/ 3.解压 ...

  4. 好未来数据中台 Node.js BFF实践(一):基础篇

    好未来数据中台 Node.js BFF实践系列文章列表: 基础篇 实战篇(TODO) 进阶篇(TODO) 好未来数据中台的Node.js中间层从7月份开始讨论可行性,截止到9月已经支持了4个平台,其中 ...

  5. 在Unity中渲染一个黑洞

    在Unity中渲染一个黑洞 前言 N年前观看<星际穿越>时,被其中的"卡冈图雅"黑洞所震撼.制作团队表示这是一个最贴近实际的黑洞效果,因为它是通过各种科学理论实现的.当 ...

  6. docker efk(filebeat+logstash+es+kibana)

    ​ 1.系统架构 ​ 通常我们说的elastic stack,也就是elk,通过es 收集日志数据,存到elasticsearch,最后通过kibana进行统计分析,但是elastic公司后续又推出了 ...

  7. 【图像处理】基于OpenCV实现图像直方图的原理

    背景 图像的直方图是衡量图像像素分布的一种方式,可以通过分析像素分布,使用直方图均衡化对图像进行优化,让图像变的清晰. opencv官方对图像直方图的定义如下: 直方图是图像中像素强度分布的图形表达方 ...

  8. 重修 Tarjan

    Tarjan是谁 Tarjan's SCCs(有向图强连通分量)algorithm 给定⼀个有向图 \(G\),若存在 \(rt\in V\),满⾜从 \(rt\) 出发能到达 \(V\) 中的所有的 ...

  9. 关于dp那些事

    拿到一道题,先写出状态转移方程,再优化时间复杂度 状态优化: 对于状态可累加 \(e.g.dp[i+j]=dp[i]+dp[j]+i+j\) 的,用倍增优化 决策优化: \(e.g.dp[i][j]= ...

  10. xml文件报Element 'beans' cannot have character [children],because the type's content type is element

    写springMvc.xml文件时,偶然遇到 Element 'beans' cannot have character [children],because the type's content t ...