栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

源码 - 01 简易版 Spring IOC 容器的实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

源码 - 01 简易版 Spring IOC 容器的实现

文章目录
    • 1. 框架具备的基本功能
    • 2. IOC 容器的实现
      • 1. 自定义注解
        • 1. 自定义注解 @Controller
        • 2. 自定义注解 @Service
        • 3. 自定义注解 @Repository
        • 4. 自定义注解 @Component
      • 2. ClassUtil 工具类封装
        • 1. 获取类加载器
        • 2. 获取扫描包下的类集合
        • 3. 实例化class
        • 4. 设置类的属性值
      • 3. 单例模式
        • 1. 恶汉模式
        • 2. 懒汉模式
        • 3. 枚举方式实现单例
      • 4. 容器 BeanContainer
        • 1. 获取Bean容器实例
        • 2. 扫描加载所有Bean
        • 3. 容器的增删改查操作
    • 3. 容器的依赖注入
      • 1. 自定义注解 @Autowired
      • 2. 实现依赖注入
      • 3. 测试

1. 框架具备的基本功能
  1. 解析配置
  2. 定位和注册对象
  3. 注入对象
  4. 提供通用的工具类
2. IOC 容器的实现
  1. 创建注解
  2. 提取标记对象
  3. 实现容器
  4. 依赖注入
1. 自定义注解 1. 自定义注解 @Controller
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
2. 自定义注解 @Service
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
3. 自定义注解 @Repository
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
4. 自定义注解 @Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
2. ClassUtil 工具类封装 1. 获取类加载器
@Slf4j
public class ClassUtil {
    
    public static  ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
}
2. 获取扫描包下的类集合

@Slf4j
public class ClassUtil {

    public static final String FILE_PROTOCOL = "file";

    
    public static Set> extractPackageClass(String packageName){
        //1.获取到类的加载器。
        ClassLoader classLoader = getClassLoader();
        //2.通过类加载器获取到加载的资源
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        // url:file:/D:/source_study/spring_source/simpleframework-master/simpleframework/target/classes/com/imooc/entity
        if (url == null){
            log.warn("unable to retrieve anything from package: " + packageName);
            return  null;
        }
        //3.依据不同的资源类型,采用不同的方式获取资源的集合
        Set> classSet = null;
        //过滤出文件类型的资源
        if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
            classSet = new HashSet>();
            // /D:/source_study/spring_source/simpleframework-master/simpleframework/target/classes/com/imooc/entity
            File packageDirectory = new File(url.getPath());
            extractClassFile(classSet, packageDirectory, packageName);
        }
        return classSet;
    }

    
    private static void extractClassFile(Set> emptyClassSet, 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 absoluteFilePath = file.getAbsolutePath();
                    if(absoluteFilePath.endsWith(".class")){
                        //若是class文件,则直接加载
                        addToClassSet(absoluteFilePath);
                    }
                }
                return false;
            }
            //根据class文件的绝对值路径,获取并生成class对象,并放入classSet中
            private void addToClassSet(String absoluteFilePath) {
                //1.从class文件的绝对值路径里提取出包含了package的类名
                // D:source_studyspring_sourcesimpleframework-mastersimpleframeworktargetclassescomimoocentityboHeadLine.class
                // D:.source_study.spring_source.simpleframework-master.simpleframework.target.classes.com.imooc.entity.bo.ShopCategory.class
                absoluteFilePath = absoluteFilePath.replace(File.separator, ".");
                // com.imooc.entity.bo.HeadLine.class
                String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
                // com.imooc.entity.bo.ShopCategory
                className = className.substring(0, className.lastIndexOf("."));
                //2.通过反射机制获取对应的Class对象并加入到classSet里
                Class targetClass = loadClass(className);
                emptyClassSet.add(targetClass);
            }
        });
        if(files != null){
            for(File f : files){
                //递归调用
                extractClassFile(emptyClassSet, f, packageName);
            }
        }
    }
    
    public static Class loadClass(String className){
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("load class error:", e);
            throw new RuntimeException(e);
        }
    }
}

测试:

public class ClassUtilTest {
    @DisplayName("提取目标类方法:extractPackageClassTest")
    @Test
    public void extractPackageClassTest(){
        Set> classSet =  ClassUtil.extractPackageClass("com.imooc.entity");
        System.out.println(classSet);
        Assertions.assertEquals(4, classSet.size());
    }
}

输出:

[class com.imooc.entity.dto.MainPageInfoDTO, class com.imooc.entity.bo.ShopCategory, class com.imooc.entity.dto.Result, class com.imooc.entity.bo.HeadLine]

