手写模拟Spring容器创建Bean对象
- 过程:
简单模拟spring容器 扫描 – > BeanDefinition bean信息 --> 创建单例bean或者原型bean对象 --> put到 singletonMap
idea创建maven工程:
接下来就是扫描路径,在使用springboot时我们常用componentScan注解 来标注扫描的路径,先写一个componentScan注解
package com.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: wangfei
* @date: 2023/1/6 16:24
* @version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
在写一个AppConfig类,在类上贴上 @ComponentScan注解
package com.wangfei;
import com.spring.annotation.ComponentScan;
/**
* @author: wangfei
* @date: 2023/1/6 16:22
* @version: 1.0
*/
@ComponentScan("com.wangfei.service")
public class AppConfig {
}
补上需要的注解 @Component,bean对象作用域的注解@Scope
package com.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: wangfei
* @date: 2023/1/9 11:16
* @version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value();
}
package com.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: wangfei
* @date: 2023/1/6 16:24
* @version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
Spring的容器类是叫 ApplicationContext,那么我们也模拟写一个容器类 WangfeiApplicationContext
package com.spring;
import com.spring.annotation.BeanDefinition;
import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author: wangfei
* @date: 2023/1/6 16:18
* @version: 1.0
*/
public class WangfeiApplicationContext {
private Class configClass;
}
写一个Test类
package com.wangfei;
import com.spring.WangfeiApplicationContext;
/**
* @author: wangfei
* @date: 2023/1/6 16:17
* @version: 1.0
*/
public class Test {
public static void main(String[] args) {
WangfeiApplicationContext applicationContext = new WangfeiApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));
System.out.println(applicationContext.getBean("userService"));
}
}
创建UserService类, 暂不进行属性注入
package com.wangfei.service;
import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
/**
* @author: wangfei
* @date: 2023/1/6 16:28
* @version: 1.0
*/
@Component("userService")
public class UserService {
// @Autowired
// private OrderService orderService;
}
完善WangfeiApplicationContext容器
package com.spring;
import com.spring.annotation.BeanDefinition;
import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author: wangfei
* @date: 2023/1/6 16:18
* @version: 1.0
*/
public class WangfeiApplicationContext {
private Class configClass;
/**
* 单例池
*/
private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap(128);
/**
* bean信息定义
*
* @param configClass
*/
private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public WangfeiApplicationContext(Class configClass) {
this.configClass = configClass;
// 解析配置类
// ComponentScan注解 ----> 扫描路径 ----> ---- 扫描 --> BeanDefinition ---> beanDefinitionMap
scan(configClass);
int a[][] = new int[][3];
for (Map.Entry<String, BeanDefinition> entry: beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
if (beanDefinition.getScope().equals("singleton")) {
// 创建bean对象
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName, bean);
}
}
}
/**
* 根据BeanDefinition定义信息 创建 Bean对象
* @param beanDefinition
* @return
*/
public Object createBean(BeanDefinition beanDefinition){
Class clazz = beanDefinition.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private void scan(Class configClass) {
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
// 扫描
// BootStrap --> jre
// Ext --------> jre/ext/lib
// App --------> classpath
ClassLoader classLoader = WangfeiApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource("com/wangfei/service");
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
className = className.replace("\\", ".");
try {
Class<?> clazz = classLoader.loadClass(className);
if (clazz.isAnnotationPresent(Component.class)) {
// 表示当前这个类是一个Bean
// .....? Class --> bean ? 解析类,判断当前bean是单例,还是prototype的bean
// BeanDefinition
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
BeanDefinition definition = new BeanDefinition();
definition.setClazz(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
definition.setScope(scopeAnnotation.value());
} else {
definition.setScope("singleton");
}
beanDefinitionMap.put(beanName, definition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
}
public Object getBean(String beanName) {
if (beanDefinitionMap.containsKey(beanName)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
Object o = singletonObjects.get(beanName);
return o;
} else {
// 创建bean对象?
Object bean = createBean(beanDefinition);
return bean;
}
} else {
// 不存在对应的bean对象
throw new NullPointerException();
}
}
}
创建BeanDefinition类, 用来定义代替Bean的信息,同时WangfeiApplicationContex容器中也有一个 ConcurrentHashMap<String, BeanDefinition> 来存储Bean的定义信息
package com.spring;
/**
* @author: wangfei
* @date: 2023/1/9 11:28
* @version: 1.0
*/
public class BeanDefinition {
// bean的类
private Class clazz;
// bean的作用域
private String scope;
public BeanDefinition() {
}
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;
}
}
容器中的ConcurrentHasMap<String, Object> singletoObjects 用来存储单例Bean对象
启动测试类可以看到 从容器中获取的三个userService的对象的地址值都不一样, 说明容器每次都会创建一个新的UserService对象
去掉UserService的@Scope注解重新执行可以看到 三次获取的bean对象都是同一个对象