发现是好久都没有写博客了,不得不说有时候工作真的是温水煮青蛙。而且现在框架层出不穷,在技术浪潮里,找到一条路变得比学习新技术更为重要

—Eddie Lee

Spring IOC

我曾经多次聊过Spring的底层原理,以及Spring Boot Operational Principle这篇文章中也详细描述了一些底层的运行原理,但真正从低往上聊,这个系列应该是第一次。现在课程,大多都讲的是应用,然后在摸一摸底层,深度是不够的。曾经老师就跟我讲过,现在你年轻,年前是你的资本,那等你老了呢,你的资本又是什么?用了多少API,会多少框架的配置嘛,绝对不是的,你的资本应该是你日积月累的,对整体的认知,对原理的探索,还有对工匠精神的敬畏。这不是一个初出茅庐的孩子所能拥有的,但也是你年龄增长之后必须要有的。

今天我把IOC这部分内容单独拿出来,也是希望能够让更多看到这篇博客的人,对底层和框架真正知识有一个大致的概念,当然,您要是前辈,我相信您在技术上的造诣比我高深,特别希望您能够对我的文章有所指正,您可以在首页的github链接中联系到我。

首先IOC容器指的究竟是什么,毋庸置疑ConcurrentHashMap

1
2
3
4
5
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

/** Names of beans that have already been created at least once. */
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

至于查询的逻辑,简单概括就是,不论你传几个参数,最终走的都是doGetBean这个方法,这个方法会找找缓存里有没有,mergedBeanDefinitions里找一找,总之各个地方找一找,没找到,就抛异常,找到了类型不对,抛异常,大概就这样

那所以我们要做的事情很明确了

  • 第一件事,启动的时候通过注解或者配置的方式,把需要交给容器管理的类注册到容器中
  • 第二件事,获取的时候,能获取到

这两件事情搞定了,其他东西就都是可有可无的,方便使用的

Bean容器工厂

既然是IOC容器,那最关键的,必然是容器工厂,也就是我们存储Bean对象的地方。
ps:我是基于Spring上一个版本的容器实现的仿制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 核心容器
*/
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/**
* 别名辅助容器
*/
private final Map<String, String> aliasesDefinitionMap = new ConcurrentHashMap<>(256);
/**
* 类型辅助容器
*/
private final Map<String, Class<?>> typeDefinitionMap = new ConcurrentHashMap<>(256);
/**
* 动态重制容器
*/
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(256);

我设置的容器比较多,原版本中,更多的内容在BeanDefinition这个对象中,是对Bean的一个封装。那我这边希望尽可能的简化,所以并没有在BeanDefinition中增加太多的逻辑

这个容器类的声明为public class DefaultBeanFactory implements BeanFactory, BeanRegister
表明同时具有BeanFactory的管理获取能力,BeanRegister的注册能力,拍平了很多原本的设计

获取的方法和注册的方法都很简单了,如果大家好奇,可以到我Github中查看相关的代码。

现在,我们已经完成了容器,也就是存Bean的工具和方法,接下来,我们需要把Bean对象注入进来了吧

Bean注入

之前有提到说,Bean注入有两种方式,第一种,手动配置,第二种,注解声明。
我们先来看第二种,注解的方式

定义一个注解

1
2
3
4
5
6
7
8
9
10
11
/**
* @author eddie
* @createTime 2019-02-13
* @description 自动注入
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoWried {
//没设置任何参数,需要的话可以在这里配置别名,是否单例等
}

注解处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @author eddie
* @createTime 2019-02-13
* @description 自动注入注解处理器
*/
public class AutoWriedProcessor implements AutoWriedService {

private BeanFactory factory;

/**
* 获取列上的注解 如果有AutoWried注解 则处理
* @param clazz
* @return
* @throws NoSuchMethodException
*/
private void classForAnnotation0(Class clazz) {
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
field.setAccessible(true);
AutoWried autoWried = field.getAnnotation(AutoWried.class);
if (autoWried != null){
Object injectBean = factory.getBean(field.getType());
if (Objects.isNull(injectBean)){
throw new RuntimeException("没有找到对应的Bean异常");
}
Object bean = factory.getBean(clazz);
try {
field.set(bean, injectBean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return;
}


@Override
public void start(String pkg, BeanFactory factory) {
this.factory = factory;
final List<Class<?>> allClassByPackageName = AnnotationUtils.getAllClassByPackageName(pkg);
for (Class<?> clazz: allClassByPackageName){
classForAnnotation0(clazz);
}
}
}

到此,注入的方式搞定了,诶,你会发现,容器和注入的类没有联系到一起啊,这各干各的没用的呀。
所以,我们还需要一个启动类,也就是Spring 中非常熟悉的XXXXXApplicationContext

ApplicationContext

作为一个集大成者,这个类涉及的继承实现关系就多了。先来看下类声明
public class ClassPathJsonApplicationContext extends AbstractApplicationContext implements ApplicationContext

AbstractApplicationContext的类声明
public abstract class AbstractApplicationContext implements Lifecycle, ConfigurableApplicationContext, BeanFactory

别害怕这个AbstractApplicationContext哈,他的实现非常简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}

@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(requiredType);
}

@Override
public boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}

