手写Spring01_之如何扫描+获取Bean(原型还是单例)
0x01_说明
暂时不涉及懒加载,暂时用扫描配置的方式进行容器的创建。
0x02_核心注解
MyComponent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.myspring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 只写在类上面
public @interface MyComponent {
// bean的name
String value() default "";
}
MyComponentScan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.myspring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 只写在类上面
public @interface MyComponentScan {
// 扫描的路径
String value() default "";
}
MyScope
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.myspring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* bean是单例的还是prototype
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 只写在类上面
public @interface MyScope {
String value();
}
0x03_BeanDefinition
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
package com.myspring.core;
public class MyBeanDefinition {
//bean的类型
private Class clazz;
//bean的作用域,是单例的还是原型
private String scope;
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
0x04_容器
MyApplicationContext
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package com.myspring;
import com.myspring.annotation.MyComponent;
import com.myspring.annotation.MyComponentScan;
import com.myspring.annotation.MyScope;
import com.myspring.core.MyBeanDefinition;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 容器类
*/
public class MyApplicationContext {
// 配置类
private Class configClass;
// 单例池,存储单例对象
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
// beanDefinition的Map
private ConcurrentHashMap<String,MyBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public MyApplicationContext(Class configClass) {
this.configClass = configClass;
//解析配置类
/**
* 解析注解@MyComponentScan 扫描什么路径--->开始扫描
*/
//获取配置类上面的MyComponentScan注解
scan(configClass);
for (Map.Entry<String,MyBeanDefinition> entry : beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
MyBeanDefinition beanDefinition = entry.getValue();
if (beanDefinition.getScope().equals("singleton")){
//如果是单例bean,就要创建一个bean
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName,bean);
}
}
}
public Object createBean(MyBeanDefinition beanDefinition){
Class clazz = beanDefinition.getClazz();
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private void scan(Class configClass) {
MyComponentScan myComponentScanAnno = (MyComponentScan) configClass.getDeclaredAnnotation(MyComponentScan.class);
//获取注解上的属性,即扫描路径
String path = myComponentScanAnno.value();
path = path.replace(".","/");
//扫描,利用类加载器才能获取某一个类上的注解,这样才能扫描到应该扫描的bean
//类加载器有3中:BootStrap,Ext,App
//BootStrap路径:jre/lib
//Ext路径:jre/ext/lib
//App路径:classpath
ClassLoader classLoader = MyApplicationContext.class.getClassLoader();// App应用类加载器
URL resource = classLoader.getResource(path);//相对路径
File file = new File(resource.getFile());//获取目录
if(file.isDirectory()){//是一个目录
File[] files = file.listFiles();//获取目录下所有的文件
for (File f : files) {
String fileAllName = f.getAbsolutePath();
if (fileAllName.endsWith(".class")){//判断是不是class文件
//获取全路径名,比如:com.bones.service.EmpService.class
String className = fileAllName.substring(fileAllName.indexOf("com"),fileAllName.indexOf(".class"));
className = className.replace("/", ".");
try {
Class<?> aClass = classLoader.loadClass(className);
if (aClass.isAnnotationPresent(MyComponent.class)){
//这里暂且不考虑懒加载
//当前这个类是一个bean,创建一个bean对象
//解析类,判断bean到底是单例的还是原型的,如果是单例的,就应该放入单例池中,如果是原型bean就不应该放入
//解析类做的就是生成一个BeanDefinition对象
//beanDefinition,spring核心概念,不用反复解析某一个类(否则下面的getBean方法也会要解析类,这样做就太麻烦了)
MyComponent componentAnno = aClass.getDeclaredAnnotation(MyComponent.class);
String beanName = componentAnno.value();
MyBeanDefinition beanDefinition = new MyBeanDefinition();
beanDefinition.setClazz(aClass);
if (aClass.isAnnotationPresent(MyScope.class)){//原型
MyScope scopeAnno = aClass.getDeclaredAnnotation(MyScope.class);
beanDefinition.setScope(scopeAnno.value());
}else {//否则就是单例
beanDefinition.setScope("singleton");
}
//存储beanDefinition到beanDefinitionMap中
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
}
/**
* 获取bean
* @param beanName 要获取的bean的名称
* @return 返回Bean对象
*/
public Object getBean(String beanName){
if (beanDefinitionMap.containsKey(beanName)){//当前要获取的bean对象是beanDefinitionMap中已经有的
MyBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")){
//是单例bean,从单例池中拿这个bean
return singletonObjects.get(beanName);
}else {
//原型bean,需要创建一个bean对象
return createBean(beanDefinition);
}
}else{//不存在这个bean
throw new RuntimeException("不存在对应的bean:"+beanName);
}
}
}
0x05_测试单例Bean和原型bean的获取
测试类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bones;
import com.myspring.MyApplicationContext;
import com.bones.config.AppConfig;
public class Test {
public static void main(String[] args) {
MyApplicationContext applicationContext = new MyApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("EmpServiceImpl"));
System.out.println(applicationContext.getBean("EmpServiceImpl"));
System.out.println(applicationContext.getBean("EmpServiceImpl"));
}
}
对于一个要注入容器的bean
1
2
3
4
5
6
7
8
9
10
11
package com.bones.service;
import com.myspring.annotation.MyComponent;
import com.myspring.annotation.MyScope;
@MyComponent("EmpServiceImpl")
//@MyScope("prototype")
public class EmpServiceImpl implements EmpService {
}
首先注释掉@MyScope("prototype")
,表示要获取的是单例的bean,
得到控制台的打印:
打开注解@MyScope("prototype")
,再次测试:
得到的bean都是原型bean
0x06_核心概念:BeanDefinition
上面的代码中核心概念就是BeanDefinition
什么是BeanDefinition
官网有以下介绍:
SpringIoc容器管理一个Bean或多个Bean,这些Bean通过我们提供给容器的配置元数据被创建出来(例如,在xml中的定义) 在容器中,这些Bean的定义用BeanDefinition对象来表示,包含以下元数据:
- 全限定类名, 通常是Bean的实际实现类;(就是上面代码中的
private Class clazz;
)- Bean行为配置元素,它们说明Bean在容器中的行为(作用域、生命周期回调等等);(上面代码中只涉及了Scope这个属性)
- Bean执行工作所需要的的其他Bean的引用,这些Bean也称为协作者或依赖项;
- 其他配置信息,例如,管理连接池的bean中,限制池的大小或者使用的连接的数量。
Spring官网中对BeanDefinition的解释还是很详细的,但是不是那么通俗易懂,其实BeanDefinition是比较容易解释的:BeanDefinition就是用来描述一个Bean或者BeanDefinition就是Bean的定义,利用BeanDefinition,可以用反射来创建bean.