前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写DI依赖注入

手写DI依赖注入

作者头像
每天学Java
发布2020-06-02 10:08:14
3790
发布2020-06-02 10:08:14
举报
文章被收录于专栏:每天学Java

DI是IOC的一种重要实现,Spring能够动态的向某个对象提供它所需要的其他对象,就是通过DI(Dependency Injection,依赖注入)来实现的

在上一篇文章中,对Spring的AOP进行了简单的实现,其中一部分代码其实就有注入的影子,但是我没有具体的提取出来,今天我们就来具体的实现注入。

01

工厂类

我们都知到在Spring项目中,所有的实例对象的获取不应该直接new,而是通过某个工厂获取。在Spring中的工厂有两个:BeanFactory和ApplicationContext,其中BeanFactory在获取Bean的时候才会创建类的实例,ApplicationContext是在配置文件被加载的时候就创建类的实例,此外ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。

这里我们自己实现一个工厂类,同DefaultListableBeanFactory一样将Bean放入到HashMap中:

代码语言:javascript
复制
public class MyApplicationContext {
    /**
     * 此Map容器用于存储类定义对象
     */
    private Map<String, Class<?>> beanDefinationFacotry = new ConcurrentHashMap<>();
    /**
     * 此Map容器用于存储单例对象
     */
    private Map<String, Object> singletonbeanFactory = new ConcurrentHashMap<>();

    /**
     * 有参构造方法,参数类型为指定要扫描加载的包名
     */
    public MyApplicationContext(String packageNames) {
        System.out.println("开始扫描包:" + packageNames);
        /**扫描指定的包路径*/
        getClasses(packageNames);
        /**进行DI依赖注入*/
        dependencyInjection();
    }

}

其中getClassess方法我就不详细展示了,还是老规矩去小程序知识模块去复制,这里展示一点改动的地方:

代码语言:javascript
复制
Class<?> c = Thread.currentThread().getContextClassLoader()
                            .loadClass(packageName + '.' + className);
//判定这个类上是否有MyComponent注解
if (c.isAnnotationPresent(MyComponent.class)) {
//将类对象存储到map容器中
beanDefinationFacotry.put(beanId, c);
}
beanDefinationFacotry.put(beanId, c);

这里是在我们遍历class文件,如果该class文件存在注解MyComponent的时候,我将对象放入到beanDefinationFacotry这个存储对象的Map中。

dependencyInjection方法我们来看一下,首先是获取beanDefinationFacotry中所有对象,然后再遍历对象定义的字段,如果字段上使用了MyAutowired注解我们就通过field.set方法将值赋到字段上面去,如此我们就注入成功了。

代码语言:javascript
复制
 private void dependencyInjection() {
        //获取容器中所有的类定义对象
        Collection<Class<?>> classes = beanDefinationFacotry.values();
        //遍历每一个类对象
        for (Class<?> cls : classes) {
            //获取类对象的名字全称(包名+类名)
            String clsName = cls.getName();
            //获取类名
            clsName = clsName.substring(clsName.lastIndexOf(".") + 1);
            //将类名(通常为大写开头)的第一个字母转换小写
            String beanId = String.valueOf(clsName.charAt(0)).toLowerCase() + clsName.substring(1);
            //获取类中所有的属性
            Field[] fields = cls.getDeclaredFields();
            //遍历每一个属性
            for (Field field : fields) {
                //如果这个属性上有MyAutowired注解,进行注入操作
                if (field.isAnnotationPresent(MyAutowired.class)) {
                    try {
                        //获取属性名
                        String fieldName = field.getName();
                        System.out.println("属性名:" + fieldName);
                        //定义为属性注入的bean对象(此对象从容器中获取)
                        Object fieldBean = null;
                        //首先根据属性名从容器中取出对象,如果不为null,则赋值给fieldBean对象
                        if (beanDefinationFacotry.get(fieldName) != null) {
                            fieldBean = getBean(fieldName, field.getType());
                        } else {    //否则按照属性的类型从容器中取出对象进行注入
                            //获取属性的类型(包名+类名)
                            String type = field.getType().getName();
                            //截取最后的类名
                            type = type.substring(type.lastIndexOf(".") + 1);
                            //将类名(通常为大写开头)的第一个字母转换小写
                            String fieldBeanId = String.valueOf(type.charAt(0)).toLowerCase() + type.substring(1);
                            System.out.println("属性类型ID:" + fieldBeanId);
                            //根据转换后的类型beanId,从容器中获取对象并赋值给fieldBean对象
                            fieldBean = getBean(fieldBeanId, field.getType());
                        }
                        System.out.println("要为属性注入的值:" + fieldBean);
                        //如果fieldBean对象不为空,则为该属性进行注入
                        if (fieldBean != null) {
                            //获取此类定义的对象的实例对象
                            Object clsBean = getBean(beanId, cls);
                            //设置此属性可访问
                            field.setAccessible(true);
                            //为该属性注入值
                            field.set(clsBean, fieldBean);
                            System.out.println("注入成功!");
                        } else {
                            System.out.println("注入失败!");
                        }
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

02

定义注解

在上面我们看到工厂类中看到了两个注解MyAutowired和MyComponent,我们具体来看

MyAutowired定义是放在FIELD上

代码语言:javascript
复制
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}

MyComponent的scope方法我们在获取bean的时候会用到,用于判断是否单例

代码语言:javascript
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
    public String scope() default "";
}

03

Service

这里我们就具体的来使用注解,然后测试效果

代码语言:javascript
复制
@MyComponent
public class Service {
    public void service() {
        System.out.println("service.....");
    }
}

测试类

代码语言:javascript
复制
@MyComponent
public class TestDI {
    private static MyApplicationContext ctx;

    @MyAutowired
    private static Service service;

    public static void main(String[] args) {
        ctx = new MyApplicationContext("com.example.demo.di");
        service = ctx.getBean("service", Service.class);
        service.service();
    }
}

测试结果:

代码语言:javascript
复制
Connected to the target VM, address: '127.0.0.1:56106', transport: 'socket'
开始扫描包:com.example.demo.di
Disconnected from the target VM, address: '127.0.0.1:56106', transport: 'socket'
属性名:service
要为属性注入的值:com.example.demo.di.Service@1c2c22f3
注入成功!
service.....

完整代码进入小程序进行查看:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档