• 自己动手写spring容器(3)


    好久没有写博客了,今天闲下来将之前未完成的表达出来。

    在之前的文章自己动手写spring容器(2)中完成了对spring的依赖注入的实现,这篇将会介绍spring基于注解的依赖注入的实现。

    在一般的Java开发中,最常接触到的可能就是@Override@SupressWarnings这两个注解了。使用@Override的时候只需要一个简单的声明即可。这种称为标记注解(marker annotation ),它的出现就代表了某种配置语义。而其它的注解是可以有自己的配置参数的。配置参数以名值对的方式出现。使用 @SupressWarnings的时候需要类似@SupressWarnings({"uncheck", "unused"})这样的语法。在括号里面的是该注解可供配置的值。由于这个注解只有一个配置参数,该参数的名称默认为value,并且可以省略。而花括号则表示是数组类型。在JPA中的@Table注解使用类似@Table(name = "Customer", schema = "APP")这样的语法。从这里可以看到名值对的用法。在使用注解时候的配置参数的值必须是编译时刻的常量。

    从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML文件移到了代码本身之中,在一个地方管理和维护。

    在一般的开发中,只需要通过阅读相关的API文档来了解每个注解的配置参数的含义,并在代码中正确使用即可。在有些情况下,可能会需要开发自己的注解。注解的定义有点类似接口。

    首先通过开发工具向导(本文是eclipse)来生成一个注解类(通过New->Annotation来新建),如:

     1 package com.juit;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 @Retention(RetentionPolicy.RUNTIME)//注解处理在运行时刻
     9 @Target({ElementType.FIELD,ElementType.METHOD})//对字段和方法使用注解
    10 public @interface YhdResource {
    11     public String name() default "";//注解里面只能声明属性,不能声明方法,声明属性的方式比较特殊:
    12                             //语法格式为:数据类型 属性() default 默认值(默认值是可选的); 如:Stringvalue();
    13 }

    注解中定义见注释,详细的注解开发中一些量的含义大家可以百度去

     要让注解实现对依赖对象的注入,必须为注解实现处理器。

     在模拟spring行为的类中加入对注解的处理,

     1     public YhdClassPathXmlApplicationContext(String fileName){
     2         
     3         //1.读取spring的配置文件
     4         this.readXml(fileName);
     5         //2.实例化bean
     6         this.instanceBeans();
     7         //3.注解方式注入依赖对象
     8         this.annotationInject();
     9         //4.实现对依赖对象的注入功能
    10         this.injectObject();
    11     }

    接下来完成annotationInject这个功能:

     1 /**
     2      * 注解方式注入
     3      * 
     4      * Administer
     5      * 2013-9-24 下午8:08:29
     6      */
     7     private void annotationInject() {
     8         //遍历所有的bean
     9         for (String beanName : sigletons.keySet()) {
    10             Object bean=sigletons.get(beanName);//获取需要注入的bean
    11             if (bean != null) {
    12                 try {
    13                     //先对属性进行处理,即setter方法上标识有注解的
    14                     BeanInfo info = Introspector.getBeanInfo(bean.getClass());//通过类Introspector的getBeanInfo方法获取对象的BeanInfo 信息  
    15                     PropertyDescriptor[] pds = info.getPropertyDescriptors();//获得 bean所有的属性描述
    16                     for (PropertyDescriptor pd : pds) {
    17                         Method setter=pd.getWriteMethod();//获取属性的setter方法
    18                         //属性存在setter方法,并且setter方法存在YhdResource注解
    19                         if (setter != null && setter.isAnnotationPresent(YhdResource.class)) {
    20                             YhdResource resource=setter.getAnnotation(YhdResource.class);//取得setter方法的注解
    21                             Object value=null;
    22                             //注解的name属性不为空
    23                             if (resource != null && resource.name() != null && !"".equals(resource.name())) {
    24                                 value=sigletons.get(resource.name());//根据注解的name属性从容器中取出来
    25                             }else {//注解上没有标注name属性
    26                                 value=sigletons.get(pd.getName());//根据属性的名称取集合中寻找此名称的bean
    27                                 if (value == null) {
    28                                     //没找到,遍历所有所有的bean,找类型相匹配的bean
    29                                     for (String key : sigletons.keySet()) {
    30                                         //判断类型是否匹配
    31                                         if (pd.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) {
    32                                             value=sigletons.get(key);//类型匹配的话就把此相同类型的
    33                                             break;//找到了类型相同的bean,退出循环
    34                                         }
    35                                     }
    36                                 }
    37                             }
    38                             setter.setAccessible(true);//保证setter方法可以访问私有
    39                             try {
    40                                 setter.invoke(bean,value);//把引用对象注入到属性中
    41                             } catch (Exception e) {
    42                                 e.printStackTrace();
    43                             }
    44                         }
    45                     }
    46                     
    47                     //再对字段进行处理,即对字段上标识有注解
    48                     Field[] fields=bean.getClass().getDeclaredFields();//取得声明的所有字段
    49                     for (Field field : fields) {
    50                         //判断字段上是否存在注解,若存在
    51                         if (field.isAnnotationPresent(YhdResource.class)) {
    52                             YhdResource resource=field.getAnnotation(YhdResource.class);//取得字段上的注解
    53                             Object value=null;
    54                             //字段上存在注解,并且字段上注解的name属性不为空
    55                             if (resource != null && resource.name() != null && !resource.name().equals("")) {
    56                                 value=sigletons.get(resource.name());//依赖对象为根据此注解的name属性指定的对象
    57                             }else {
    58                                 value=sigletons.get(field.getName());//根据字段的名称到容器中寻找bean
    59                                 if (value == null) {
    60                                     //没找到,根据字段的类型去寻找
    61                                     for (String key : sigletons.keySet()) {
    62                                         //判断类型是否匹配
    63                                         if (field.getType().isAssignableFrom(sigletons.get(key).getClass())) {
    64                                             value=sigletons.get(key);//类型匹配的话就把此相同类型的
    65                                             break;//找到了类型相同的bean,退出循环
    66                                         }
    67                                     }
    68                                 }
    69                             }
    70                             field.setAccessible(true);//设置允许访问私有字段
    71                             try {
    72                                 field.set(bean, value);//将值为value的注入到bean对象上
    73                             } catch (Exception e) {
    74                                 e.printStackTrace();
    75                             }
    76                             
    77                         }
    78                     }
    79                 } catch (IntrospectionException e) {
    80                     e.printStackTrace();
    81                 }
    82                 
    83             }
    84         }
    85     }

    方法中分两种情况处理,先对属性进行处理,即对setter方法上含有注解的,然后对字段进行处理,即对字段上含有注解的。

    代码写完了,我们就要进行测试,把beans.xml中配置改为:

    1 <bean id="personService" class="com.yangyang.service.impl.PersonServiceImpl">
    2 </bean>

    然后在业务方法的类中添加注解:

    此处先测试setter方法上有注解的:

     1 package com.yangyang.service.impl;
     2 
     3 import com.juit.YhdResource;
     4 import com.yangyang.dao.PersonDao;
     5 import com.yangyang.model.Person;
     6 import com.yangyang.service.PersonService;
     7 
     8 public class PersonServiceImpl implements PersonService{
     9     
    10     public PersonDao getPersonDao() {
    11         return personDao;
    12     }
    13     @YhdResource
    14     public void setPersonDao(PersonDao personDao) {
    15         this.personDao = personDao;
    16     }
    17 
    18     @Override
    19     public void savePerson() {
    20         System.out.println("service中的save方法调用成功");
    21         personDao.savePerson();
    22     }
    23 
    24 }

    执行单元测试方法:

    1     @Test
    2     public void testInstanceSping() {
    3         YhdClassPathXmlApplicationContext ctx=new YhdClassPathXmlApplicationContext("resources/beans.xml");
    4         PersonService personService=(PersonService)ctx.getBean("personService");
    5         personService.savePerson();
    6     }

    可以看到控制台打印出:service中的save方法调用成功

    dao中的save方法调用成功

    再测试字段上有注解的:

     1 package com.yangyang.service.impl;
     2 
     3 import com.juit.YhdResource;
     4 import com.yangyang.dao.PersonDao;
     5 import com.yangyang.model.Person;
     6 import com.yangyang.service.PersonService;
     7 
     8 public class PersonServiceImpl implements PersonService{
     9     @YhdResource
    10     private PersonDao personDao;
    11     
    12     public PersonDao getPersonDao() {
    13         return personDao;
    14     }
    15     public void setPersonDao(PersonDao personDao) {
    16         this.personDao = personDao;
    17     }
    18 
    19 
    20     @Override
    21     public void savePerson() {
    22         System.out.println("service中的save方法调用成功");
    23         personDao.savePerson();
    24     }
    25 
    26 }

    同样的可以看到控制台打印:

    service中的save方法调用成功

    dao中的save方法调用成功

    这样注解来实现依赖对象的注入就基本上完成了。

  • 相关阅读:
    Fix Installing .NET Framework 3.5 failed Error Code 0x800F0954 on Windows 10
    RHEL8安装五笔输入法
    Enable EPEL and Local Repository on RHEL8
    Why is Yum Replaced by DNF?
    检查Linux服务器是否被攻击的常用命令及方法
    IDEA 主题
    IDEA 如何显示一个类中所有的方法
    Appium 安装以及安装过程中遇到的问题
    Maven 如何发布 jar 包到 Nexus 私库
    java泛型的基本使用
  • 原文地址:https://www.cnblogs.com/shunyang/p/3337700.html
Copyright © 2020-2023  润新知