目录
前言
之前实现了封装Bean信息,接下来实现依赖注入,同时在第一天时,我是将Bean创建好后直接放入SingletonObjects中,但在Spring的实现中,是将所有的Bean读取到BeanDefinitionMap中后再从Map集合中加载到SingletonObjects中的。
今天对之前的代码进行修改,按Spring的实现方式,进行加载Bean,同时实现依赖注入。
从BeanDefinitionMap中加载bean到单例池
在init方法中,只初始化了BeanDefinitionMap集合。接下来,我们要实现从Map集合中获取单例的bean加载到单例池,添加一个新的初始化单例池的方法
创建自定义注解
要实现依赖注入,那么一定要存在于一个注解,在这里我只实现了按bean名称注入,因此我们采用Spring中的@Resource的依赖注入方式
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyResource {
String value() default "";
}
创建initSingletonObjects方法
初始化SingletonObjects方法应该在初始化完BeanDefinitionMap之后执行,同时我们应该注意单例对象的注入以及非单例对象的注入方式不同。
private void initSingletonObjects() {
//从BeanDefinitionMap当中获取信息并加载单例对象到单例池中
Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
for (Map.Entry<String, BeanDefinition> entry : entries) {
createBean(entry);
}
}
遍历BeanDefinitionMap集合获取单个元素后执行createBean()方法,主要是去实现Bean对象的创建以及依赖注入问题,同时我们需要规避循环引用的问题,具体实现如下
/**
* 实现依赖注入
*
* @param entry
*/
private void createBean(Map.Entry<String, BeanDefinition> entry) {
//获取到类
Object o = null;
String beanName = entry.getKey();
if (cacheSingleton.containsKey(beanName)) {
o = cacheSingleton.get(beanName);
} else {
Class<?> clazz = entry.getValue().getClazz();
try {
o = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
//获取该类中的字段
Field[] declaredFields = o.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段上是否存在@MyResource注解
if (declaredField.isAnnotationPresent(MyResource.class)) {
MyResource annotation = declaredField.getAnnotation(MyResource.class);
//需要被注入的名称
String setBeanName = annotation.value();
if ("".equals(setBeanName)) {
//如果MyResource没有指定,那么以字段名为注入名
setBeanName = declaredField.getName();
}
if (!beanDefinitionMap.containsKey(setBeanName)) {
throw new RuntimeException("不存在该bean名称:" + setBeanName);
}
//开放权限
declaredField.setAccessible(true);
BeanDefinition beanDefinition = beanDefinitionMap.get(setBeanName);
try {
if (beanDefinition.getType().equals("Singleton")) {
//如果是单例bean
if (!cacheSingleton.containsKey(setBeanName)) {
//如果缓存单例池不存在,那么去单例池寻找
if (singletonObjects.containsKey(setBeanName)) {
//单例池存在的情况
Object o1 = singletonObjects.get(setBeanName);
declaredField.set(o, o1);
} else {
//单例池中也不存在
Object o1 = beanDefinition.getClazz().newInstance();
//存放在缓存单例池中
cacheSingleton.put(setBeanName, o1);
declaredField.set(o, o1);
}
} else {
//如果已经创建过了,但未完成创建
Object o1 = cacheSingleton.get(setBeanName);
declaredField.set(o, o1);
}
} else {
Object o1 = beanDefinition.getClazz().newInstance();
declaredField.set(o, o1);
}
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
//到这里执行结束,清除缓存单例池信息
cacheSingleton.remove(beanName);
singletonObjects.put(beanName, o);
}
以上代码实现了将单例Bean加载到单例池。对于非单例的Bean不需要添加到单例池中,其次加入到单例池的对象是已经注入好属性的对象,在createBean方法中也解决了循环依赖的问题,因为我又创建了一个cacheObjects属性用来保存没有创建完成的bean,在Spring中采用的是三级缓存,但是我只采用了一个。
完整ApplicationContext代码
public class SpringApplication2 {
//配置类属性
private Class<?> configClass;
//单例池
private ConcurrentHashMap<String, Object> singletonObjects;
//BeanDefinitionMap
private Map<String, BeanDefinition> beanDefinitionMap;
private Map<String, Object> cacheSingleton;
//每创建一个容器都会有各自的单例池
{
singletonObjects = new ConcurrentHashMap<>(256);
beanDefinitionMap = new ConcurrentHashMap<>(256);
cacheSingleton = new ConcurrentHashMap<>(256);
}
public SpringApplication2(Class<?> configClass) throws Exception {
this.configClass = configClass;
this.init();
this.initSingletonObjects();
}
private void init() throws Exception {
//判断该类是否是配置类
MyConfiguration configuration = configClass.getAnnotation(MyConfiguration.class);
if (configuration == null) {
//说明不是配置类
throw new RuntimeException("该类不是配置类,目前只支持传入配置类");
}
//如果是配置类,那么获取需要扫描的类路径
MyComponentScan scan = configClass.getAnnotation(MyComponentScan.class);
if (scan == null) {
throw new RuntimeException("不存在扫描注解");
}
String[] paths = scan.value();
//遍历需要扫描的路径
for (String path : paths) {
path = URLDecoder.decode(path, "UTF-8");
//path这里还有作用,因此不建议将path接收replace的结果
// com/zmt
String newPath = path.replace(".", "/");
//获取根路径,并去除最前面的/
String classPath = configClass.getResource("/").getPath().substring(1);
classPath = URLDecoder.decode(classPath, "UTF-8");
System.out.println("classPath:" + classPath);
//获取被扫描包的绝对路径
String absolutePath = configClass.getResource("/" + newPath).getPath();
absolutePath = URLDecoder.decode(absolutePath, "UTF-8");
System.out.println(absolutePath);
//遍历绝对路径的子包,寻找.class结尾的文件
File rootFile = new File(absolutePath);
if (rootFile.isFile()) {
throw new RuntimeException("不支持扫描具体类");
}
//说明指定路径是一个文件夹
//获取所有的类名称
List<String> listClass = dirFile(rootFile);
for (String absoluteClassPath : listClass) {
//完整类名,通过Class.forName()创建出该类
String fullClassName = absoluteClassPath.replace(classPath, "").replace("/", ".");
//加载出该类
ClassLoader classLoader = configClass.getClassLoader();
Class<?> aClass = classLoader.loadClass(fullClassName);
MyComponent myComponent = aClass.getAnnotation(MyComponent.class);
if (myComponent == null) {
//该类不应该被加载
continue;
}
//获取Bean名称
String beanName = getBeanName(fullClassName, aClass, myComponent);
//判断是否添加了@MyScope注解,通知判断该注解的值
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType("Singleton");
if (aClass.isAnnotationPresent(MyScope.class)) {
String value = aClass.getAnnotation(MyScope.class).value();
if (!"Singleton".equals(value) && "Prototype".equals(value)) {
//说明是不是非单例的对象
beanDefinition.setType("Prototype");
}
}
beanDefinition.setClazz(aClass);
beanDefinitionMap.put(beanName, beanDefinition);
}
}
}
private void initSingletonObjects() {
//从BeanDefinitionMap当中获取信息并加载单例对象到单例池中
Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
for (Map.Entry<String, BeanDefinition> entry : entries) {
createBean(entry);
}
}
/**
* 实现依赖注入
*
* @param entry
*/
private void createBean(Map.Entry<String, BeanDefinition> entry) {
//获取到类
Object o = null;
String beanName = entry.getKey();
if (cacheSingleton.containsKey(beanName)) {
o = cacheSingleton.get(beanName);
} else {
Class<?> clazz = entry.getValue().getClazz();
try {
o = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
//获取该类中的字段
Field[] declaredFields = o.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段上是否存在@MyResource注解
if (declaredField.isAnnotationPresent(MyResource.class)) {
MyResource annotation = declaredField.getAnnotation(MyResource.class);
//需要被注入的名称
String setBeanName = annotation.value();
if ("".equals(setBeanName)) {
//如果MyResource没有指定,那么以字段名为注入名
setBeanName = declaredField.getName();
}
if (!beanDefinitionMap.containsKey(setBeanName)) {
throw new RuntimeException("不存在该bean名称:" + setBeanName);
}
//开放权限
declaredField.setAccessible(true);
BeanDefinition beanDefinition = beanDefinitionMap.get(setBeanName);
try {
if (beanDefinition.getType().equals("Singleton")) {
//如果是单例bean
if (!cacheSingleton.containsKey(setBeanName)) {
//如果缓存单例池不存在,那么去单例池寻找
if (singletonObjects.containsKey(setBeanName)) {
//单例池存在的情况
Object o1 = singletonObjects.get(setBeanName);
declaredField.set(o, o1);
} else {
//单例池中也不存在
Object o1 = beanDefinition.getClazz().newInstance();
//存放在缓存单例池中
cacheSingleton.put(setBeanName, o1);
declaredField.set(o, o1);
}
} else {
//如果已经创建过了,但未完成创建
Object o1 = cacheSingleton.get(setBeanName);
declaredField.set(o, o1);
}
} else {
Object o1 = beanDefinition.getClazz().newInstance();
declaredField.set(o, o1);
}
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
}
//到这里执行结束,清除缓存单例池信息
cacheSingleton.remove(beanName);
singletonObjects.put(beanName, o);
}
private String getBeanName(String fullClassName, Class<?> aClass, MyComponent myComponent) {
//如果注解中没有没有给定值,那么采用类名首字母小写的方式
String beanName = myComponent.value();
if ("".equals(beanName)) {
System.out.println(aClass.getName() + "没有指定bean名称");
String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
}
return beanName;
}
/**
* 遍历文件夹,获取所有类绝对路径集合
*
* @param rootFile
* @return
*/
private List<String> dirFile(File rootFile) {
List<String> fileList = new ArrayList<>();
if (rootFile.isDirectory()) {
File[] files = rootFile.listFiles();
for (File file : files) {
fileList.addAll(dirFile(file));
}
} else {
//走到这说明已经不是文件夹了
String filePath = rootFile.getPath();
if (filePath.endsWith(".class")) {
//类的绝对路径
String classPath = filePath.replace("\\", "/").replace(".class", "");
System.out.println(classPath);
fileList.add(classPath);
}
}
return fileList;
}
public Object getBean(String beanName) {
Object o = this.singletonObjects.get(beanName);
if (o == null) {
throw new RuntimeException("不存在bean为:" + beanName);
}
return o;
}
}
测试
public class SpringStart {
public static void main(String[] args) throws Exception {
SpringApplication2 springApplication = new SpringApplication2(SpringConfig.class);
UserController userController = (UserController) springApplication.getBean("userController");
UserServiceImpl userService = (UserServiceImpl) springApplication.getBean("userServiceImpl");
System.out.println(userController.getUserDao() == userService.getUserDao());
}
}
在上面测试代码中,只有UserDao上添加了@MyScope注解并值为Prototype,而Controller与Service类中均依赖了Dao,测试两个类中的Dao是否是同一个对象。其次,Controller与Service又互相引用对方,我们测试能否执行测试通过

由此可以看出,实现了单例与非单例的加载也实现了依赖注入。并且循环引用的问题也可以得到解决。
本文详细描述了如何按照Spring框架的实现方式,从BeanDefinitionMap中加载Bean到单例池,并创建自定义注解进行依赖注入,处理单例与非单例对象及循环引用问题。
1050





