引言
我们上一篇文章中,完成了BeanDefinition的创建,这个类中保存了我们需要实例化bean的信息,这篇文章就是模拟一个创建bean的底层实现,让我们的mini-spring有一个完整的注入功能。
ok,我们开始吧!😁
本文所有的代码都在这个项目工程里,大家需要的时候可以随时取用。传送门
创建Bean
目前为止,我们已经完成从@ComponentScan
注解定义的value中取到扫描包的位置,然后根据这个位置取出所有包含@Component
注解的类,并将其信息存储到BeanDefinition
中去。
接下来,我们要做的就非常简单了,那就是创建bean。我们需要在MiniSpringApplicationContext生产BeanDefinition之后直接创建bean,然后保存以供后续使用,具体代码如下:
package com.zhu.spring;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MiniSpringApplicationContext {
private Class configClass;
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public MiniSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//scan the class decorate by @ComponentScan
if(configClass.isAnnotationPresent(ComponentScan.class)){
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
//scan path, eg: com.zhu.service
String path = componentScanAnnotation.value();
// com.zhu.service ----> com/zhu/service
path = path.replace(".", "/");
//find absolute path from MiniSpringApplicationContext context
ClassLoader classLoader = MiniSpringApplicationContext.class.getClassLoader();
// get url , /Users/knight/IdeaProjects/mini-spring/out/production/mini-spring/com/zhu/service
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String absolutePath = f.getAbsolutePath();
if(absolutePath.endsWith(".class")){
//real load class
// /Users/knight/IdeaProjects/mini-spring/out/production/mini-spring/com/zhu/service/UserService ---> com.zhu.service.UserService
//com/zhu/service/UserService
String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
//com.zhu.service.UserService
className = className.replace("/", ".");
try {
Class<?> clazz = classLoader.loadClass(className);
if(clazz.isAnnotationPresent(Component.class)){
Component componentAnnotation = clazz.getAnnotation(Component.class);
String beanName = componentAnnotation.value();
//generate BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
}else{
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
//create bean 生成beandefinition之后,完成bean的初始化
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition.getScope().equals("singleton")){
//单例的时候创建,然后放到单例池,即一个map中去
Object bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, bean);
}
}
}
//具体初始化的代码,很简单利用Java的反射机制
private Object createBean(String beanName, BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
Object o = null;
try {
o = clazz.getConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return o;
}
public Object getBean(String beanName){
//从beandefinitionMap中获取对应类的信息
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition == null){
throw new RuntimeException("class not found with bean name:"+beanName);
}
String scope = beanDefinition.getScope();
if("singleton".equals(scope)){
//单例的话先判断单例池中有没有,没有的话需要创建,有的话直接返回
Object bean = singletonObjects.get(beanName);
if(bean == null){
Object createdBean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, createdBean);
return createdBean;
}
return bean;
}else{
//多例的话每次直接创建
return createBean(beanName, beanDefinition);
}
}
}
自此,我们基本模拟完成了spring创建bean的一个过程,简单实现了一个单例或者多例的功能。
ok,我们可以验证一下,修改UserService
里面@Scope
注解的值,查看生产的对象的地址是不是一致,即可验证单例功能是否生效,具体代码如下:
@Component("userService")
@Scope("prototype")
public class UserService {
}
package com.zhu.service;
import com.zhu.spring.MiniSpringApplicationContext;
public class Test {
public static void main(String[] args) {
MiniSpringApplicationContext miniSpringApplicationContext = new MiniSpringApplicationContext(AppConfig.class);
//经过测试发现地址都不一样,符合多例的初衷。
System.out.println(miniSpringApplicationContext.getBean("userService"));
System.out.println(miniSpringApplicationContext.getBean("userService"));
System.out.println(miniSpringApplicationContext.getBean("userService"));
System.out.println(miniSpringApplicationContext.getBean("userService"));
System.out.println(miniSpringApplicationContext.getBean("userService"));
}
}
总结:
目前为止,我们实现了简单类(就只有一个类,不依赖其他类)的注入。但是,这在我们实际生产中很少有这种简单的bean,一般都是需要依赖其他的bean。所以,我们下一步的目标就是完成依赖注入的流程。