运用反射机制和自定义注解模拟实现IOC容器,使其具有自动加载、自动装配和根据全限定类名获取Bean的功能。
具体的体现为:
1-2 自动加载
-
-
@Component注解加在一个类前,表示此类被IOC容器管理,成为IOC容器中的一个组件;
-
-
-
@Autowired注解加在类中的一个属性前,表示此属性的值需要被自动装配;
-
2-2 目录结构
-
-
Cat, Dog, User为实体类;
-
IocContainer为IOC容器;
-
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; @Component public class Cat { public void mew(){ System.out.println("Meow Meow Meow..."); } }
Dog.java
package com.hutao.springioc.model; import com.hutao.springioc.annotation.Component; @Component public 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; @Component public 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容器的实现原理只是基本的原理,甚至未查看源码进行验证,仅用于初步理解;