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

在之前的文章自己动手写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来新建),如:

 package com.juit;

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

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

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

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

     public YhdClassPathXmlApplicationContext(String fileName){

         //1.读取spring的配置文件
this.readXml(fileName);
//2.实例化bean
this.instanceBeans();
//3.注解方式注入依赖对象
this.annotationInject();
//4.实现对依赖对象的注入功能
this.injectObject();
}

接下来完成annotationInject这个功能:

 /**
* 注解方式注入
*
* Administer
* 2013-9-24 下午8:08:29
*/
private void annotationInject() {
//遍历所有的bean
for (String beanName : sigletons.keySet()) {
Object bean=sigletons.get(beanName);//获取需要注入的bean
if (bean != null) {
try {
//先对属性进行处理,即setter方法上标识有注解的
BeanInfo info = Introspector.getBeanInfo(bean.getClass());//通过类Introspector的getBeanInfo方法获取对象的BeanInfo 信息
PropertyDescriptor[] pds = info.getPropertyDescriptors();//获得 bean所有的属性描述
for (PropertyDescriptor pd : pds) {
Method setter=pd.getWriteMethod();//获取属性的setter方法
//属性存在setter方法,并且setter方法存在YhdResource注解
if (setter != null && setter.isAnnotationPresent(YhdResource.class)) {
YhdResource resource=setter.getAnnotation(YhdResource.class);//取得setter方法的注解
Object value=null;
//注解的name属性不为空
if (resource != null && resource.name() != null && !"".equals(resource.name())) {
value=sigletons.get(resource.name());//根据注解的name属性从容器中取出来
}else {//注解上没有标注name属性
value=sigletons.get(pd.getName());//根据属性的名称取集合中寻找此名称的bean
if (value == null) {
//没找到,遍历所有所有的bean,找类型相匹配的bean
for (String key : sigletons.keySet()) {
//判断类型是否匹配
if (pd.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) {
value=sigletons.get(key);//类型匹配的话就把此相同类型的
break;//找到了类型相同的bean,退出循环
}
}
}
}
setter.setAccessible(true);//保证setter方法可以访问私有
try {
setter.invoke(bean,value);//把引用对象注入到属性中
} catch (Exception e) {
e.printStackTrace();
}
}
} //再对字段进行处理,即对字段上标识有注解
Field[] fields=bean.getClass().getDeclaredFields();//取得声明的所有字段
for (Field field : fields) {
//判断字段上是否存在注解,若存在
if (field.isAnnotationPresent(YhdResource.class)) {
YhdResource resource=field.getAnnotation(YhdResource.class);//取得字段上的注解
Object value=null;
//字段上存在注解,并且字段上注解的name属性不为空
if (resource != null && resource.name() != null && !resource.name().equals("")) {
value=sigletons.get(resource.name());//依赖对象为根据此注解的name属性指定的对象
}else {
value=sigletons.get(field.getName());//根据字段的名称到容器中寻找bean
if (value == null) {
//没找到,根据字段的类型去寻找
for (String key : sigletons.keySet()) {
//判断类型是否匹配
if (field.getType().isAssignableFrom(sigletons.get(key).getClass())) {
value=sigletons.get(key);//类型匹配的话就把此相同类型的
break;//找到了类型相同的bean,退出循环
}
}
}
}
field.setAccessible(true);//设置允许访问私有字段
try {
field.set(bean, value);//将值为value的注入到bean对象上
} catch (Exception e) {
e.printStackTrace();
} }
}
} catch (IntrospectionException e) {
e.printStackTrace();
} }
}
}

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

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

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

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

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

 package com.yangyang.service.impl;

 import com.juit.YhdResource;
import com.yangyang.dao.PersonDao;
import com.yangyang.model.Person;
import com.yangyang.service.PersonService; public class PersonServiceImpl implements PersonService{ public PersonDao getPersonDao() {
return personDao;
}
@YhdResource
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
} @Override
public void savePerson() {
System.out.println("service中的save方法调用成功");
personDao.savePerson();
} }

执行单元测试方法:

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

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

dao中的save方法调用成功

再测试字段上有注解的:

 package com.yangyang.service.impl;

 import com.juit.YhdResource;
import com.yangyang.dao.PersonDao;
import com.yangyang.model.Person;
import com.yangyang.service.PersonService; public class PersonServiceImpl implements PersonService{
@YhdResource
private PersonDao personDao; public PersonDao getPersonDao() {
return personDao;
}
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
} @Override
public void savePerson() {
System.out.println("service中的save方法调用成功");
personDao.savePerson();
} }

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

service中的save方法调用成功

dao中的save方法调用成功

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

