手写Spring
复盘Spring手写过程
流程图

ioc容器代码
package Myspring;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
public class lxyApplicationContext {
private Class config;
/*
四、判断getBean获取到的是single还是prototype
ConcurrentHashMap 二级哈希表
优点: 线程安全执行效率高<非阻塞>
用处: 多线程,高并发时使用较多
* */
private ConcurrentHashMap<String ,Object> Singletonpoolmap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public lxyApplicationContext(Class config) {
this.config = config;
Scan(config);
/*
* 目的: 在lxyApplicationContext创建的时候将把单例Bean对象创建好
* 在getBean的时候直接调用
* 1. 先获取beanDefinitionMap中所有的key(BeanName)
* 知识点:
* 1.Map.Entry是Map的一个接口,属于Map遍历的其中一种方式
* */
for(Map.Entry<String,BeanDefinition> entry : beanDefinitionMap.entrySet() ){
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
if (beanName.equals("singleton")) {
Object creatBean = creatBean(beanDefinition);
Singletonpoolmap.put(beanName,creatBean);
}
}
}
/*
* 通过Compontentclass的定义类(BeanDefinition)创建一个单例对象*/
private Object creatBean(BeanDefinition beanDefinition) {
/*在beanDefinition中有Class属性,使用反射.构造方法创建实例,并返回实例对象
在这需要获取class
Type FiledName ;通过这两个关键代表去找到Value(对象值)
fields.set(Object lxyService, Object Singlelenpoor) 通过反射给属性赋值(依赖注入)
*/
try {
Object instance = beanDefinition.getBclass().getDeclaredConstructor().newInstance();
for (Field fields : beanDefinition.getBclass().getDeclaredFields()) {
if (fields.isAnnotationPresent(Autowired.class)) {
// Object bean = Singletonpoolmap.get(instance); //这样不太好
Object bean = getBean(fields.getName());
fields.setAccessible(true);//因为是私有属性,所以需要权限
fields.set(instance,bean);
}
}
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 config) {
/*
一、判断配置类中是否存在ComponentScan,并取值
- Class getDeclaredAnnotation -->返回Appconfig中额所有注解(更加精准)
原因: 解析类,并不是真的把Appconfig的类声明进行解析,而是更加关注于注解
- 获取ComponentScan中自己写进去的value值(包名)
*/
ComponentScan classCompontScan = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
String value = classCompontScan.value();
String valuesymbol = value.replace(".", "\\");
/*
二、通过提供包名的ioc容器类.class,获取包下所有类(.class的file类型)
* 通过类加载器获取到Service下面的所有类(App<属于自定义类型>)
判断这个文件是否是文件夹,是则获取全限包名
* */
ClassLoader classLoader = lxyApplicationContext.class.getClassLoader();
File file =new File(classLoader.getResource(valuesymbol).getFile()) ;
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File fs : files) {
String absFilepath = fs.getAbsolutePath();
/*
abs: C:\Spring_review\target\classes\Service\Lxyservice.class
* 格式处理: 反射获取类信息(Service\\lxyservice)-->(Service.lxyservice)<反射获取类的写法和格式!!>
- String substring(int x, int y); 分割字符串
- int indexof(String x); 截取字符串下标
- String replace("$","@") 符号替换
* */
if(absFilepath.endsWith(".class")){
String className = absFilepath.substring(absFilepath.indexOf("Service"), absFilepath.indexOf(".class"));
String classNamesymbol = className.replace("\\", ".");
/*
三、 使用同一个classLoader加载classNamesymbol得到类对象
* - classLoader.loadClass 得到的class只进行加载,并没有进行连接和初始化??
* - isAnnotationPresent Component类型的注解是否在lxyservice类上
- getDeclaredAnnotation
该方法获取直接修饰该class对象对应类的指定类型的Annotation,如果不存在,则返回null
* */
try {
Class<?> aClass = classLoader.loadClass(classNamesymbol);
/*
* 判断当前类对象是否存在此注解,存在意味着当前class是一个Bean
* Bean类型 1.单例 2.原型*/
//这个证明了它存在为true,因为执行了下面的语句
if (aClass.isAnnotationPresent(Component.class)) {
//为什么获取不到lxyservice类上的@Component注解,明明写了??
Component component = aClass.getDeclaredAnnotation(Component.class);
String beanName = component.value();
/*包空指针的原因: 空的东西调用了方法*/
BeanDefinition beanDefinition = new BeanDefinition();
/*
错误1: 未生成aclass
* 生成BeanDefinition对象,<String beanName, Class aclass(Lxyservice)>
*/
beanDefinition.setBclass(aClass);
if(aClass.isAnnotationPresent(Scope.class)){
Scope scopeComponent = (Scope) aClass.getDeclaredAnnotation(Scope.class);
beanDefinition.setScopetype(scopeComponent.value());
beanDefinition.setScopetype("prototype");
}
else {
beanDefinition.setScopetype("singleton");
}
/*
* 当我扫描出一个带有component注解的class
* 将这个类的BeanName和类的定义放进Bean定义池中
* */
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
//问题1: 根据(Componentname)字符串找到对应的class,生成对应的Bean对象?
public Object getBean(String beanName){
if(beanDefinitionMap.containsKey(beanName)){
/*
* 从BeanDefinition池中通过BeanName获取BeanName对象
* 判断此对象的作用域范围是singleton还是prototype
* 1. 单例: 就直接从单例池中取出来
* 2. 原型: 每次getBean的时候都应该创建BeanClass对象???
* */
BeanDefinition beanDefinitionobj = beanDefinitionMap.get(beanName);
if(beanDefinitionobj.getScopetype().equals("singleton")){
Object singletonBean = Singletonpoolmap.get(beanName);
return singletonBean;//返回的是BeanDefinition对象
}else{
Object prototypeBean = creatBean(beanDefinitionobj);
return prototypeBean;
}
} else{
/*
* java手动抛出异常
* - throw new Exception("worry message");
* */
throw new NullPointerException("这个BeanName不存在@Component注解||BeanName写错");
}
// return null;
}
}
抛出来的异常经验(VS)新的知识
Spring手写的框架!
- 1.API
1.1 config(配置类.class)
1.2 getBean(Compontent(value()))
- 2.Spring的注解
2.1 Component(BeanName)
2.2 ComponentScan(需要扫描的包名)
- 3.解析配置类(注解)
3.1 判断是否是scan,是的话取value,通过类加载器获取所有的class,判断是否是compontent注解,
- 4.main 测试类
问题01:
解决01:
isAnnotationPresent
判断注释Scope是否在aClass(类对象上)
getDeclaredAnnotation
返回直接存在此元素上的所有注释
目的:
得到返回值value();
Class<?> aClass = classLoader.loadClass(classNamesymbol);
if(aClass.isAnnotationPresent(Scope.class)){
Scope scopeComponent = (Scope) config.getDeclaredAnnotation(Scope.class);
}
问题02得不到lxyservice类上的Component注解??
通过配置类获取扫描路径
ComponentScan classCompontScan = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
通过类对象获取对象上的Component注解
Component component = (Component) aClass.getDeclaredAnnotation(Component.class);
问题03lxyservice得值为null
原因lxyService没有开启单例模式
Lxyservice lxyservice = (Lxyservice) lxyApplicationContext.getBean("lxyservice");
问题03.1可是我写的Autowired是单例模式的所以怎么办??
解决直接@scope(value = "singleton")就好了!!!
注意:
依赖注入的abc属性值 = null的原因是它没有设置为单例模式
视频链接地址
最后总结
还会有后文,这里只是单纯的扫描+解析
浙公网安备 33010602011771号