手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【任务阶段3】

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【任务阶段3】

任务阶段1链接
一、实现任务阶段 1- 编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象
任务阶段2链接
二、实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map
程序框架图在这里插入图片描述

三、实现任务阶段 3- 初始化 bean 单例池,并完成 getBean 方法 , createBean 方法
1…在CodeSnakeSpringApplicationContext类中定义singletonObjects容器,并将单例的Bean对象创建好实例放入其中(scanSingletonObjects()方法),实现getBean(String beanName)方法,没有@Scope(value = “prototype”)的对象默认设为@Scope(value = “singleton”)

package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;
    //定义beanDefinitionMap,存放BeanDefinition对象
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //定义singletonObjects,存放单例对象
    private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public CodeSnakeSpringApplicationContext(Class configClass) {
        scanBeanDefinition(configClass);
    }
    
    //初始化单例池方法
    private void scanSingletonObjects() {
        //通过 beanDefinitionMap,初始化 singletonObjects 单例池
        ConcurrentHashMap.KeySetView<String, BeanDefinition> keys = beanDefinitionMap.keySet();
        for (String key : keys) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(key);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition, key);
                singletonObjects.put(key, bean);
            }
        }
    }
    
    //该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
    private void scanBeanDefinition(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                //将类名首字母小写作为beanName
                                beanName = StringUtils.uncapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,获取他配置的值
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置值,就默认Singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinition放入beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
    //完成createBean(BeanDefinition beanDefinition)方法
    private Object createBean(BeanDefinition beanDefinition, String beanName) {
        //得到Bean的clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到对象实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
            System.out.println("=====创建好实例=====" + instance);
            if (instance != null) {
                return instance;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    //编写方法,返回容器的对象
    public Object getBean(String name) {
        if (beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //说明是单例配置
                return singletonObjects.get(name);
            } else {//如果不是单例的,就反射一个新的对象
                return createBean(beanDefinition, name);
            }
        }
        throw new NullPointerException("没有该bean");
    }
}

2.将studentService.java设置成多例的(@Scope(value = “prototype”)),方便测试

package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Autowired;
import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.Scope;
import com.hykedu.spring.processor.InitializingBean;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Scope(value = "prototype")
@Component(value = "monsterService")
public class MonsterService implements InitializingBean {
}

测试方法AppMain,对其进行debug

package com.hykedu.spring;

import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        StudentDao studentDao1 = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentDao studentDao = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentService studentService1 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        StudentService studentService2 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        System.out.println("ok");
    }
}

测试结果:只有单例的StudentDao存放到了singletonObjects容器中,实现了@Scope(value = “prototype”)注解的的多例对象StudentService没有存放到单例池中,而单例的StudentDao对象创建了两个对象地址相同,多例的StudentService的两个对象地址不同,与原生Spring一致


任务阶段4链接
实现任务阶段 4- 完成依赖注入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值