zoukankan      html  css  js  c++  java
  • 照虎画猫写自己的Spring——自定义注解

    Fairy已经实现的功能

    • 读取XML格式配置文件,解析得到Bean

    • 读取JSON格式配置文件,解析得到Bean

    • 基于XML配置的依赖注入

    所以,理所当然,今天该实现基于注解的依赖注入了。

    基于XML配置文件方式的依赖注入一直是使用依赖注入的标配。使用配置文件让开发变的更加灵活,告别了硬编码和扩展性差的问题。

    但是,随着时间的推移以及大量开发人员的深度使用,越发觉得配置文件显得非常臃肿和复杂。Spring也是如此的敏锐和贴心,给我们带了很多的注解,好比我们每天都要用的@Autowired,这样我们不再需要在XML配置文件中在合适的位置小心翼翼的配置注册你的Bean了。

    尤其是这两年Spring Boot被越来越多的公司使用,倡导无配置文件开发,大量简单易用的注解呈现在开发者面前,@GetMapping、@ComponentScan、@RestController……


    自定义注解

    平时开发,我们能用到很多注解@Override、@Service、@Value,这些都是java或者各个框架定义好的,我们自己也可以声明自定义注解。

    在定义自定义注解之前,需要用到一些元注解,就是java事先定义好的

    • @Target-定义注解的位置,

    表示支持注解的程序元素的种类,

    一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等等

    • @Retention-定义注解的时机,

    表示注解类型保留时间的长短

    它接收RetentionPolicy参数,可能的值有SOURCE, CLASS, 以及RUNTIME。

    • @Documented-

    表示使用该注解的元素应被javadoc或类似工具文档化

    • @Inherited-

    表示一个注解类型会被自动继承

    有了这些知识储备,就可以定义自定义注解JackieAutowired了

    
    @Retention(RetentionPolicy.RUNTIME)
    
    @Target({ElementType.METHOD, ElementType.FIELD})
    
    public @interface JackieAutowired {
    
        public String name() default "";
    
    }
    
    
    • RetentionPolicy.RUNTIME表示注解在运行时生效

    • Target有两个值,分别表示作用在方法和属性上

    • 声明了一个属性name,default表示默认值,可为空

    基于注解实现依赖注入

    在FairyApplicationContext中添加对于注解的处理

    
    public FairyApplicationContext(String configLocation, ParseType parseType) {
    
            // 加载xml并转换为BeanDefinition
    
            this.loadConfigFile(configLocation, parseType);
    
            // 实例化BeanDefinition
    
            this.instanceBeanDefinitions();
    
            // 基于注解的依赖注入
    
            this.annotationInject();
    
            // 实现依赖注入
    
            this.injectObject();
    
        }
    
    

    使用在方法上的注解处理

    
    private void annotationInject() {
    
            for (String beanName : instanceBeans.keySet()) {
    
                Object bean = instanceBeans.get(beanName);
    
                if (bean != null) {
    
                    try {
    
                        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
    
                        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    
                        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
    
                            // 获取Setter方法
    
                            Method setter = propertyDescriptor.getWriteMethod();
    
                            if (setter != null && setter.isAnnotationPresent(JackieAutowired.class)) {
    
                                JackieAutowired jackieAutowired = setter.getAnnotation(JackieAutowired.class);
    
                                Object value = null;
    
                                if (jackieAutowired != null && StringUtils.isNotEmpty(jackieAutowired.name())) {
    
                                    value = instanceBeans.get(jackieAutowired.name());
    
                                } else {
    
                                    value = instanceBeans.get(propertyDescriptor.getName());
    
                                    if (value == null) {
    
                                        for (String key : instanceBeans.keySet()) {
    
                                            if (propertyDescriptor.getPropertyType().isAssignableFrom(instanceBeans.get(key).getClass())) {
    
                                                value=instanceBeans.get(key);//类型匹配的话就把此相同类型的
    
                                                break;//找到了类型相同的bean,退出循环
    
                                            }
    
                                        }
    
                                    }
    
                                }
    
                                setter.setAccessible(true);
    
                                try {
    
                                    setter.invoke(bean, value);
    
                                } catch (Exception e) {
    
                                    LOG.error("invoke setter invoke failed", e);
    
                                }
    
                            }
    
                        }
    
                    } catch (Exception e) {
    
                        LOG.error("invoke getBean failed", e);
    
                    }
    
                }
    
            }
    
        }
    
    
    • 通过内省的方式获取Bean的属性和getter setter方法和上篇一致

    • 得到setter方法,通过setter.isAnnotationPresent(JackieAutowired.class)判断该方法上是否有注解JackieAutowired

    • 读取注解JackieAutowired属性name,如果能够取到值,则直接从Bean的上下文map集合中取出

    • 如果属性name值没取到,则读取当前Bean属性的名称,然后依次根据名称或类型进行加载需要注入的Bean

    • 通过反射的方式注入实例化后的Bean,完成依赖注入

    添加JackieAutowired注解

    这时候只需要在FairyServiceImpl类中的setFairyDao方法上加上注解JackieAutowired即可

    
    public FairyDao getFairyDao() {
    
            System.out.println("===getFairyDao===: " + fairyDao1.toString());
    
            return fairyDao1;
    
        }
    
        @JackieAutowired
    
        public void setFairyDao(FairyDao fairyDao1) {
    
            System.out.println("===setFairyDao===: " + fairyDao1.toString());
    
            this.fairyDao1 = fairyDao1;
    
        }
    
    

    配置文件声明如下

    
    <beans>
    
        <bean id="fairyService" class="com.jackie.fairy.bean.impl.FairyServiceImpl">
    
        </bean>
    
        <bean id="fairyDao" class="com.jackie.fairy.bean.impl.FairyDaoImpl">
    
        </bean>
    
    </beans>
    
    

    这时候,我们不再需要在FairyServiceImpl中声明property属性,也不用声明ref指向fairyDao了,因为JackieAutowired已经能够处理他们之间的依赖关系并进行注入了。

    运行结果

    使用在属性上的注解处理

    
    private void annotationInject() {
    
            for (String beanName : instanceBeans.keySet()) {
    
                Object bean = instanceBeans.get(beanName);
    
                if (bean != null) {
    
                    try {
    
                        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
    
                        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    
                        Field[] fields = bean.getClass().getDeclaredFields();
    
                        for (Field field : fields) {
    
                            if (field.isAnnotationPresent(JackieAutowired.class)) {
    
                                JackieAutowired jackieAutowired = field.getAnnotation(JackieAutowired.class);
    
                                Object value = null;
    
                                if (jackieAutowired != null && StringUtils.isNotEmpty(jackieAutowired.name())) {
    
                                    value = instanceBeans.get(jackieAutowired.name());
    
                                } else {
    
                                    value = instanceBeans.get(field.getName());
    
                                    if (value == null) {
    
                                        for (String key : instanceBeans.keySet()) {
    
                                            if (field.getType().isAssignableFrom(instanceBeans.get(key).getClass())) {
    
                                                value = instanceBeans.get(key);
    
                                                break;
    
                                            }
    
                                        }
    
                                    }
    
                                }
    
                                field.setAccessible(true);
    
                                try {
    
                                    field.set(bean, value);
    
                                } catch (Exception e) {
    
                                    LOG.error("invoke field.set failed", e);
    
                                }
    
                            }
    
                        }
    
                    } catch (Exception e) {
    
                        LOG.error("invoke getBean failed", e);
    
                    }
    
                }
    
            }
    
        }
    
    
    • 通过反射拿到Bean的所有字段Field数组

    • 和上面类似,通过field.isAnnotationPresent(JackieAutowired.class)判断相应的字段上是否有JackieAutowired注解

    • 找到JackieAutowired注解后,读取其name属性,如果有值,则进入上下文map中查找相应的bean实例

    • 如果没有配置name属性,则通过属性的名称进入上下文map中根据名称和类型进行遍历,找到相应的bean实例

    * 通过反射的方式注入实例化后的Bean,完成依赖注入

    添加JackieAutowired注解

    这时候添加的位置在属性上

    
    @JackieAutowired
    
    private FairyDao fairyDao
    
    

    其他配置同上一种情况,最终运行结果正常。

    至此,Fairy实现了基于JackieAutowired注解的依赖注入。
    项目地址https://github.com/DMinerJackie/fairy

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

    我的博客即将同步至腾讯云+社区,邀请大家一同入驻。

  • 相关阅读:
    [git] git 的基本认知
    [Java] Java IO Files
    [Java] Java IO 概况
    [Java] JavaMail 发送带图片的 html 格式的邮件
    [Java] HashMap 导致的高 CPU 使用率
    [Struts] Hello World Demo
    [Hibernate] 注解映射例子
    [Hibernate] List 映射例子
    cmd的xcopy命令
    wpf custom control
  • 原文地址:https://www.cnblogs.com/bigdataZJ/p/Fairy3.html
Copyright © 2011-2022 走看看