手写高仿真Spring框架
流行词:顶层设计
参考代码:https://gitee.com/li-lixiang/lean-spring-2.0.git
控制反转IOC
初始化流程GPApplicationContext类,按照模板模式,实现IOC初始化。
//1、定位,定位配置文件
reader = new GPBeanDefinitionReader(this.configLoactions);
//2、加载配置文件,扫描相关的类,把它们封装成
BeanDefinition List<GPBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//3、注册,把配置信息放到容器里面(伪IOC容器)
doRegisterBeanDefinition(beanDefinitions);
//4、把不是延时加载的类,有提前初始化 doAutowrited();
以下代码主要实现以上1、2步骤。
解析配置,将配置中所有的类加载到容器中
public GPBeanDefinitionReader(String... locations){
//通过URL定位找到其所对应的文件,然后转换为文件流
InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:",""));
try {
config.load(is);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
doScanner(config.getProperty(SCAN_PACKAGE));
}
private void doScanner(String scanPackage) {
//转换为文件路径,实际上就是把.替换为/就OK了
URL url = this.getClass().getResource("/" + scanPackage.replaceAll("\\.","/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if(file.isDirectory()){
doScanner(scanPackage + "." + file.getName());
}else{
if(!file.getName().endsWith(".class")){ continue;}
String className = (scanPackage + "." + file.getName().replace(".class",""));
registyBeanClasses.add(className);
}
}
}
并将容器中所有扫描到的结果类转换成BeanDefinition
public List<GPBeanDefinition> loadBeanDefinitions(){
List<GPBeanDefinition> result = new ArrayList<GPBeanDefinition>();
try {
for (String className : registyBeanClasses) {
Class<?> beanClass = Class.forName(className);
//如果是一个接口,是不能实例化的
//用它实现类来实例化
if(beanClass.isInterface()) { continue; }
//beanName有三种情况:
//1、默认是类名首字母小写
//2、自定义名字
//3、接口注入
result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()),beanClass.getName()));
// result.add(doCreateBeanDefinition(beanClass.getName(),beanClass.getName()));
Class<?> [] interfaces = beanClass.getInterfaces();
for (Class<?> i : interfaces) {
//如果是多个实现类,只能覆盖
//为什么?因为Spring没那么智能,就是这么傻
//这个时候,可以自定义名字
result.add(doCreateBeanDefinition(i.getName(),beanClass.getName()));
}
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
//把每一个配信息解析成一个BeanDefinition
private GPBeanDefinition doCreateBeanDefinition(String factoryBeanName,String beanClassName){
GPBeanDefinition beanDefinition = new GPBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
beanDefinition.setFactoryBeanName(factoryBeanName);
return beanDefinition;
}
//如果类名本身是小写字母,确实会出问题
//但是我要说明的是:这个方法是我自己用,private的
//传值也是自己传,类也都遵循了驼峰命名法
//默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况
//为了简化程序逻辑,就不做其他判断了,大家了解就OK
//其实用写注释的时间都能够把逻辑写完了
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
//之所以加,是因为大小写字母的ASCII码相差32,
// 而且大写字母的ASCII码要小于小写字母的ASCII码
//在Java中,对char做算学运算,实际上就是对ASCII码做算学运算
chars[0] += 32;
return String.valueOf(chars);
}
在以上第3步骤中,实现容器初始化
private void doRegisterBeanDefinition(List<GPBeanDefinition> beanDefinitions) throws Exception {
for (GPBeanDefinition beanDefinition: beanDefinitions) {
if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
}
super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
}
//到这里为止,容器初始化完毕
}
最后第4步,处理非延时加载的类,非延时加载的进行实例化,延时的不做处理。
private void doAutowrited() {
for (Map.Entry<String, GPBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
String beanName = beanDefinitionEntry.getKey();
if(!beanDefinitionEntry.getValue().isLazyInit()) {
try {
getBean(beanName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}