手写一个简易版的spring ioc容器
步骤
1.创建注解
2.提取标记对象
3.实现容器
4.依赖注入
创建注解
创建4个注解,用来自动装配
package org.simpleframework.core; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) // 作用在类上 @Retention(RetentionPolicy.RUNTIME) public @interface Component { }
提取标记对象
实现思路:
1.指定扫包范围,获取范围内的所有类
2.遍历所有类,获取被注解标记的类并加载进容器
extractPackageClass需要完成的事情:
1.获取到类的加载器
2.通过类加载器获取到加载的资源信息
3.根据不同的资源类型,采用不同的方式获取资源的集合
package org.simpleframework.util; import java.io.File; import java.io.FileFilter; import java.net.URL; import java.util.HashSet; import java.util.Set; public class ClassUtil { public static final String FILE_PROTOCOL = "file"; public static final String CLASS_SUFFIX = ".class"; public static Set> extractPackageClass(String packageName) { // 1.获取到类的加载器 ClassLoader classLoader = getClassLoader(); // 2.通过类加载器获取到加载的资源信息 URL url = classLoader.getResource(packageName.replace(".", "/")); if (url == null) { System.out.println("unable to retrieve anything from package " + packageName); return null; } // 3.根据不同的资源类型,采用不同的方式获取资源的集合 Set > classSet = null; // 过滤出文件类型的资源 if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)) { classSet = new HashSet<>(); File packageDirectory = new File(url.getPath()); extractClassFile(classSet, packageDirectory, packageName); } // else { } TODO 此处还可以加入对其他类型资源的处理 return classSet; } private static void extractClassFile(Set > classSet, File fileSource, String packageName) { // 不是一个目录则直接结束递归方法 if (!fileSource.isDirectory()) { return; } // 如果是一个文件夹,则调用listFiles方法获取文件夹下的文件或文件夹 File[] files = fileSource.listFiles(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else { // 获取文件的绝对路径 String absolutePath = file.getAbsolutePath(); // 判断是不是字节码文件 if (absolutePath.endsWith(CLASS_SUFFIX)) { // 若是class文件,则放入classSet addToClassSet(absolutePath); } } return false; } // 根据class文件的绝对路径,获取并生成class对象,并放入classSet中 private void addToClassSet(String absolutePath) { // 1.从class文件的绝对路径里提取出包含了package的类名 absolutePath = absolutePath.replace(File.separator, "."); // 去掉项目路径 String className = absolutePath.substring(absolutePath.indexOf(packageName)); // 去掉.class后缀 className = className.substring(0, className.lastIndexOf(".")); // 2.通过反射获取对应的Class对象并放入到classSet中 try { Class> targetClass = Class.forName(className); classSet.add(targetClass); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } } }); if (files != null && files.length != 0) { for (File file : files) { // 递归调用 extractClassFile(classSet, file, packageName); } } } public static ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } public static void main(String[] args) { Set > classSet = extractPackageClass("com.imooc.entity"); System.out.println(); } }
实现容器
容器的组成部分
1.保存Class对象及其实例的载体
2.容器的加载
3.容器的 *** 作方式
实现容器的加载
1.配置的管理与获取
2.获取指定范围内的Class对象
3.根据配置提取Class对象,连同实例一并存入容器
package org.simpleframework.core; import org.simpleframework.core.annotation.Component; import org.simpleframework.core.annotation.Controller; import org.simpleframework.core.annotation.Repository; import org.simpleframework.core.annotation.Service; import org.simpleframework.util.ClassUtil; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class BeanContainer { private final Map, Object> beanMap = new ConcurrentHashMap<>(); public static final List > BEAN_ANNOTATION_LIST = Arrays.asList( Component.class, Controller.class, Repository.class, Service.class ); private boolean loaded = false; private BeanContainer() { } public static BeanContainer getInstance() { return BeanContainerHolder.HOLDER.instance; } private enum BeanContainerHolder { HOLDER; private final BeanContainer instance; BeanContainerHolder() { instance = new BeanContainer(); } } public synchronized void loadBeans(String packageName) { if (loaded) { return; } Set > classSet = ClassUtil.extractPackageClass(packageName); if (classSet == null && classSet.isEmpty()) { System.out.println("extract nothing from package " + packageName); return; } for (Class> clazz : classSet) { for (Class extends Annotation> annotation : BEAN_ANNOTATION_LIST) { // 如果类上面标注了指定的注解 if (clazz.isAnnotationPresent(annotation)) { // 将目标类本身作为键,目标类的实例作为值,放入到beanMap中 beanMap.put(clazz, ClassUtil.newInstance(clazz)); } } } loaded = true; } public static void main(String[] args) { BeanContainer beanContainer = BeanContainer.getInstance(); beanContainer.loadBeans("com.imooc"); System.out.println(); } }
使用自定义的注解标注需要加载进容器的类
测试自定义的容器加载bean
实现容器的 *** 作方式
1.增加,删除 *** 作
2.根据Class获取对应实例
3.获取所有的Class和实例
4.通过注解来获取被注解标注的Class
5.通过超类获取子类的Class
6.获取容器载体保存Class的数量
依赖注入
实现思路
1.定义相关的注解标签
2.实现创建被注解标记的成员变量的实例,并将其注入到成员变量里
3.依赖注入的使用
定义相关的注解标签
package org.simpleframework.inject.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) // 作用在成员变量上 @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { }
实现创建被注解标记的成员变量的实例,并将其注入到成员变量里
执行ioc
1.遍历bean容器中所有的Class对象
2.遍历Class对象的所有成员变量
3.找出被Autowired标记的成员变量
4.获取这些成员变量的类型
5.获取这些成员变量的类型在容器里对应的实例
6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
package org.simpleframework.inject; import com.imooc.controller.UserController; import com.imooc.service.UserService; import org.simpleframework.core.BeanContainer; import org.simpleframework.inject.annotation.Autowired; import org.simpleframework.util.ClassUtil; import java.lang.reflect.Field; import java.util.Set; public class DependencyInjector { private BeanContainer beanContainer; public DependencyInjector() { beanContainer = BeanContainer.getInstance(); } public void doIoc() { // 1.遍历bean容器中所有的Class对象 Set> classSet = beanContainer.getClasses(); if (classSet == null || classSet.isEmpty()) { System.out.println("empty classSet in BeanContainer"); return; } for (Class> clazz : classSet) { // 2.遍历Class对象的所有成员变量 Field[] fields = clazz.getDeclaredFields(); if (fields.length == 0) { continue; } for (Field field : fields) { // 3.找出被Autowired标记的成员变量 if (field.isAnnotationPresent(Autowired.class)) { Autowired autowired = field.getAnnotation(Autowired.class); String autowiredValue = autowired.value(); // 4.获取这些成员变量的类型 Class> fieldClass = field.getType(); // 5.获取这些成员变量的类型在容器里对应的实例 Object fieldValue = getFieldInstance(fieldClass, autowiredValue); if (fieldValue == null) { throw new RuntimeException("unable to inject relevant type, target fieldClass is " + fieldClass.getName() + "autowiredValue is " + autowiredValue); } // 6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里 Object targetBean = beanContainer.getBean(clazz); ClassUtil.setField(targetBean, field, fieldValue); } } } } private Object getFieldInstance(Class> fieldClass, String autowiredValue) { Object fieldValue = beanContainer.getBean(fieldClass); if (fieldValue != null) { return fieldValue; } else { Class> implementClass = getImplementClass(fieldClass, autowiredValue); if (implementClass != null) { return beanContainer.getBean(implementClass); } else { return null; } } } private Class> getImplementClass(Class> fieldClass, String autowiredValue) { Set > classSet = beanContainer.getClassesBySuper(fieldClass); if (classSet == null || classSet.isEmpty()) { System.out.println("empty classSet in BeanContainer"); return null; } else { // 如果@Autowired's value没有值 if (autowiredValue == null || autowiredValue.length() == 0) { if (classSet.size() == 1) { return classSet.iterator().next(); } else { // 如果多于两个实现类,且用户未指定其中一个实现类,则抛出异常 throw new RuntimeException("multiple implement classes for " + fieldClass.getName() + ", please set @Autowired's value to pick one"); } } else { for (Class> clazz : classSet) { if (autowiredValue.equals(clazz.getSimpleName())) { return clazz; } } } } return null; } public static void main(String[] args) { BeanContainer beanContainer = BeanContainer.getInstance(); beanContainer.loadBeans("com.imooc"); UserController userController = (UserController) beanContainer.getBean(UserController.class); UserService userService = userController.getUserService(); // 执行ioc依赖注入前 new DependencyInjector().doIoc(); // 执行ioc依赖注入后 UserService userService1 = userController.getUserService(); System.out.println(); } }
至此,实现了简易ioc的整个流程,有装载bean的容器,能够注入依赖
源码地址https://gitee.com/jiangli31346337/simple-framework
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)