自己动手写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方法调用成功

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

原文地址:https://www.cnblogs.com/shunyang/p/3337700.html