3. 实例化class
public static  T newInstance(Class clazz, boolean accessible){
    try {
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(accessible);
        return (T)constructor.newInstance();
    } catch (Exception e) {
        log.error("newInstance error", e);
        throw new RuntimeException(e);
    }
}
4. 设置类的属性值
public static void setField(Field field, Object target, Object value, boolean accessible){
    field.setAccessible(accessible);
    try {
        field.set(target, value);
    } catch (IllegalAccessException e) {
        log.error("setField error", e);
        throw new RuntimeException(e);
    }
}
3. 单例模式 1. 恶汉模式
public class StarvingSingleton {
    private static final StarvingSingleton starvingSingleton = new StarvingSingleton();
    private StarvingSingleton(){ }
    public static StarvingSingleton getInstance(){
        return starvingSingleton;
    }
}
2. 懒汉模式
public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton instance;
    private LazyDoubleCheckSingleton(){}
    public static LazyDoubleCheckSingleton getInstance(){
        //第一次检测
        if (instance==null){
            //同步
            synchronized (LazyDoubleCheckSingleton.class){
                if (instance == null){
                    //memory = allocate(); //1.分配对象内存空间
                    //instance(memory);    //2.初始化对象
                    //instance = memory;   //3.设置instance指向刚分配的内存地址,此时instance!=null
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
    public static void main(String[] args) {
        System.out.println(LazyDoubleCheckSingleton.getInstance());
        System.out.println(LazyDoubleCheckSingleton.getInstance());
    }
}
3. 枚举方式实现单例
public class EnumStarvingSingleton {
    private EnumStarvingSingleton(){}

    public static EnumStarvingSingleton getInstance(){
        return ContainerHolder.HOLDER.instance;
    }
    private enum ContainerHolder{
        
        HOLDER;
        private EnumStarvingSingleton instance;
        ContainerHolder(){
            instance = new EnumStarvingSingleton();
        }
    }
}
4. 容器 BeanContainer 1. 获取Bean容器实例
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    
    private enum ContainerHolder {
        // 容器
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }
}
2. 扫描加载所有Bean
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    
    private final Map, Object> beanMap = new ConcurrentHashMap<>();
    
    
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    
    private enum ContainerHolder {
        // 容器
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }
    
    
    public int size() {
        return beanMap.size();
    }

    
    private boolean loaded = false;

    
    public boolean isLoaded() {
        return loaded;
    }
    
    
    private static final List> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);

    
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName" + packageName);
            return;
        }
        for (Class clazz : classSet) {
            for (Class annotation : BEAN_ANNOTATION) {
                //如果类上面标记了定义的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将目标类本身作为键,目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }
}

测试:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeanContainerTest {
    private static BeanContainer beanContainer;
    
    @BeforeAll
    static void init(){
        beanContainer = BeanContainer.getInstance();
    }
    
    @DisplayName("加载目标类及其实例到BeanContainer:loadBeansTest")
    @Order(1)
    @Test
    public void loadBeansTest(){
        Assertions.assertEquals(false, beanContainer.isLoaded());
        beanContainer.loadBeans("com.imooc");
        Assertions.assertEquals(6, beanContainer.size());
        Assertions.assertEquals(true, beanContainer.isLoaded());
    }
}
3. 容器的增删改查操作
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    
    private final Map, Object> beanMap = new ConcurrentHashMap<>();

    
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    
    private enum ContainerHolder {
        // 容器
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }

    
    public int size() {
        return beanMap.size();
    }

    
    private boolean loaded = false;

    
    public boolean isLoaded() {
        return loaded;
    }

    
    private static final List> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);

    
    public synchronized void loadBeans(String packageName) {
        //判断bean容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName" + packageName);
            return;
        }
        for (Class clazz : classSet) {
            for (Class annotation : BEAN_ANNOTATION) {
                //如果类上面标记了定义的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    //将目标类本身作为键,目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }

    
    public Object addBean(Class clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    
    public Object removeBean(Class clazz) {
        return beanMap.remove(clazz);
    }

    
    public Object getBean(Class clazz) {
        return beanMap.get(clazz);
    }
    
    
    public Set> getClasses(){
        return beanMap.keySet();
    }
    
    
    public Set getBeans(){
        return new HashSet<>( beanMap.values());
    }
    
    
    public Set> getClassesByAnnotation(Class annotation){
        //1.获取beanMap的所有class对象
        Set> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.通过注解筛选被注解标记的class对象,并添加到classSet里
        Set> classSet = new HashSet<>();
        for(Class clazz : keySet){
            //类是否有相关的注解标记
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
    
    
    public Set> getClassesBySuper(Class interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
        Set> classSet = new HashSet<>();
        for(Class clazz : keySet){
            // 判断keySet里的元素是否是传入的接口或者类的子类
            // public native boolean isAssignableFrom(Class cls):
            // 确定此 Class 对象表示的类或接口是否与指定的 Class 参数表示的类或接口相同,或者是其超类或超接口。如果是,则返回 true;否则返回false。
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0? classSet: null;
    }
}
 

测试:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeanContainerTest {
    private static BeanContainer beanContainer;

    @BeforeAll
    static void init(){
        beanContainer = BeanContainer.getInstance();
    }

    @DisplayName("根据类获取其实例:getBeanTest")
    @Order(2)
    @Test
    public void getBeanTest(){
        MainPageController controller = (MainPageController)beanContainer.getBean(MainPageController.class);
        Assertions.assertEquals(true, controller instanceof MainPageController);
        DispatcherServlet dispatcherServlet = (DispatcherServlet) beanContainer.getBean(DispatcherServlet.class);
        Assertions.assertEquals(null, dispatcherServlet);
    }
    
    @DisplayName("根据注解获取对应的实例:getClassesByAnnotationTest")
    @Order(3)
    @Test
    public void getClassesByAnnotationTest(){
        Assertions.assertEquals(true, beanContainer.isLoaded());
        Assertions.assertEquals(3, beanContainer.getClassesByAnnotation(Controller.class).size());
    }

    @DisplayName("根据接口获取实现类:getClassesBySuperTest")
    @Order(4)
    @Test
    public void getClassesBySuperTest(){
        Assertions.assertEquals(true, beanContainer.isLoaded());
        Assertions.assertEquals(true, beanContainer.getClassesBySuper(HeadLineService.class).contains(HeadLineServiceImpl.class));
    }
}
3. 容器的依赖注入
  1. 定义依赖注入的注解标签

  2. 实现被创建注解标记的成员变量实例,并将其注入到成员变量里

  3. 依赖注入的使用

1. 自定义注解 @Autowired
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}
2. 实现依赖注入
@Slf4j
public class DependencyInjector {
    
    private BeanContainer beanContainer;
    public DependencyInjector(){
        beanContainer = BeanContainer.getInstance();
    }
    
    
    public void doIoc(){
        if(ValidationUtil.isEmpty(beanContainer.getClasses())){
            log.warn("empty classset in BeanContainer");
            return;
        }
        //1.遍历Bean容器中所有的Class对象
        for(Class clazz : beanContainer.getClasses()){
            //2.遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)){
                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);
                    } else {
                        //6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
                        Object targetBean =  beanContainer.getBean(clazz);
                        ClassUtil.setField(field, targetBean, fieldValue, true);
                    }
                }
            }
        }
    }
    
    private Object getFieldInstance(Class fieldClass, String autowiredValue) {
        // 如果容器中存在该类的实例,直接返回
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null){
            return fieldValue;
        } else {
            // 获取该类的子类,并返回子类的实例
            Class implementedClass = getImplementedClass(fieldClass, autowiredValue);
            if(implementedClass != null){
                return beanContainer.getBean(implementedClass);
            } else {
                return null;
            }
        }
    }

    
    private Class getImplementedClass(Class fieldClass, String autowiredValue) {
        Set> classSet =  beanContainer.getClassesBySuper(fieldClass);
        if(!ValidationUtil.isEmpty(classSet)){
            // 如果@Autowired注解中没有指定名称
            if(ValidationUtil.isEmpty(autowiredValue)){
                if(classSet.size() == 1){
                    return classSet.iterator().next();
                } else {
                    //如果多于两个实现类且用户未指定其中一个实现类,则抛出异常
                    throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowired's value to pick one");
                }
            } else {
                // 如果@Autowired注解中指定了属性名称,找到对应属性名称的类
                for(Class clazz : classSet){
                    if(autowiredValue.equals(clazz.getSimpleName())){
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
}
3. 测试

① Controller

@Controller
@Getter
@RequestMapping(value = "/main")
public class MainPageController {

    @Autowired(value = "HeadLineShopCategoryCombineServiceImpl")
    private HeadLineShopCategoryCombineService headLineShopCategoryCombineService;
    
}

② Service

public interface HeadLineShopCategoryCombineService {
    Result getMainPageInfo();
}

③ ServiceImpl

@Service
public class HeadLineShopCategoryCombineServiceImpl implements HeadLineShopCategoryCombineService {
    @Autowired
    private HeadLineService headLineService;
    @Autowired
    private ShopCategoryService shopCategoryService;
}

④ 测试方法:

public class DependencyInjectorTest {
    @DisplayName("依赖注入doIoc")
    @Test
    public void doIocTest(){
        BeanContainer beanContainer = BeanContainer.getInstance();
        beanContainer.loadBeans("com.imooc");
        Assertions.assertEquals(true, beanContainer.isLoaded());
        MainPageController mainPageController = (MainPageController)beanContainer.getBean(MainPageController.class);
        Assertions.assertEquals(true, mainPageController instanceof MainPageController);
        Assertions.assertEquals(null, mainPageController.getHeadLineShopCategoryCombineService());
        new DependencyInjector().doIoc();
        Assertions.assertNotEquals(null, mainPageController.getHeadLineShopCategoryCombineService());
        Assertions.assertEquals(true, mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
        Assertions.assertEquals(false, mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);
    }
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号