文章目录
- 1、思路
- 2、实现
- 2.1、@ComponentScan 注解
- 2.2、@Component注解
- 2.3、@Scope注解
- 2.4、BeanDefinition
- 2.5、SanApplicationContext
- 2.6、SpringConfig
- 3、测试
- 3.1、在SpringConfig配置的被扫描的包路径下建立需要被Spring管理的类
- 3.2、Test
- 3.3、结果分析
- 4、总结
1、思路
- 1.1、Spring通过读取配置文件或者配置类获取需要Spring管理的Bean信息。
- 1.2、通过配置类获取需要被扫描的配置类。
- 1.3、通过ApplicationClassLoader加载所有被扫描到的类信息,获取需要被Spring控制的类信息,判断作用域或者其他Spring控制的属性,生成BeanDefinition集合信息,将BeanDefinition作为一个Spring上下文的一个Map类型属性存在上下文中。
- 1.4、处理Spring上下文中的BeanDefinition信息,对BeanDefinition集合遍历,根据各种Spring控制属性和类信息反射生成Bean。若是默认的单例模式,初始化的时候就生成Spring的Bean对象,将该对象放入单例池Singleton集合中,该单例池也作为Spring的上下文一个Map类型属性存在上下文中。
2、实现
2.1、@ComponentScan 注解
- Spring根据@ComponentScan扫描需要被Spring管理的包路径。
/**
* Created on 2021/4/17
*
* @author kaer
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() default "";
}
2.2、@Component注解
- Spring根据@Component来识别需要Spring管理的Bean。
/**
* Created on 2021/4/17
*
* @author kaer
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value();
}
2.3、@Scope注解
- Spring的Bean的作用域标识.。
/**
* Created on 2021/4/17
*
* @author kaer
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value();
}
2.4、BeanDefinition
- bean定义Model,有作用域和当前类信息等属性。
/**
* Created on 2021/4/17
*
* @author kaer
*/
public class BeanDefinition {
/**
* bean作用域
*/
private String scope;
/**
* 类对象
*/
private Class clazz;
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;
}
}
2.5、SanApplicationContext
- Spring的上下文Model与初始化步骤控制–扫描、BeanDefinition信息的生成、单例Bean创建与被单例池管理。Spring上下文主要有两个属性
单例池:private Map<String, Object> singleTonBeanMap = new ConcurrentHashMap<>();
Bean定义集合:private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* Created on 2021/4/17
*
* @author linjianshan
*/
public class SanApplicationContext {
private static final String SINGLETON = "singleton";
private static final String PROTOTYPE = "prototype";
private Class configClass;
/**
* 单例bean池
*/
private Map<String, Object> singleTonBeanMap = new ConcurrentHashMap<>();
/**
* bena定义map
*/
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public SanApplicationContext(Class configClass) {
this.configClass = configClass;
/**
* 扫描类,初始化 beanDefinitio
*/
scan(configClass);
/**
* 创建bean
*/
createBean(beanDefinitionMap);
}
private void createBean(Map<String, BeanDefinition> beanDefinitionMap) {
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
Class clazz = beanDefinition.getClazz();
try {
Object o = clazz.getDeclaredConstructor().newInstance();
singleTonBeanMap.put(beanName, o);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
private void scan(Class configClass) {
//解析配置类
//ComponentScan注解-->扫描路径-->扫描
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value();
System.out.println(path);
path = path.replace(".", "/");
ClassLoader classLoader = SanApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();
System.out.println(fileName);
if (fileName.endsWith(".class")) {
try {
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
className = className.replace("\\", ".");
System.out.println(className);
Class<?> clazz = classLoader.loadClass(className);
if (clazz.isAnnotationPresent(Component.class)) {
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//默认单例
beanDefinition.setScope(SINGLETON);
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
//随便抛错
throw new NullPointerException();
}
Object result = null;
String scope = beanDefinition.getScope();
if (SINGLETON.equals(scope)) {
result = singleTonBeanMap.get(beanName);
} else {
Class clazz = beanDefinition.getClazz();
try {
result = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
return result;
}
}
2.6、SpringConfig
- Spring配置类,也可以使用配置文件的方式。配置类上使用@ComponentScan注解配置Spring扫描的包路径。
/**
* Created on 2021/4/17
*
* @author kaer
*/
@ComponentScan("com.san.service")
public class SpringConfig {
}
3、测试
3.1、在SpringConfig配置的被扫描的包路径下建立需要被Spring管理的类
- MemberService,标记出需要被Spirng管理,不自定义其作用域。
/**
* Created on 2021/4/17
*
* @author kaer
*/
@Component("memberService")
public class MemberService {
}
- AssetService,标记出需要被Spring管理,自定义作用域为原型。
/**
* Created on 2021/4/18
*
* @author kaer
*/
@Component("assetService")
@Scope("prototype")
public class AssetService {
}
3.2、Test
/**
* Created on 2021/4/17
*
* @author kaer
*/
public class Test {
public static void main(String[] args) {
SanApplicationContext sanApplicationContext = new SanApplicationContext(SpringConfig.class);
System.out.println(sanApplicationContext.getBean("memberService"));
System.out.println(sanApplicationContext.getBean("memberService"));
System.out.println(sanApplicationContext.getBean("assetService"));
System.out.println(sanApplicationContext.getBean("assetService"));
}
}
3.3、结果分析
com.san.service.MemberService@1d44bcfa
com.san.service.MemberService@1d44bcfa
com.san.service.AssetService@266474c2
com.san.service.AssetService@6f94fa3e
结果如上,分析输出类的toString()方法可知:
- 通过BeanName获取MemberService多个Bean时,结果显示同一个实例Bean,符合MemberService默认的作用域定义。
- 通过BeanName获取AssetService多个Bean时,结果显示不是同一个Bean,符合AssetService自定义原型作用域。
4、总结
- 实际Spring的启动、Bean的实例化过程还是有许多细节和步骤的,本文将其简化了,但其思想是一致的。首选就是扫描Spring管理的类获取Beanfinition的信息,然后将单例Bean先实例化存入单例池中,其他作用域再根据其他操作进行控制。比如原型模式作用域就是每次客户端获取Bean时,就获取对应的BeanDefinition信息再来通过反射的方式来实例化新的Bean返回。