参考:《Spring 5核心原理》
我们这里实现Spring中DI,自动注入的功能,主要涉及class遍历,加载,解析,然后存入三个容器中,实例化几个过程。核心的内容就是:反射。
整体结构:
一,注解设计
1.@XService
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XService {
String value() default "";
}
2.@XAutowired
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XAutowired {
String value() default "";
}
3.@XController
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XController {
String value() default "";
}
二,顶层接口设计
1.XFactoryBean
public interface XFactoryBean {
}
2.XBeanFactory,它提供getBean()方法
public interface XBeanFactory {
Object getBean(String beanName) throws Exception;
public Object getBean(Class<?> beanClass) throws Exception;
}
3.XBeanDefinition( beanClass的封装类)这里使用了lombok插件
@Data
public class XBeanDefinition {
private String beanClassName; //原生bean的全类名
private boolean lazyInit=false;//标记是否延时加载
private String factoryBeanName; //保存beanName,在IOC容器中 存储的key
}
4.XBeanWrapper (bean实例的封装)
@Data
public class XBeanWrapper {
private Object wrappedInstance;
private Class<?> wrappedClass;
public XBeanWrapper(Object instance){
this.wrappedInstance=instance;
}
}
5.XAbstractApplicationContext
容器的顶层抽象
public abstract class XAbstractApplicationContext {
public void refresh() throws Exception{}
}
6.XDefaultListableBeanFactory
public class XDefaultListableBeanFactory extends XAbstractApplicationContext {
//存储注册消息的beanDefinition
protected final Map<String,XBeanDefinition> beanDefinitionMap=new ConcurrentHashMap<String, XBeanDefinition>();
}
三,主要内容
1.用户入门类XApplicationContext
public class XApplicationContext extends XDefaultListableBeanFactory implements XBeanFactory {
private String[] configLocations;
private XBeanDefinitionReader reader;
//单例的IOC容器缓冲
private Map<String,Object> factoryBeanObjectCache =new ConcurrentHashMap<String, Object>();
//通用的 IOC容器
private Map<String, XBeanWrapper> factoryBeanInstanceCache =new ConcurrentHashMap<String, XBeanWrapper>(); //防止多线程
public XApplicationContext(String... configLocations){
this.configLocations=configLocations;
try{
refresh();
}catch (Exception e){
e.printStackTrace();
}
}
public void refresh() throws Exception{
//1.定位 配置文件 把扫描路径下 所有class全限名,全部准备好了
reader=new XBeanDefinitionReader(this.configLocations);
//2.加载配置文件 开始解析
List<XBeanDefinition> beanDefinitions=reader.loadBeanDefinitions();
//3.注册,把配置信息放到容器里面
doRegisterBeanDefinition(beanDefinitions);
//4.把不是延时加载的类提前初始化
doAutowired();
}
//只处理非延时加载的情况
private void doAutowired(){
for(Map.Entry<String,XBeanDefinition> beanDefinitionEntry:super.beanDefinitionMap.entrySet()){
String beanName=beanDefinitionEntry.getKey();
if(!beanDefinitionEntry.getValue().isLazyInit()){
try{
getBean(beanName);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
//注册? 容器初始化
private void doRegisterBeanDefinition(List<XBeanDefinition> beanDefinitions) throws Exception{
for(XBeanDefinition beanDefinition:beanDefinitions){
if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
throw new Exception("the “"+beanDefinition.getFactoryBeanName()+" ” is existe!!");
}
super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
}
//容器初始化完毕。
}
//通过class对象,获得实例
public Object getBean(Class<?> beanClass) throws Exception{
return getBean(beanClass.getName());
}
//依赖注入,从这里开始,读取beanDefinition中的信息,
//然后通过反射机制创建一个实例并返回
//spring做法是,不会把最原始的对象放出去,会用一个beanWrapper来进行一次包装
//装饰器模式:
//1.保留原来的oop关系
//2.需要对他进行扩展,增强(为了以后的aop打基础)
public Object getBean(String beanName) throws Exception{
XBeanDefinition beanDefinition=super.beanDefinitionMap.get(beanName);
try{
XBeanPostProcessor beanPostProcessor=new XBeanPostProcessor();
Object instance=instantiateBean(beanDefinition);
if(instance==null) {
System.out.println(beanName+" instance fail!");
return null;
}
//在实例初始化以前,调用一次
beanPostProcessor.postProcessAfterInitialization(instance,beanName);
XBeanWrapper beanWrapper=new XBeanWrapper(instance);
this.factoryBeanInstanceCache.put(beanName,beanWrapper);
//在实例化,初始化以后调用一次
beanPostProcessor.postProcessAfterInitialization(instance,beanName);
populateBean(beanName,instance);
return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
private void populateBean(String beanName,Object instance){
Class clazz=instance.getClass(); //获得class对象进行分析。
//一定要实现这两个注解 才可能 有自动注入的 字段
if(!(clazz.isAnnotationPresent(XController.class))||clazz.isAnnotationPresent(XService.class))
return;
Field []fields=clazz.getDeclaredFields(); //获得所有的字段
for(Field field:fields){ //遍历 所有成员变量(字段)
if(!field.isAnnotationPresent(XAutowired.class))
continue;
XAutowired autowired=field.getAnnotation(XAutowired.class); //获得这个标签干嘛?
String autowiredBeanName =autowired.value().trim(); //获得标签的value值(一般value是空的)
if("".equalsIgnoreCase(autowiredBeanName)){ //如果这个name为空
autowiredBeanName=field.getType().getName(); //把它的类型 全限名字 给他。(这个是字段的名字,beanName是自己类的名字)不能混用
}
field.setAccessible(true);
try{ //里面放的是 类变量的名字 ,, get要获得的是 类的全限名字,而且还是接口的。
if(this.factoryBeanInstanceCache.get(autowiredBeanName)==null){
getBean(autowiredBeanName);
}
field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance()); //设置实例,从缓冲中获取。
}catch (Exception e){
e.printStackTrace();
}
}
}
//传一个beanDefinition,就返回一个实例bean
private Object instantiateBean(XBeanDefinition beanDefinition){
Object instance=null;
String className=beanDefinition.getBeanClassName();
try{
//因为根据class才能确定一个类是否有实例
if(this.factoryBeanObjectCache.containsKey(className)){
instance=this.factoryBeanObjectCache.get(className);//直接获取
}else{
Class<?> clazz=Class.forName(className);
instance=clazz.newInstance(); //返回一个实例化 bean
this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance); //实例化的bean 放入缓冲中
}
return instance;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public String[] getBeanDefinitionNames(){
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
public int getBeanDefinitionCount(){
return this.beanDefinitionMap.size();
}
public Properties getConfig(){
return this.reader.getConfig();
}
}
这个类十分关键,我们的程序就是从这里启动的,它的构造方法开始,传入一个application.properties的文件路径,去读取,里面存放的是包扫描的路径,如下所示:
scanPackage = com.myproject.text
它的启动走的是refresh()方法,和原版spring同步,进入这里,分成了四个方法,第一个就是通过路径,读取该配置文件(通过Reader初始化去读出application.properties)
Reader 使用java自带的Properties去解析.properties文件,直接获取我们的scanPackage值。然后使用doScaner()扫描该路径,将路径下的所有class文件的全限名,保存起来(这里的class肯定是,编译之后的classes文件下的,所以这里的路径导入,很有讲究,涉及到this.getClass().getResource(),还有this.getClass().getClassLoader(),这两者下传入路径参数不同
参考:https://www.cnblogs.com/ncy1/articles/8811238.html
https://blog.csdn.net/qq_37859539/article/details/82822107
对这里目录,必须采用递归的方式,把所有内容放到成员变量registryBeanClasses中。其实我们这里的扫描,就是把所有的bean都搞出来了。
//对配置文件 进行查找,读取,解析
public class XBeanDefinitionReader {
private List<String> registryBeanClasses=new ArrayList<String>();
private Properties config=new Properties();
private final String SCAN_PACKAGE="scanPackage";
public XBeanDefinitionReader(String... locations){
//通过url定位找到 其所对应的文件,然后转换为文件流
//找到配置文件 application.properties
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();
}
}
}
//从配置文件 application.properties中 读取数据!---》找到需要扫描包 路径!
System.out.println("package scan : "+config.getProperty(SCAN_PACKAGE));
doScanner(config.getProperty(SCAN_PACKAGE));
}
//遍历指定的 扫描路径,把扫描到的所以class 权限名,加入注册列表中
private void doScanner(String scanPackage){
//将包的.X.X路径 转换为 文件路径,实际上是把. 替换为/
// getXX(),接收相对路径,或者绝对路径,将一个文件,封装成为url。
// 相对的是class文件的位置 ,绝对是”/“开头的项目路径,路径位置是 编译后的target文件中的 路径。!
System.out.println("/"+scanPackage.replaceAll("\\.","/"));
URL url=this.getClass().getResource("/"+scanPackage.replaceAll("\\.","/"));
if(url==null){
System.out.println("url is null");
return;
}
//文件遍历,找到class文件。
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",""));
registryBeanClasses.add(className); //放入列表中
}
}
}
public Properties getConfig(){
return this.config;
}
public List<XBeanDefinition> loadBeanDefinitions(){
List<XBeanDefinition> result=new ArrayList<XBeanDefinition>();
try{
for(String className: registryBeanClasses) {
Class<?> beanClass = Class.forName(className);
if (beanClass.isInterface())
continue;
result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
Class<?>[] interfaces = beanClass.getInterfaces();
for (Class<?> i : interfaces) {
result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
//把每一个配置信息解析成为一个beanDefinition
private XBeanDefinition doCreateBeanDefinition(String factoryBeanName,String beanClassName){
XBeanDefinition beanDefinition=new XBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
beanDefinition.setFactoryBeanName(factoryBeanName);
return beanDefinition;
}
//把类名 首字母改为小写
private String toLowerFirstCase(String simpleName){
char []chars=simpleName.toCharArray();
chars[0]+=32;
return String.valueOf(chars);
}
}
初始化好reader之后,我们回到refresh()方法,到第二步,loadBeanDefinitionis(),就是通过类的全限名,获得Class对象(这里反射就进场了),然后还要解析这个类的接口,把接口的全限名也搞出来,同时把小名也查出来,封装到beanDefintion中。全部放到ApplicationContext的refresh()方法中的局部变量中,这里还是一个ArrayList()但是它有了所有的需要的class的beanDefinitioni对象。
(注意,这里遍历的时候,是把没有用的接口,排除了的,接口的注入,一定是有类实现了的!,所以最后实例化的时候,肯定要排除接口!!!!)
第三步:将List<beanDifintion> 转为HasMap<String,BeanDefinition>,放到DefaultApplicationContext中。
private void doRegisterBeanDefinition(List<XBeanDefinition> beanDefinitions) throws Exception{
for(XBeanDefinition beanDefinition:beanDefinitions){
if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
throw new Exception("the “"+beanDefinition.getFactoryBeanName()+" ” is existe!!");
}
super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
}
//容器初始化完毕。
}
第四步:实例化bean(内容顶上面三步)
调用doAutowired()方法,遍历父类的HashMap,然后获得map.key也就是beanName,直接采用getBean(beanName)实例化,为什么使用getBean,进行实例化呢?因为我用户可以直接调用getBean,获取bean,bean又分为单例,和原型,默认是单例的,所以如果遇到原型,在getBean()的时候,还是需要进行实例化。
重点就是getBean()方法中!
这里在我们的ApplicationContext中存在两个Cache,一个是Object,另外一个是Instance(它是wrapper封装的)
我们遍历一个beanName,就对它进行实例化,使用beanDefinition+反射,获得实例化对象,然后存入Object中,然后进行一些前后处理,封装,放如wrapper中,再放入instanceCache中,最后对其内部成员进行实例化,如果有注解@Service和@Controller那么内部是有可能需要自动注入的,他们的注入,是同构递归getBean()实现的!
最后实例就存在与InstanceCache中了!
最后几个 走形式的类
public class XBeanPostProcessor {
//为在bean的初始化之前 提供回调入口
public Object postProcessBeforeInitialization(Object bean,String beanName) throws Exception{
return bean;
}
//为在bean 初始化之后提供回调入口
public Object postProcessAfterInitialization(Object bean,String beanName) throws Exception{
return bean;
}
}
@Data
public class XBeanWrapper {
private Object wrappedInstance;
private Class<?> wrappedClass;
public XBeanWrapper(Object instance){
this.wrappedInstance=instance;
}
}