“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
根据spring框架底层原理,自己动手写一个,这里实现的只是一些简单的功能。
spring创建bean的流程:
- 1.加载配置类。
- 2.根据配置类配置的扫描路径,扫描带有@Component 注解的类。
- 3.将扫描到的类,定义为beanDefinition(封装有类的信息:属性,类型,是否是单例等等。)
- 4.创建bean.
1.参考spring代码仿写
毕竟是模拟,我们先看spring是怎么写的。
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
CompanyService companyService = (CompanyService)context.getBean("companyService");
companyService.test();
}
}
我们可以看到:
- 1.创建了一个AnnotationConfigApplicationContext(conetxt) 对象,
- 2.用的是有参构造方法。
- 3.使用context调用getBean()方法,
- 4.传了一个beanName,获取一个bean. 那么我们就可以仿着来
- 1.创建一个类似AnnotationConfigApplicationContext的类。
- 2.这个类里有:接收配置类的属性,一个有参方法,
- 3.还需要一个getBean()方法,
public class StartApplicationContext{
// 接收配置类的属性
private Class configClass;
public StarsApplicationContext(Class configClass) {
this.configClass = configClass;
}
//getBean
public Object getBean(String name){
return null;
}
}
public class Test {
public static void main(String[] args) {
StartApplicationContext context = new StartApplicationContext(AppConfig.class);
CompanyService companyService = (CompanyService) context.getBean("companyService");
}
}
在spring框架中,在写配置类的时候要加一个@ComponentScan注解配置扫描路径,还需要注解@Component来判断一个类是否要spring来加载为bean。 创建一个@ComponentScan注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
2.扫描
梳理下spring加载配置类的大致流程:
- 扫描配置类
- 1.判断配置类上是否加了@ComponentScan 注解
- 2.获取配置类上配置的扫描路径,就是注解的value值。
- 3.获取扫描路径下的类文件。
- 4.加载类,判断类上面是否有@Component注解,等等。
- 创建单例bean。
public class StartApplicationContext {
private Class configClass;
public StartApplicationContext(Class configClass) {
this.configClass = configClass;
// 扫描 到 创建bean
// 扫描的过程
// 判断传进来得配置类,是否加了 @ComponentScan
// 获取注解的值,定义的扫描包路径
// 获取扫描包路径下的类
// 判断类上面是否有 Component 注解。
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String pathValue = componentScanAnnotation.value();
String replace = pathValue.replace(".", "/");
System.out.println(replace);
ClassLoader classLoader = StartApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(replace);
File file = new File(resource.getFile());
boolean a = false;
if(file.isDirectory()){
for (File f : file.listFiles()){
String path = f.getAbsolutePath();
System.out.println(path);
path = path.substring(path.indexOf("com"), path.indexOf(".class"));
path = path.replace("/",".");
System.out.println(path);
try {
Class<?> loadClass = classLoader.loadClass(path);
if(loadClass.isAnnotationPresent(Component.class)){
if(loadClass.isAnnotationPresent(Scope.class)){
}else {
//单例
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String name) {
return null;
}
上面这个流程(如上面的示例代码) 是在 public StartApplicationContext(Class configClass) 这个方法中,那么还有一个getBean(String name) 方法,如果只是按照上面这个流程,那么getBean(String name )的时候还要再重复去判断这些类的信息。
所以这里会引入bean定义的概念:就是在扫描的时候获取的这些类的信息,封装成一个对象,存到缓存中。
类的信息包括:是否是单里,是否是懒加载等等。
public class BeanDefinition {
private Class type;
private String scope;
private boolean isLazy;
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public boolean isLazy() {
return isLazy;
}
public void setLazy(boolean lazy) {
isLazy = lazy;
}
}
public class StartApplicationContext {
private Class configClass;
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
public StartApplicationContext(Class configClass) {
this.configClass = configClass;
scan(configClass);
}
private void scan(Class configClass) {
// 扫描 到 创建bean
// 扫描的过程
// 判断传进来得配置类,是否加了 @ComponentScan
// 获取注解的值,定义的扫描包路径
// 获取扫描包路径下的类
// 判断类上面是否有 Component 注解。
// 封装bean定义存到缓存中
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String pathValue = componentScanAnnotation.value();
String replace = pathValue.replace(".", "/");
System.out.println(replace);
ClassLoader classLoader = StartApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(replace);
File file = new File(resource.getFile());
boolean a = false;
if (file.isDirectory()) {
for (File f : file.listFiles()) {
String path = f.getAbsolutePath();
System.out.println(path);
path = path.substring(path.indexOf("com"), path.indexOf(".class"));
path = path.replace("/", ".");
System.out.println(path);
try {
Class<?> loadClass = classLoader.loadClass(path);
if (loadClass.isAnnotationPresent(Component.class)) {
Component annotation = loadClass.getAnnotation(Component.class);
String beanName = annotation.value();
//如果没有定义bean的名字,默认生成一个bean的名字。
String beanName = annotation.value();
if("".equals(beanName)){
beanName = Introspector.decapitalize(loadClass.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(loadClass);
if (loadClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = loadClass.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String name) {
return null;
}
}
这个是引入bean定义后修改的。到现在,我已经通过扫描拿到了相关bean的信息。
3.创建bean
bean 的相关信息已经缓存起来,接下来就该创建bean了。
- 从缓存中拿到bean信息,
- 判断这个bean是否是单例,
- 如果是单例bean则创建bean,然后存入单例缓存中。
- 如果不是单例则每次获取单例的时候都要创建。
如下示例;
package com.star.spring;
import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class StartApplicationContext {
private Class configClass;
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
private Map<String, Object> singletonObjectMap = new HashMap<>();
public StartApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描
scan(configClass);
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
String key = entry.getKey();
BeanDefinition value = entry.getValue();
if ("singleton".equals(value.getScope())) {
Object bean = createBean(key, value);
singletonObjectMap.put(key, bean);
}
}
}
public Object createBean(String beanName, BeanDefinition beanDefinition) {
Class aClass = beanDefinition.getType();
Object object = null;
try {
object = aClass.getConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return object;
}
public Object getBean(String name) {
if (!beanDefinitionMap.containsKey(name)) {
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
String scope = beanDefinition.getScope();
if ("singleton".equals(scope)) {
Object singletonObject = singletonObjectMap.get(name);
return singletonObject;
} else {
Object bean = createBean(name, beanDefinition);
return bean;
}
}
private void scan(Class configClass) {
// 扫描 到 创建bean
// 扫描的过程
// 判断传进来得配置类,是否加了 @ComponentScan
// 获取注解的值,定义的扫描包路径
// 获取扫描包路径下的类
// 判断类上面是否有 Component 注解。
// 封装bean定义存到缓存中
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String pathValue = componentScanAnnotation.value();
String replace = pathValue.replace(".", "/");
System.out.println(replace);
ClassLoader classLoader = StartApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(replace);
File file = new File(resource.getFile());
boolean a = false;
if (file.isDirectory()) {
for (File f : file.listFiles()) {
String path = f.getAbsolutePath();
System.out.println(path);
path = path.substring(path.indexOf("com"), path.indexOf(".class"));
path = path.replace("/", ".");
System.out.println(path);
try {
Class<?> loadClass = classLoader.loadClass(path);
if (loadClass.isAnnotationPresent(Component.class)) {
Component annotation = loadClass.getAnnotation(Component.class);
String beanName = annotation.value();
if("".equals(beanName)){
beanName = Introspector.decapitalize(loadClass.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(loadClass);
if (loadClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = loadClass.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
}
4.属性赋值
spring框架中的属性值是很复杂的,我们这里就简单实现好了:使用名字匹配。
属性赋值很显然,我们要在对象创建完之后,给属性赋值。
步骤:
- 获取对象中的所有属性循环
- 判断属性是否有@Autowired 注解。
- 根据属性字段的名字给属性赋值。
public class StartApplicationContext {
private Class configClass;
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
private Map<String, Object> singletonObjectMap = new HashMap<>();
public StartApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描
scan(configClass);
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
String key = entry.getKey();
BeanDefinition value = entry.getValue();
if ("singleton".equals(value.getScope())) {
Object bean = createBean(key, value);
singletonObjectMap.put(key, bean);
}
}
}
public Object createBean(String beanName, BeanDefinition beanDefinition) {
Class aClass = beanDefinition.getType();
Object object = null;
try {
object = aClass.getConstructor().newInstance();
//属性赋值
for (Field field : aClass.getDeclaredFields()){
if(field.isAnnotationPresent(Autowired.class)){
field.setAccessible(true);
field.set(object,getBean(field.getName()));
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return object;
}
public Object getBean(String name) {
if (!beanDefinitionMap.containsKey(name)) {
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
String scope = beanDefinition.getScope();
if ("singleton".equals(scope)) {
Object singletonObject = singletonObjectMap.get(name);
//属性赋值的时候,获取对象可能会为空
if(singletonObject == null){
singletonObject = createBean(name, beanDefinition);
singletonObjectMap.put(name,singletonObject);
}
return singletonObject;
} else {
Object bean = createBean(name, beanDefinition);
return bean;
}
}
private void scan(Class configClass) {
// 扫描 到 创建bean
// 扫描的过程
// 判断传进来得配置类,是否加了 @ComponentScan
// 获取注解的值,定义的扫描包路径
// 获取扫描包路径下的类
// 判断类上面是否有 Component 注解。
// 封装bean定义存到缓存中
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String pathValue = componentScanAnnotation.value();
String replace = pathValue.replace(".", "/");
System.out.println(replace);
ClassLoader classLoader = StartApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(replace);
File file = new File(resource.getFile());
boolean a = false;
if (file.isDirectory()) {
for (File f : file.listFiles()) {
String path = f.getAbsolutePath();
System.out.println(path);
path = path.substring(path.indexOf("com"), path.indexOf(".class"));
path = path.replace("/", ".");
System.out.println(path);
try {
Class<?> loadClass = classLoader.loadClass(path);
if (loadClass.isAnnotationPresent(Component.class)) {
Component annotation = loadClass.getAnnotation(Component.class);
String beanName = annotation.value();
if("".equals(beanName)){
beanName = Introspector.decapitalize(loadClass.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(loadClass);
if (loadClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = loadClass.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
}
以上就是这次模拟spring框架创建bean 给属性赋值。 里面有需要注意的地方:
- 判断是否实现了哪个注解,isAnnotationPresent();
- 类加载器:ClassLoader classLoader = StartApplicationContext.class.getClassLoader();
- 反射创建对象: aClass.getConstructor().newInstance();
- 获取一个对象的所有属性。aClass.getDeclaredFields()
- 还有就是,创建bean的整个流程