spring手写---DI实现

参考:《Spring 5核心原理》

    我们这里实现Spring中DI,自动注入的功能,主要涉及class遍历,加载,解析,然后存入三个容器中,实例化几个过程。核心的内容就是:反射。

整体结构:

  一,注解设计

1.@XService

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XService {
    String value() default "";
}

2.@XAutowired

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XAutowired {
    String value() default "";
}

3.@XController

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XController {
    String value() default "";
}

二,顶层接口设计

1.XFactoryBean

public interface XFactoryBean {
}

2.XBeanFactory,它提供getBean()方法

public interface XBeanFactory {
    Object getBean(String beanName) throws Exception;
    public Object getBean(Class<?> beanClass) throws Exception;
}

3.XBeanDefinition( beanClass的封装类)这里使用了lombok插件

@Data
public class XBeanDefinition {
    private String beanClassName; //原生bean的全类名
    private boolean lazyInit=false;//标记是否延时加载
    private String factoryBeanName; //保存beanName,在IOC容器中 存储的key
}

4.XBeanWrapper (bean实例的封装)

@Data
public class XBeanWrapper {
    private Object wrappedInstance;
    private Class<?> wrappedClass;
    public XBeanWrapper(Object instance){
        this.wrappedInstance=instance;
    }
}

5.XAbstractApplicationContext

容器的顶层抽象

public abstract class XAbstractApplicationContext {
    public void refresh() throws Exception{}
}

6.XDefaultListableBeanFactory

public class XDefaultListableBeanFactory extends XAbstractApplicationContext {
    //存储注册消息的beanDefinition
    protected  final Map<String,XBeanDefinition> beanDefinitionMap=new ConcurrentHashMap<String, XBeanDefinition>();
}

三,主要内容

1.用户入门类XApplicationContext

