在上一篇文章中,我们粗略的对Spring源码IOC这块过了一遍,那么这篇文章来简单写一个IOC的过程。由于理论性的东西都在上一篇解释过了,这篇咱就直接在代码中理解。
一、准备工作 1.1本文所用到的依赖包:
org.projectlombok
lombok
1.18.12
org.springframework
spring-webmvc
4.3.9.RELEASE
javax.servlet
servlet-api
2.5
1.2 web.xml文件
MVC Web Application
CcMvc
com.ccc.spring2.webmvc.XHDispatcherServlet
contextConfigLocation
classpath:application.properties
1
CcMvc
/*
1.3 建立项目结构
我们模仿spring的项目结构
1.3.1 创建BeanFactory接口在包beans下创建:
/**
* @author chenxh
* @date 2020/3/23 20:28
* @Description: 单例工厂顶层
* @modify:
* @modifyDate:
* @Description:
*/
public interface XHBeanFactory {
/**
* 根据beanName从IOC容器中获取一个实例Bean
* spring中使用单例有利于管理和维护
*
*/
Object getBean(String beanName) throws Exception;
Object getBean(Class beanClass) throws Exception;
}
1.3.2 创建BeanDefinition
在之前说道过在Spring容器启动的过程中,会将Bean解析成BeanDefinition结构。因此我们也创建一个,模仿spring的包名创建一个config包:
@Data
public class XHBeanDefinition {
private String beanClassName; //全包名
private boolean isLazyInit = false; //是否懒加载
private String factoryBeanName; //类名首字母小写
private boolean isSingleton = true; //是否单例
}
1.3.3 BeanDefinitionReader
这个方法主要作用是解析配置文件并将其封装成BeanDefinition对象供IOC操作,此方法在上几篇文章MVC中写过,这里就不在解释了。
package com.ccc.spring2.beans.support;
import com.ccc.spring2.beans.config.XHBeanDefinition;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* 加载配置文件的类
*/
public class XHBeanDefinitionReader {
private Properties config = new Properties();
//定义配置文件,暂时写死
private final String SCAN_PACKAGE = "scanPackage";
//存储所有的类名
private List registyBeanClasses = new ArrayList(); //全包名
public XHBeanDefinitionReader(String... locations) {
//通过URL定位找到对应的文件,转为文件流读取
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:", ""))) {
config.load(is);
} 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);
}
}
}
public Properties getConfig() {
return this.config;
}
//扫描配置信息并内部封装成XHBeanDefinition对象,便于IOC操作
public List loadBeanDefinitions(String... locations) {
List result = new ArrayList();
try {
System.out.println("registyBeanClasses:"+registyBeanClasses);
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
//把每一个配信息解析成一个BeanDefinition
private XHBeanDefinition doCreateBeanDefinition(String factoryBeanName,String beanClassName){
XHBeanDefinition beanDefinition = new XHBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
beanDefinition.setFactoryBeanName(factoryBeanName);
return beanDefinition;
}
/**
* 首字母小写
*/
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
chars[0] += (1在判断是否@Service、@Controller这些类注解->在判断是否@Autowried等注解。
对于IOC 整理就以上这些就介绍这么多,如果发现有补充的地方,我会继续添加,同时希望各位看过的伙伴们如果发现了问题能够及时批评指正,在此感谢。下一篇我会记录DI的过程。
上一篇:深入剖析Spring(四):IOC核心思想(源码分析篇)