@Override
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}

@Override
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isPrototype(name);
}

@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
return getBeanFactory().isTypeMatch(name, typeToMatch);
}

@Override
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}

@Override
public String getAliases(String name) {
return getBeanFactory().getAliases(name);
}

/**
* 获取bean工厂
* @return
* @throws IllegalStateException
*/
@Override
public abstract BeanFactory getBeanFactory() throws IllegalStateException;

都是去调的getBeanFactory()这个方法获取的BeanFactory,至于获取的是谁,由子类决定,对吧,最简单的模版模式

回到ClassPathJsonApplicationContext类,这个类的实现更加简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private static final Active active;

private static final AutoWriedService autoWriedService;

private ConfigLoader loader;

static {
/**
* 获取类上的注解 如果有Bean注解 则处理
*/
active = new DefaultBeanProcessor();
/**
* 获取列上的注解 如果有AutoWried注解 则处理
*/
autoWriedService = new AutoWriedProcessor();
}

public ClassPathJsonApplicationContext (String configPath){
log.info("start load context - 开始加载上下文 读取配置文件");
loader = new ConfigLoader(configPath);
log.info("start load annotation - 根据配置文件 加载所有注解类 bean类型");
start();
}

@Override
public BeanFactory getBeanFactory() {
return DefaultBeanFactory.getInstance();
}

@Override
public void start() {
BeanRegister register= DefaultBeanFactory.getInstance();
log.info("start load bean to IOC map - 开始注册bean对象到IOC容器");
active.activate(loader.getBaseBeanPackage(), register);
autoWriedService.start(loader.getBaseBeanPackage(), getBeanFactory());
}

@Override
public void stop() {
}

@Override
public boolean isRunning() {
return true;
}

致此,我们是不是实现了自动获取对象注入到这个Bean容器中了? 对嘛,注册成功了。
那细心的同学会想到一个问题,和发现一个点

这个问题时,获取完了,怎么用呢??
当然有个办法是通过ClassPathJsonApplicationContextgetBean方法,但我觉得很Low,Spring的AutoWried就很不错。

那关注到的那个点,就是AutoWriedProcessor,我们初始化的时候,有个AutoWriedProcessor这个类,没错,就干的这事。

自动注入

实现同样非常简单,先上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private BeanFactory factory;

/**
* 获取列上的注解 如果有AutoWried注解 则处理
* @param clazz
* @return
* @throws NoSuchMethodException
*/
private void classForAnnotation0(Class clazz) {
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
field.setAccessible(true);
AutoWried autoWried = field.getAnnotation(AutoWried.class);
if (autoWried != null){
Object injectBean = factory.getBean(field.getType());
if (Objects.isNull(injectBean)){
throw new RuntimeException("没有找到对应的Bean异常");
}
Object bean = factory.getBean(clazz);
try {
field.set(bean, injectBean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return;
}


@Override
public void start(String pkg, BeanFactory factory) {
this.factory = factory;
final List<Class<?>> allClassByPackageName = AnnotationUtils.getAllClassByPackageName(pkg);
for (Class<?> clazz: allClassByPackageName){
classForAnnotation0(clazz);
}
}

诶,乍一看,我们这个事是不是就做完了啊?

对嘛,主体功能已经OK了啊,你还想要什么呢,基于配置文件嘛?
好吧,既然提到了,这个小功能就来看下这个小功能

我就不放代码了。。。。就是写个xxx.json,然后再ClassPathJsonApplicationContext启动的时候加载,解析,然后用就完了。。。
默认必填那几个项,没获取到或者获取为Null,“”就报个错出来,经济又实惠
好,这个问题就算过了哈,没懂的,自己翻github去

那再请大家思考下,这个事是不是做完了??

其实还有个问题,有参构造器的情况还没有考虑,这个问题就留给看到这篇文章的兄弟们来实现吧

当然,你们肯定有比我方案更优的设计,或者我有什么不正确的地方,可以在我Github中提出Issus哟。
我的Github && 这个项目的地址

感谢大家的阅读时间,有机会我把这个博客开通留言功能,[开心的表情]