public class XApplicationContext extends XDefaultListableBeanFactory implements XBeanFactory {
    private String[] configLocations;
    private XBeanDefinitionReader reader;
    //单例的IOC容器缓冲
    private Map<String,Object> factoryBeanObjectCache =new ConcurrentHashMap<String, Object>();
    //通用的 IOC容器
    private Map<String, XBeanWrapper> factoryBeanInstanceCache =new ConcurrentHashMap<String, XBeanWrapper>(); //防止多线程
    public XApplicationContext(String... configLocations){
        this.configLocations=configLocations;
        try{
            refresh();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void refresh() throws Exception{
        //1.定位 配置文件 把扫描路径下 所有class全限名,全部准备好了
        reader=new XBeanDefinitionReader(this.configLocations);
        //2.加载配置文件   开始解析
        List<XBeanDefinition> beanDefinitions=reader.loadBeanDefinitions();
        //3.注册,把配置信息放到容器里面
        doRegisterBeanDefinition(beanDefinitions);
        //4.把不是延时加载的类提前初始化
        doAutowired();
    }
    //只处理非延时加载的情况
    private void doAutowired(){
        for(Map.Entry<String,XBeanDefinition> beanDefinitionEntry:super.beanDefinitionMap.entrySet()){
            String beanName=beanDefinitionEntry.getKey();
            if(!beanDefinitionEntry.getValue().isLazyInit()){
                try{
                    getBean(beanName);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
    //注册? 容器初始化
    private void doRegisterBeanDefinition(List<XBeanDefinition> beanDefinitions) throws Exception{
        for(XBeanDefinition beanDefinition:beanDefinitions){
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("the “"+beanDefinition.getFactoryBeanName()+" ” is existe!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
        }
        //容器初始化完毕。
    }
    //通过class对象,获得实例
    public Object getBean(Class<?> beanClass) throws Exception{
        return getBean(beanClass.getName());
    }
    //依赖注入,从这里开始,读取beanDefinition中的信息,
    //然后通过反射机制创建一个实例并返回
    //spring做法是,不会把最原始的对象放出去,会用一个beanWrapper来进行一次包装
    //装饰器模式:
    //1.保留原来的oop关系
    //2.需要对他进行扩展,增强(为了以后的aop打基础)
    public Object getBean(String beanName) throws Exception{
        XBeanDefinition beanDefinition=super.beanDefinitionMap.get(beanName);
        try{
            XBeanPostProcessor beanPostProcessor=new XBeanPostProcessor();
            Object instance=instantiateBean(beanDefinition);
            if(instance==null) {
                System.out.println(beanName+" instance fail!");
                return null;
            }
            //在实例初始化以前,调用一次
            beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            XBeanWrapper beanWrapper=new XBeanWrapper(instance);
            this.factoryBeanInstanceCache.put(beanName,beanWrapper);
            //在实例化,初始化以后调用一次
            beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            populateBean(beanName,instance);
            return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    private void populateBean(String beanName,Object instance){
        Class clazz=instance.getClass();  //获得class对象进行分析。
        //一定要实现这两个注解 才可能 有自动注入的 字段
        if(!(clazz.isAnnotationPresent(XController.class))||clazz.isAnnotationPresent(XService.class))
            return;
        Field []fields=clazz.getDeclaredFields(); //获得所有的字段
        for(Field field:fields){  //遍历 所有成员变量(字段)
            if(!field.isAnnotationPresent(XAutowired.class))
                continue;
            XAutowired autowired=field.getAnnotation(XAutowired.class); //获得这个标签干嘛?
            String autowiredBeanName =autowired.value().trim(); //获得标签的value值(一般value是空的)
            if("".equalsIgnoreCase(autowiredBeanName)){  //如果这个name为空
                autowiredBeanName=field.getType().getName();  //把它的类型 全限名字 给他。(这个是字段的名字,beanName是自己类的名字)不能混用
            }
            field.setAccessible(true);
            try{  //里面放的是 类变量的名字 ,, get要获得的是 类的全限名字,而且还是接口的。
                if(this.factoryBeanInstanceCache.get(autowiredBeanName)==null){
                    getBean(autowiredBeanName);
                }
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance()); //设置实例,从缓冲中获取。
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    //传一个beanDefinition,就返回一个实例bean
    private Object instantiateBean(XBeanDefinition beanDefinition){
        Object instance=null;
        String className=beanDefinition.getBeanClassName();
        try{
            //因为根据class才能确定一个类是否有实例
            if(this.factoryBeanObjectCache.containsKey(className)){
                instance=this.factoryBeanObjectCache.get(className);//直接获取
            }else{
                Class<?> clazz=Class.forName(className);
                instance=clazz.newInstance(); //返回一个实例化 bean
                this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance); //实例化的bean 放入缓冲中
            }
            return instance;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    public String[] getBeanDefinitionNames(){
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }
    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }
    public Properties getConfig(){
        return this.reader.getConfig();
    }
}

这个类十分关键,我们的程序就是从这里启动的,它的构造方法开始,传入一个application.properties的文件路径,去读取,里面存放的是包扫描的路径,如下所示:

scanPackage = com.myproject.text

它的启动走的是refresh()方法,和原版spring同步,进入这里,分成了四个方法,第一个就是通过路径,读取该配置文件(通过Reader初始化去读出application.properties)

Reader 使用java自带的Properties去解析.properties文件,直接获取我们的scanPackage值。然后使用doScaner()扫描该路径,将路径下的所有class文件的全限名,保存起来(这里的class肯定是,编译之后的classes文件下的,所以这里的路径导入,很有讲究,涉及到this.getClass().getResource(),还有this.getClass().getClassLoader(),这两者下传入路径参数不同

参考:https://www.cnblogs.com/ncy1/articles/8811238.html

          https://blog.csdn.net/qq_37859539/article/details/82822107

对这里目录,必须采用递归的方式,把所有内容放到成员变量registryBeanClasses中。其实我们这里的扫描,就是把所有的bean都搞出来了。

//对配置文件 进行查找,读取,解析
public class XBeanDefinitionReader {
    private List<String> registryBeanClasses=new ArrayList<String>();
    private Properties config=new Properties();
    private final String SCAN_PACKAGE="scanPackage";
    public XBeanDefinitionReader(String... locations){
        //通过url定位找到 其所对应的文件,然后转换为文件流
        //找到配置文件 application.properties
        InputStream is=this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:",""));
        try{
            config.load(is);
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(null!=is){
                try{
                    is.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        //从配置文件 application.properties中 读取数据!---》找到需要扫描包 路径!
        System.out.println("package scan : "+config.getProperty(SCAN_PACKAGE));
        doScanner(config.getProperty(SCAN_PACKAGE));
    }
    //遍历指定的 扫描路径,把扫描到的所以class 权限名,加入注册列表中
    private void doScanner(String scanPackage){
        //将包的.X.X路径 转换为 文件路径,实际上是把. 替换为/
        // getXX(),接收相对路径,或者绝对路径,将一个文件,封装成为url。
        // 相对的是class文件的位置 ,绝对是”/“开头的项目路径,路径位置是 编译后的target文件中的 路径。!
        System.out.println("/"+scanPackage.replaceAll("\\.","/"));
        URL url=this.getClass().getResource("/"+scanPackage.replaceAll("\\.","/"));
        if(url==null){
            System.out.println("url is null");
            return;
        }
        //文件遍历,找到class文件。
        File classPath=new File(url.getFile());
        for(File file:classPath.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else {
                if(!file.getName().endsWith(".class"))
                    continue;
                String className=(scanPackage+"."+file.getName().replace(".class",""));
                registryBeanClasses.add(className); //放入列表中
            }
        }
    }
    public Properties getConfig(){
        return this.config;
    }
    public List<XBeanDefinition> loadBeanDefinitions(){
        List<XBeanDefinition> result=new ArrayList<XBeanDefinition>();
        try{
            for(String className: registryBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                if (beanClass.isInterface())
                    continue;
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
                Class<?>[] interfaces = beanClass.getInterfaces();
                for (Class<?> i : interfaces) {
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return result;
    }
    //把每一个配置信息解析成为一个beanDefinition
    private XBeanDefinition doCreateBeanDefinition(String factoryBeanName,String beanClassName){
        XBeanDefinition beanDefinition=new XBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        return beanDefinition;
    }
    //把类名 首字母改为小写
    private String toLowerFirstCase(String simpleName){
        char []chars=simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }
}

初始化好reader之后,我们回到refresh()方法,到第二步,loadBeanDefinitionis(),就是通过类的全限名,获得Class对象(这里反射就进场了),然后还要解析这个类的接口,把接口的全限名也搞出来,同时把小名也查出来,封装到beanDefintion中。全部放到ApplicationContext的refresh()方法中的局部变量中,这里还是一个ArrayList()但是它有了所有的需要的class的beanDefinitioni对象。

(注意,这里遍历的时候,是把没有用的接口,排除了的,接口的注入,一定是有类实现了的!,所以最后实例化的时候,肯定要排除接口!!!!)

第三步:将List<beanDifintion> 转为HasMap<String,BeanDefinition>,放到DefaultApplicationContext中。

   private void doRegisterBeanDefinition(List<XBeanDefinition> beanDefinitions) throws Exception{
        for(XBeanDefinition beanDefinition:beanDefinitions){
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("the “"+beanDefinition.getFactoryBeanName()+" ” is existe!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
        }
        //容器初始化完毕。
    }

第四步:实例化bean(内容顶上面三步)

调用doAutowired()方法,遍历父类的HashMap,然后获得map.key也就是beanName,直接采用getBean(beanName)实例化,为什么使用getBean,进行实例化呢?因为我用户可以直接调用getBean,获取bean,bean又分为单例,和原型,默认是单例的,所以如果遇到原型,在getBean()的时候,还是需要进行实例化。

重点就是getBean()方法中!

这里在我们的ApplicationContext中存在两个Cache,一个是Object,另外一个是Instance(它是wrapper封装的)

我们遍历一个beanName,就对它进行实例化,使用beanDefinition+反射,获得实例化对象,然后存入Object中,然后进行一些前后处理,封装,放如wrapper中,再放入instanceCache中,最后对其内部成员进行实例化,如果有注解@Service和@Controller那么内部是有可能需要自动注入的,他们的注入,是同构递归getBean()实现的!

最后实例就存在与InstanceCache中了!

最后几个 走形式的类

public class XBeanPostProcessor {
    //为在bean的初始化之前 提供回调入口
    public Object postProcessBeforeInitialization(Object bean,String beanName) throws Exception{
        return bean;
    }
    //为在bean 初始化之后提供回调入口
    public Object postProcessAfterInitialization(Object bean,String beanName) throws Exception{
        return  bean;
    }
}
@Data
public class XBeanWrapper {
    private Object wrappedInstance;
    private Class<?> wrappedClass;
    public XBeanWrapper(Object instance){
        this.wrappedInstance=instance;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值