zoukankan      html  css  js  c++  java
  • 自己动手写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方法调用成功

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

  • 相关阅读:
    MySQL:按前缀批量删除表格
    用Parallel.For()和Parallel.For<TLocal>()方法实现并行运行迭代
    用资源管理器右键编译 Visual Studio 解决方案文件
    C#和C++中char类型的区别
    传递给系统调用的数据区域太小。 (异常来自 HRESULT:0x8007007A)
    NHibernate之映射文件配置说明
    warning,C4996,sprintf,deprecated,C4996,strcpy,C4996,strcat
    OPC服务器开发浅谈 — 服务器模型(转)
    进程内COM与进程外COM
    fork()函数
  • 原文地址:https://www.cnblogs.com/shunyang/p/3337700.html
Copyright © 2011-2022 走看看