2020-11-27

IOC容器模拟实现

运用反射机制和自定义注解模拟实现IOC容器,使其具有自动加载、自动装配和根据全限定类名获取Bean的功能。

一. 实现原理

1-1 IOC容器的本质

IOC容器可理解为是一个map,其中的一个entry可理解为一个component(组件),entry中的key为beanId(全限定类名),entry中的value为bean(类对应的对象);

 

 

 

 

 

 

 

 

 

具体的体现为:

1-2 自动加载

  • 自动加载是指IOC容器会自动加载被@Component等(以下用@Component为例)注解标记的类,使其成为IOC容器中的一个组件;

  • @Component注解加在一个类前,表示此类被IOC容器管理,成为IOC容器中的一个组件;

  • 在自动加载时,容器会自动扫描给定包路径所对应的包及其子包下的所有类,判断类是否是接口,如果不是接口就再判断是否被@Component标记,如果被标记了就将其添加到IOC容器中,key为该类的全限定类名,value为该类的对象。具体流程如图:

1-3 自动装配

  • 自动装配是指IOC容器会自动装配容器中各个bean中被@Autowired注解标记属性的属性值;

  • @Autowired注解加在类中的一个属性前,表示此属性的值需要被自动装配;

  • 待自动加载完成后,容器会根据keySet中的全限定类名遍历容器中各个类的各个属性,判断属性是否被@Autowired注解标记,如果被标记了,就会根据属性的类型的全限定类名(beanId)从容器中找到对应的bean,然后将找到的bean 的引用赋值给对应属性(模拟bean的scope为singleton)。具体的流程如图:

二. 具体实现

2-1 模拟情形

IOC容器自动加载com.hutao.springioc包及其子包下的所有类,并自动完成各类对应bean的属性装配。

2-2 目录结构

  • Autowired, Component为自定义注解;

  • Cat, Dog, User为实体类;

  • IocContainer为IOC容器;

  • Demo为测试。

2-3 实现

Autowired.java

package com.hutao.springioc.annotation;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public @interface Autowired {}

 

Component.java

package com.hutao.springioc.annotation;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public @interface Component {}

 

Cat.java

package com.hutao.springioc.model;import com.hutao.springioc.annotation.Component;@Componentpublic class Cat { public void mew(){  System.out.println("Meow Meow Meow..."); }}

 

Dog.java

package com.hutao.springioc.model;import com.hutao.springioc.annotation.Component;@Componentpublic class Dog { public void bark(){  System.out.println("Wow wow wow..."); }}

 

User.java

package com.hutao.springioc.model;import com.hutao.springioc.annotation.Autowired;import com.hutao.springioc.annotation.Component;@Componentpublic class User { @Autowired private Dog dog; @Autowired private Cat cat; public void chat(){  System.out.println("This is my dog and cat.");  dog.bark();  cat.mew(); }}

 

IocContainer.java