自己动手写spring容器(3)的更多相关文章

  1. 自己动手写spring容器(2)

    上篇我们自己写了一个很简单的spring容器,该容器只是做了简单的bean的实例化,并没有spring的核心之一的IOC(依赖注入),也叫做控制反转,这里我就不讲这个的具体含义,不知道的园友可以自行百 ...

  2. 自己动手写spring容器(1)

    毕业刚刚一年多一点,毕业了后也顺利的进入了一家著名的互联网公司,做的是后台系统,用的呢也是SSI(struts2,spring)框架,平时做做项目,也已足够了,但是感觉越来越没动力了,越来越没有激情了 ...

  3. 自己动手写Spring框架--IOC、MVC

    对于一名Java开发人员,我相信没有人不知道 Spring 框架,而且也能够轻松就说出 Spring 的特性-- IOC.MVC.AOP.ORM(batis). 下面我想简单介绍一下我写的轻量级的 S ...

  4. 自己动手编写spring IOC源码

    前言:对于spring IOC概念不是很了解的朋友可以阅读我上一篇博客--轻松理解spring IOC(这两篇博客也是由于我的个人原因导致现在才发布,惭愧啊).通过这篇博客的理解之后,相信大家会对sp ...

  5. [原]容器学习(一):动手模拟spring的IoC

    介绍 学习经典框架的实现原理以及设计模式在其实际中的运用,是非常有必要的,可以让我们更好进行面向对象. 本篇文章就来模拟Spring的IOC功能,明白原理后,可以更好的使用它,进而为进行面向对象提供一 ...

  6. 容器学习(一):动手模拟spring的IoC

    介绍 学习经典框架的实现原理以及设计模式在事实上际中的运用,是很有必要的,能够让我们更好进行面向对象. 本篇文章就来模拟Spring的IOC功能.明确原理后,能够更好的使用它,进而为进行面向对象提供一 ...

  7. 我自横刀向天笑,手写Spring IOC容器,快来Look Look!

    目录 IOC分析 IOC是什么 IOC能够带来什么好处 IOC容器是做什么工作的 IOC容器是否是工厂模式的实例 IOC设计实现 设计IOC需要什么 定义接口 一:Bean工厂接口 二:Bean定义的 ...

  8. 手写Spring,定义标记类型Aware接口,实现感知容器对象

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同事写的代码,我竟丝毫看不懂! 大佬的代码,就像 "赖蛤蟆泡青蛙,张的丑玩 ...

  9. 自己动手开发IOC容器

    前两天写简历.写了一句:精通Spring IoC容器.怎么个精通法?还是自己动手写个IOC容器吧. 什么是IoC(Inversion of Control)?什么是DI(Dependency Inje ...

随机推荐

  1. WPF中嵌入WinForm中的webbrowser控件

    原文:WPF中嵌入WinForm中的webbrowser控件 使用VS2008创建WPF应用程序,需使用webbrowser.从工具箱中添加WPF组件中的webbrowser发现其中有很多属性事件不能 ...

  2. 条形码(JBarcode)

    一世尘梦 少小离家老大回,妖娆尘世,程序唧唧:问君能有几多愁,恰是满屏BUG无处修. 商品条形码(JBarcode) 之前没有使用过这个,现在使用JBarcode生成商品条形码,工作之前的准备工作: ...

  3. 访问Google神器,魔高一尺,道高一丈!

    最近谷歌的IP被大范围的禁用了.身处一个连谷歌都用不了的过度的程序员,深感命运多舛.幸好,魔高一尺,道高一丈.下面是几种可以使用谷歌的方法. 方法一 1)在chrome浏览器中输入:chrome:// ...

  4. Installshield停止操作系统进程的代码--IS5版本适用

    原文:Installshield停止操作系统进程的代码--IS5版本适用 出处:http://www.installsite.org/pages/en/isp_ext.htm这个地址上有不少好东西,有 ...

  5. [SQL]死锁处理语句

    原文:[SQL]死锁处理语句 引言 今天在群里看到分享的解决死锁的sql语句,就想着这东西以后肯定用的着,就下载下来,在这里记录一下,以后查找也方便. SQL SET QUOTED_IDENTIFIE ...

  6. ASP.NET 5+EntityFramework 7

    爱与恨的抉择:ASP.NET 5+EntityFramework 7   EF7 的纠缠 ASP.NET 5 的无助 忘不了你的好 一开始列出的这个博文大纲,让我想到了很久之前的一篇博文:恋爱虽易,相 ...

  7. sb2-admin

    近期开发中遇到的问题总结   最近准备把后台管理系统重新设计开发下,使用了bootstrap,在网上找了个漂亮的后台模板:sb2-admin,在使用中遇到了不少问题,总结下,以免以后忘记. 1.EF5 ...

  8. codeforces #257 C称号Jzzhu and Chocolate

    职务地址:http://codeforces.com/contest/450/problem/C 这次CF的时候绝壁脑残了. ..A题和C题都出现了脑残失误... 唯一一个AC的B题还是被HACK了. ...

  9. easyui datagrid 前台分页

    核心方法 //分页data function pagerFilter(data, pagegrid) { if (typeof data.length == 'number' && t ...

  10. Mysql高级之索引

    原文:Mysql高级之索引 索引:是针对数据所建立的目录. 作用: 可以加快查询速度 负面影响: 降低了增删改的速度. 索引的创建原则: 1:不要过度索引 2:在where条件最频繁的列上加.在重复度 ...