理解Spring:IOC的原理及手动实现
Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的。
要学习Spring,除了在我们的项目中使用之外,也需要对它的源码进行研读,但是Spring的实现涵盖的知识很多,在加上其中的类的数量也是非常的多,在我们阅读源码时可能会在几十个类之间穿插阅读,很有可能一不小心就导致思维混乱。
有鉴于此,我这里先对Spring中的几个重要的模块进行一个手动的简易实现,一是熟悉这些模块的原理,同时也是仿造Spring中的结构来对后面阅读源码打下基础。
IOC(Inversion of Control)
Inversion of Control即控制反转,其意思是将我们之前由客户端代码来创建的对象交由IOC容器来进行控制,对象的创建,初始化以及后面的管理都由IOC完成。
IOC的好处
解耦:IOC的出现解决了类与类之间的耦合,我们在Web开发的Servlet时代,如果一个Servlet需要依赖另一个类的某些实现,那么我们需要在当前类对依赖的类进行创建和初始化,如果其他类也依赖了这个类,那也需要进行创建和初始化,而交给了IOC来管理的话,那么在需要的时候只需向IOC进行申请,而不需要重复的创建和初始化。当然,IOC也允许每次都重新创建一个新的对象。
方便与AOP进行配合:AOP也是一个使用十分频繁的功能,通过IOC可以十分方便的与AOP进行配合。
IOC中设计的设计模式
工厂模式。IOC容器来负责创建管理类实例对象,在需要时向IOC进行申请,从IOC中获取。所以IOC容器也称为bean工厂。
工厂模式是一种比较简单易懂的设计模式,这里就不在介绍了,如果有需要的可以看看工厂模式。
IOC的手动实现
Bean定义
IOC的主要的功能便是对Bean进行管理,包括创建、初始化、管理以及销毁的工作。首先我们面对的问题就是我们怎么让IOC能够创建一个Bean?为了创建Bean我们需要提供一些什么?
如何创建Bean
在不手动通过new关键字创建的情况下创建类实例的对象方法有两种:
- 反射:通过反射的方法可以创建类的实例:clazz.getClass().newInstance();。
- 工厂模式:工厂模式可以让我们在不接触实例类的情况下创建出实例。
public class PersonFactory{
public Person getPerson(){
return new Person();
}
}
为了创建Bean我们需要提供什么
通过分析上面的两种方法可以轻松得出答案。
对于反射的方式我们仅需提供实例的Class对象。
对于工厂方法我们需要提供的就是创建该类的工厂名(factoryName)和方法名(methodName);
除了创建bean还需要做些什么
IOC容器是对bean的整个生命周期进行管理,除了创建之外还需要对bean进行初始化,以及不需要时对bean进行销毁的工作(如释放资源等)。所以我们还需要提供初始化和销毁等操作。
到这里创建bean需要的基本分析完了,看类图:
Bean工厂
Bean的定义解决了,但是这个bean定义以及创建好的Bean实例放在哪里呢,我们需要一个统一的地方来存放这些东西以方便我们要用的时候方便取。
我们定义一个Bean工厂来存放bean,在需要的时候从bean工厂中取即可,bean工厂对外提供的也仅仅是一个获取bean的方法即可,由于bean的类型不定,所以返回值定位Object。
注册Bean定义
到了现在我们有了创建bean的Bean定义,有了存放和管理bean的Bean工厂,现在需要考虑的是怎么来联系这两个类,我们还需要另外一个接口,接口的功能是让我们能注册和获取bean定义,这里我们通过beanName来区分不同的bean。参考:深究Spring中Bean的生命周期
代码实现
到这里我们实现一个简易的IOC容器的需要的东西基本准备完成了,看下基本类图:
基本代码实现:
DefaultBeanDefinition:
public class DefaultBeanDefinition implements BeanDefinition{
private Class<?> clazz;
private String beanFactoryName;
private String createBeanMethodName;
private String staticCreateBeanMethodName;
private String beanInitMethodName;
private String beanDestoryMethodName;
private boolean isSingleton;
// setter
public void setSingleton(boolean singleton) {
isSingleton = singleton;
}
@Override
public Class<?> getBeanClass() {
return this.clazz;
}
@Override
public String getBeanFactory() {
return this.beanFactoryName;
}
@Override
public String getCreateBeanMethod() {
return this.createBeanMethodName;
}
@Override
public String getStaticCreateBeanMethod() {
return this.staticCreateBeanMethodName;
}
@Override
public String getBeanInitMethodName() {
return this.beanInitMethodName;
}
@Override
public String getBeanDestoryMethodName() {
return this.beanDestoryMethodName;
}
@Override
public String getScope() {
return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;
}
@Override
public boolean isSingleton() {
return this.isSingleton;
}
@Override
public boolean isPrototype() {
return !this.isSingleton;
}
}
DefaultBeanFactory:
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {
private Log log = LogFactory.getLog(this.getClass());
//ConcurrentHashMap应对并发环境
private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
@Override
public void register(BeanDefinition bd, String beanName) {
Assert.assertNotNull("beanName不能为空 beanName", beanName);
Assert.assertNotNull("BeanDefinition不能为空", bd);
if(bdMap.containsKey(beanName)){
log.info("[" + beanName + "]已经存在");
}
if(!bd.validate()){
log.info("BeanDefinition不合法");
}
if(!bdMap.containsKey(beanName)){
bdMap.put(beanName, bd);
}
}
@Override
public boolean containsBeanDefinition(String beanName) {
return bdMap.containsKey(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) {
if(!bdMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return bdMap.get(beanName);
}
public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
Object instance = beanMap.get(beanName);
if(instance != null){
return instance;
}
//不存在则进行创建
if(!this.bdMap.containsKey(beanName)){
log.info("不存在名为:[" + beanName + "]的bean定义");
}
BeanDefinition bd = this.bdMap.get(beanName);
Class<?> beanClass = bd.getBeanClass();
if(beanClass != null){
instance = createBeanByConstruct(beanClass);
if(instance == null){
instance = createBeanByStaticFactoryMethod(bd);
}
}else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){
instance = createBeanByFactoryMethod(bd);
}
this.doInit(bd, instance);
if(instance != null && bd.isSingleton()){
beanMap.put(beanName, instance);
}
return instance;
}
private void doInit(BeanDefinition bd, Object instance) {
Class<?> beanClass = instance.getClass();
if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){
try {
Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);
method.invoke(instance, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 构造方法创建实例
* @param beanClass
* @return
*/
private Object createBeanByConstruct(Class<?> beanClass) {
Object instance = null;
try {
instance = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 普通工厂方法创建实例
* @param bd
* @return
*/
private Object createBeanByFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
//获取工厂类
Object factory = doGetBean(bd.getBeanFactory());
//获取创建实例的方法
Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());
//执行方法
instance = method.invoke(factory, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
/**
* 静态方法创建实例
* @param bd
* @return
*/
private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {
Object instance = null;
try {
Class<?> beanClass = bd.getBeanClass();
//获取创建实例的方法
Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());
instance = method.invoke(beanClass, null);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
@Override
public Object getBean(String beanName) {
if(!beanMap.containsKey(beanName)){
log.info("[" + beanName + "]不存在");
}
return beanMap.get(beanName);
}
@Override
public void close() throws IOException {
Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();
for(Map.Entry<String, BeanDefinition> entry: entries){
BeanDefinition value = entry.getValue();
String destoryMethodName = value.getBeanDestoryMethodName();
try {
Method method = value.getBeanClass().getMethod(destoryMethodName, null);
method.invoke(value.getBeanClass(), null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
简单测试一下:实例bean:
public class User {
private String name;
private int age;
//getter setter
public void init(){
System.out.println("init...");
}
public void destory(){
System.out.println("destory...");
}
}
工厂类:
public class TestFactory {
public Object createMethod(){
return new User();
}
public static Object staticCreateMethod(){
return new User();
}
}
测试类:
public class MySpringTest {
static DefaultBeanFactory factory = new DefaultBeanFactory();
@Test
public void test() throws IllegalAccessException, InstantiationException {
DefaultBeanDefinition bd = new DefaultBeanDefinition();
bd.setClazz(User.class);
bd.setSingleton(true);
bd.setBeanFactoryName("TestFactory");
bd.setCreateBeanMethodName("createMethod");
bd.setStaticCreateBeanMethodName("staticCreateMethod");
bd.setBeanInitMethodName("init");
factory.register(bd, "user");
System.out.println(factory.doGetBean("user"));
}
}
小结
一个简易的容器就这样实现了,当然我们这里只是具备了基本的功能,实际上还差的远,比如带参数的bean的实例化等功能。但是IOC的基本原理已经表达出来了,后面我们只需在这个基础上添加新的功能即可。
理解Spring:IOC的原理及手动实现的更多相关文章
- 深入理解Spring IOC工作原理
为什么会出现spring,spring出现解决了什么问题? 1.分析普通多层架构存在的问题 JSP->Servlet->Service->Dao 层与层之间的依赖很强,属于耦合而且是 ...
- 通俗化理解Spring3 IoC的原理和主要组件(spring系列知识二总结)
♣什么是IoC? ♣通俗化理解IoC原理 ♣IoC好处 ♣工厂模式 ♣IoC的主要组件 ♣IoC的应用实例 ♣附:实例代码 1.什么是IoC(控制反转)? Spring3框架的核心是实现控制反转(Io ...
- Spring系列之IOC的原理及手动实现
目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 导语 Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架.也是几乎所有J ...
- Spring IOC设计原理解析:本文乃学习整理参考而来
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
- Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计
在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...
- Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程
上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...
- Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...
- spring ioc aop 原理
spring ioc aop 原理 spring ioc aop 的原理 spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分. 在传统的程序设计中,当调 ...
- Spring IoC底层原理
-------------------siwuxie095 Spring IoC 底层原理 1.IoC 即 Invers ...
- Java工厂模式解耦 —— 理解Spring IOC
Java工厂模式解耦 -- 理解Spring IOC 最近看到一个很好的思想来理解Spring IOC,故记录下来. 资源获取方式 主动式:(要什么资源都自己创建) 被动式:(资源的获取不是我们创建, ...
随机推荐
- 初始化mysql报错bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory
原因:缺少libaio.so.1 解决办法:安装即可 yum install -y libaio
- No input file specified.问题的解决
问题描述:apache配置网站出现问题"No input file specified." 解决1: 打开.htaccess 在RewriteRule 后面的index.php教程 ...
- SpringBoot+MyBatis练手项目笔记汇总
以下是我在练习SpringBoot+MyBatis训练时候个人一些笔记汇总(可以点击跳转),献丑了,网上很多大佬的文章都比我写的详细,一些好的文章,我会将贴到各个内容中. 1. 插入数据返回id和内部 ...
- hdu - 1716 排列2 (使用set对全排列结果去重)
题意很简单,只是有几个细节要注意,首先就是一次只是输入四个数字.输出结果要从小到大(进行全排列之前要进行排序).题目要求千位数相同的在一行,中间使用空格隔开(第二次在输出的时候判断上一次记录的千位数是 ...
- NetCore3.1及Vue开发通用RBAC前后端通用框架
目录 框架说明 项目框架图 多租户权限设计表 效果图 后端拉取运行 前端项目请参考 前端系列 发布到docker中 netcore3.1 发布到docker中所遇到的坑及解决 框架说明 该框架是本人学 ...
- DVWA--SQL Injection
sql注入是危害比较大的一种漏洞,登录数据库可以进行文件上传,敏感信息获取等等. Low 先来看一下源码 <?php if( isset( $_REQUEST[ 'Submit' ] ) ) { ...
- 5Spring动态代理开发小结
5Spring动态代理开发小结 1.为什么要有动态代理? 好处 1.利于程序维护 2.利于原始类功能的增强 3.得益于JDK或者CGlib等动态代理技术使得程序扩展性很强 为什么说使得程序扩展性很强? ...
- [Qt] 事件机制(二)
在samp4_1中加一个小功能,点击右上角关闭按钮时,弹出"确认是否关闭"的消息框.如果点"yes"则关闭,如果点"No"则不关闭 在wid ...
- CentOS 8 配置 VNC Server
CentOS 8 配置 VNC Server 2020-12-31 | 标签: centos, vnc 前言 CentOS 8 配置 VNC Server, 使用户可以远程访问,本例介绍安装和配置流程 ...
- Linux讲解之定时任务
https://www.php.cn/linux-369884.html Linux讲解之定时任务 原创2018-05-14 10:11:3101319 本文目录: 12.1 配置定时任务 1 ...