package com.hutao.springioc;import com.hutao.springioc.annotation.Autowired;import com.hutao.springioc.annotation.Component;import java.io.File;import java.io.IOException;import java.lang.reflect.Field;import java.net.URL;import java.util.*;/** * 模拟Ioc容器,实现自动加载组件、自动装配、根据类的全限定名获取Bean * * @author Hutao * @createDate 2020/11/14 */public class IocContainer { //Ioc容器 存储的键值对为 <类的完全限定名称,该类的一个对象> private Map<String, Object> container = new HashMap<String, Object>(); //Ioc容器可扫描到该包及其子包下的所有类 private String packageName; public IocContainer(String packageName) {  this.packageName = packageName;  try{   //添加组件到容器   loadComponent();   //装配组件   assemble();  }catch (Exception e){   e.printStackTrace();  } } /**  * 将制定包及其子包下的所有组件加载到容器中  *  * @author Hutao  * @createDate 2020/11/14  */ private void loadComponent() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {  //用于获取包对应的URL,而URL中的分隔符为"/", 所以将包路径的分隔符"." 用"/"代替  String packagePath = packageName.replace(".","/");  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();  URL resource = classLoader.getResource(packagePath);  //通过获取此资源的协议名称,判断包名对应的资源类型  String protocol = resource.getProtocol();  if(!"file".equals(protocol)){   //只测试protocol为file的情况,其他情况还有jar等   return;  }  //获取了指定包及其子包下所对应的所有以suffix(.class)结尾的文件路径  List<String> filePathList = listFilePath(resource.getPath());  //获取类的完全限定名称  List<String> fullClassNameList = listFullClassName(filePathList);  //加载component到容器中  for (String fullClassName : fullClassNameList) {   addToContainer(fullClassName);  } } /**  * 获取指定文件夹下所有以.class结尾文件的抽象路径名的规范路径  * @param directoryPath 指定文件夹的路径  * @return 如获取到:包含符合条件的文件的规范路径的List  *   如未获取到:空的List  *  * @author Hutao  * @createDate 2020/11/14  */ private List<String> listFilePath(String directoryPath) throws IOException {  List<String> filePathList = new ArrayList<String>();  //参数校验  if(null==directoryPath){   return filePathList;  }  File directoryFile = new File(directoryPath);  if(!directoryFile.isDirectory()){   return filePathList;  }  String filePath = null;  File[] files = directoryFile.listFiles();  for (File file : files) {   if(!file.isDirectory()){    filePath = file.getCanonicalPath();    if(filePath.endsWith(".class")){     filePathList.add(filePath);    }   }   else{    //递归调用    filePathList.addAll(listFilePath(file.getCanonicalPath()));   }  }  return filePathList; } /**  * 根据.class文件的规范路径获取其类的全限定名  * @param filePathList .class文件的规范路径List  * @return 如获取到:包含类的全限定名的的List  *   如未获取到:空的List  *  * @author Hutao  * @createDate 2020/11/14  */ private List<String> listFullClassName(List<String> filePathList){  List<String> fullClassNameList = new ArrayList<String>();  if(null==packageName||null==filePathList){   return fullClassNameList;  }  String packagePath = packageName.replace(".","\\");  for (String filePath : filePathList) {   fullClassNameList.add(filePath.substring(filePath.indexOf(packagePath),filePath.indexOf(".class")).replace("\\","."));  }  return fullClassNameList; } /**  * 根据类的全限定名判断该类是否被标记为容器的组件,如果是则将组件添加到容器中  * @param fullClassName 类的全限定名  *  * @author Hutao  * @createDate 2020/11/14  */ private void addToContainer(String fullClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {  Class classObject = Class.forName(fullClassName);  if(!classObject.isInterface()    &&null!=classObject.getAnnotation(Component.class)){   //如果扫描的Class对象不是接口类型且有@Component注解,就将对应的component装配到容器中   container.put(fullClassName,classObject.newInstance());  } } /**  * 自动装配组件的属性值  *  * @author Hutao  * @createDate 2020/11/14  */ private void assemble() throws IllegalAccessException, ClassNotFoundException {  Set<Map.Entry<String, Object>> entrySet = container.entrySet();  for (Map.Entry<String, Object> entry : entrySet) {   Class classObj = Class.forName(entry.getKey());   Field[] declaredFieldArray = classObj.getDeclaredFields();   for (Field field : declaredFieldArray) {    if(null!=field.getAnnotation(Autowired.class)){     //如果属性被@Autowired注解标注,则根据属性的类型名进行自动装配     field.setAccessible(true);     String beanId = field.getType().getName();     field.set(entry.getValue(),container.get(beanId));    }   }  } } /**  * 根据类的全限定名从Ioc容器中获取对应的Bean  * @param fullClassName  * @return  *  * @author Hutao  * @createDate 2020/11/14  */ public Object getBean(String fullClassName){  if(null==fullClassName){   return null;  }  return container.get(fullClassName); }}

 

Demo.class

package com.hutao.springioc;import com.hutao.springioc.model.User;import java.io.IOException;public class Demo { public static void main(String[] args) throws IOException, ClassNotFoundException {  IocContainer iocContainer = new IocContainer("com.hutao.springioc");  User user = (User)iocContainer.getBean(User.class.getName());  user.chat(); }}

 

测试结果为:

//console输出:This is my dog and cat.Wow wow wow...Meow Meow Meow...Process finished with exit code 0

 注意:

  1. 以上IOC容器的实现原理只是基本的原理,甚至未查看源码进行验证,仅用于初步理解;









原文转载:http://www.shaoqun.com/a/493274.html

海维:https://www.ikjzd.com/w/1891

c88:https://www.ikjzd.com/w/1017.html

netporter:https://www.ikjzd.com/w/2132


运用反射机制和自定义注解模拟实现IOC容器,使其具有自动加载、自动装配和根据全限定类名获取Bean的功能。一.实现原理1-1IOC容器的本质IOC容器可理解为是一个map,其中的一个entry可理解为一个component(组件),entry中的key为beanId(全限定类名),entry中的value为bean(类对应的对象);具体的体现为:1-2自动加载自动加载是指IOC容器会自动加载被@C
转运中国:转运中国
transfer:transfer
上海海洋水族馆门票 :上海海洋水族馆门票
2020三亚宋城千古情春节有什么节目?三亚千古情春节节目时:2020三亚宋城千古情春节有什么节目?三亚千古情春节节目时
​洲际酒店集团宣布全面升级忠诚客户奖励计划IHG®优悦会—:​洲际酒店集团宣布全面升级忠诚客户奖励计划IHG®优悦会—

No comments:

Post a Comment