一、前言

最近使用Spring里面的依赖注入,比如StudentServiceImple2.java代码:

package di.service.imple;

import com.mengya.spring.annotation.MyResource;

import di.dao.StudentDao;
import di.service.StudentService; public class StudentServiceImple2 implements StudentService { @MyResource
private StudentDao stuDao; public void save() {
stuDao.add();
} }

就是向属性stuDao注入一个StudengDao对象,那么这是怎么实现的哪?如果想要彻底了解它,需要提前知道两个知识点,反射和注解:http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html

二、实现过程

据我的理解,他的过程如下:

  1. 扫描XML文件,把里面的Bean实例对象保存在HashMap内,对应的KEY的值即为BeanId
  2. 扫描JAVA类的注解,找到需要注入的地方,比如:@MyResource
  3. 将已经保存的Bean实例赋值给注解对应的属性

那么让我们根据事例来逐条分析。

三、详细分析

3.1 扫描XML文件

这需要三个文件来实现:bean3.xml、MySpringAnnotationTest.java、MengyaClassPathXMLApplicationContext.java

bean3.xml

这就是一个普普通通的xml文件,里面包含了stuDao和stuService两个Bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="stuDao" class="di.dao.imple.StudentDaoImple"></bean> <bean id="stuService" class="di.service.imple.StudentServiceImple2"></bean> </beans>

MySpringAnnotationTest.java

这个是解析XML的一个类,没啥说的

package com.test;

import com.mengya.context.MengyaClassPathXMLApplicationContext;

import di.service.StudentService;

public class MySpringAnnotationTest {
public static void main(String[] args) {
MengyaClassPathXMLApplicationContext ctx = new MengyaClassPathXMLApplicationContext("beans3.xml");
StudentService stuService = (StudentService) ctx.getBean("stuService");
stuService.save();
}
}

MengyaClassPathXMLApplicationContext.java

这个是重点的核心了。

injectObject() 函数是将stuDao和stuService两个Bean保存在属性sigletons里面

annotationInject() 函数是查找StudentServiceImple2类内的@Resource注解,并将StudentDao注入给stuDao属性

package com.mengya.context;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import com.mengya.spring.annotation.MyResource;
import com.mengya.spring.bean.BeanDefinition;
import com.mengya.spring.bean.PropertyDefinition;
import com.mengya.spring.util.ReadXMLUtil; public class MengyaClassPathXMLApplicationContext { private List<BeanDefinition> beanDefintionList = null; private Map<String, Object> sigletons = new HashMap<String, Object>(); /**
* 容器初始化时 传入配置文件名称 读取配置文件..实例化bean
*
* @param configFileName
*/
public MengyaClassPathXMLApplicationContext(String configFileName) {
beanDefintionList = ReadXMLUtil.readXMLBeanDefintion(configFileName);
this.instanceBeans();
this.injectObject();
this.annotationInject();
} /**
* 注解实现依赖注入
*
*/
private void annotationInject() {
/**
* 遍历bean,获取bean里的所有属性描述对象,遍历属性描述对象数组.
* ---获取属性的setter方法.如果该属性setter方法存在,判断方法上是否有MyResource注解,
* 如果有,获取注解对象,通过注解对象获取name值
* 如果name值存在:根据name值查找Map中是否有该名称的bean,如果有,调用该属性的setter方法执行注入.
* 如果name值不存在:获取该属性的名称,从map中查找是否有此名称的bean. 如果有:调用setter方法注入
* 没有:获取该属性的类型,遍历map查找map中是否有和此属性类型一致的bean,如果有,则执行注入
*
* ---获取该属性,判断该属性上是否有MyResource注解 如果有:获取该注解的对象,通过该对象获取name值
* 如果name值存在:根据name值查找map中是否有该bean如果有则执行注入
* 如果name值不存在:获取该属性的名称,查找map中是否有该名称的bean 如果有:执行注入 没有:获取该属性的类型
* 遍历map中判断是否有和该类型一致的bean
*
*/
for (String beanName : sigletons.keySet()) {
System.out.println("beanName: " + beanName);
Object bean = getBean(beanName);
System.out.println("bean:" + bean.toString());
if (null != bean) {
try {
// 获取所有的属性
PropertyDescriptor pd[] = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : pd) {
// 获取set方法
Method setter = descriptor.getWriteMethod();
// 若在set方法设置了MyResource注解
if (null != setter && setter.isAnnotationPresent(MyResource.class)) {
MyResource myResource = setter.getAnnotation(MyResource.class);
String diName = null;
Object diObject = null;
// 设置了name属性值
if (null != myResource.name() && !"".equals(myResource.name())) {
diName = myResource.name();
} else {// 按默认的属性值装配置
diName = descriptor.getName();
}
diObject = getBean(diName);
setter.setAccessible(true);
setter.invoke(bean, diObject);
}
}
// 获取所有字段
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyResource.class)) {
MyResource myResource = field.getAnnotation(MyResource.class);
String diName = null;
Object diObject = null;
// 设置了name属性值
if (null != myResource.name() && !"".equals(myResource.name())) {
diName = myResource.name();
} else {// 按默认的属性值装配置
diName = field.getName();
}
diObject = getBean(diName);
field.setAccessible(true);
field.set(bean, diObject);
}
} } catch (Exception e) {
e.printStackTrace();
}
}
}
} /**
* 注入Bean
*
*/
private void injectObject() {
for (BeanDefinition beanDefinition : beanDefintionList) {
Object obj = getBean(beanDefinition.getId());
if (null != obj) {
List<PropertyDefinition> propertys = beanDefinition.getPropertys();
if (null != propertys && propertys.size() > 0) {
try {
//通过Java的内省机制获取到对象中所有属性的描述信息
PropertyDescriptor[] ps = Introspector.getBeanInfo(obj.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : ps) {
for (PropertyDefinition property : propertys) {
//判断XML文件中解析出来的属性和对象中的属性名称是否一样
if (descriptor.getName().equals(property.getName())) {
if (null != property.getRef() && !"".equals(property.getRef())) {
Object diObject = getBean(property.getRef());
descriptor.getWriteMethod().invoke(obj, diObject);
} else {
BeanUtils.setProperty(obj, property.getName(), property.getValue());
} }
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} /**
* 实例化Bean
*
*/
private void instanceBeans() {
for (BeanDefinition beanDefinition : beanDefintionList) {
try {
Object obj = Class.forName(beanDefinition.getClassName()).newInstance();
this.sigletons.put(beanDefinition.getId(), obj);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("this.sigletons: " + this.sigletons.toString());
} /**
* 获取Bean实例
*
* @param beanName
* @return
*/
public Object getBean(String beanName) {
return this.sigletons.get(beanName);
} }

代码附件下载

JAVA的Spring注入机制事例详解的更多相关文章

  1. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  2. Java SPI机制实战详解及源码分析

    背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...

  3. Java Spring cron表达式使用详解

    Java Spring cron表达式使用详解   By:授客 QQ:1033553122 语法格式 Seconds Minutes Hours DayofMonth Month DayofWeek ...

  4. 牛客网 Java 工程师能力评估 20 题 - 详解

    牛客网 Java 工程师能力评估 20 题 - 详解 不知在看博客的你是否知道 牛客网,不知道就太落后了,分享给你 : 牛客网 此 20 题,绝对不只是 20 题! 免责声明:本博客为学习笔记,如有侵 ...

  5. Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程

    上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...

  6. Spring 2.5配置文件详解(转)

    http://book.51cto.com/art/201004/193743.htm 6.2.3  Spring 2.5配置文件详解 Spring配置文件是用于指导Spring工厂进行Bean生产. ...

  7. Spring IoC @Autowired 注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 我们平时使用 Spring 时,想要 依赖 ...

  8. Spring IoC 公共注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 什么是公共注解?公共注解就是常见的Java ...

  9. Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计

    在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...

随机推荐

  1. java.lang.ClassCastException: android.widget.RelativeLayout$LayoutParams cannot be cast to android.widget.AbsListView$LayoutParams

    java.lang.ClassCastException: android.widget.RelativeLayout$LayoutParams cannot be cast to android.w ...

  2. python测试开发django-39.xadmin详情页面布局form_layout

    前言 xadmin的详情页面默认是一行展示一个字段,可以使用form_layout对详情页面的布局重新设计. 可以设置必填和非必填字段,也可以设置不显示,不可以编辑的字段. models模块 先在mo ...

  3. 浏览器中回车(Enter)和刷新的区别是什么?[转载]

    在浏览器中回车和F5刷新有什么区别那?今天就来说说:浏览器中回车(Enter)和刷新的区别是什么? 这点事. 概论: 1.回车在 Expires有效的时候,是不会去请求服务器的,打开调试看到的请求也只 ...

  4. 实用ExtJS教程100例-001:开天辟地的Hello World

    ExtJS功能繁多,要想彻底的了解确实很困难.作为初学者,如何能找到一条快速的通道呢?我觉得,如果你有Javascript的基础,那就不要惧怕ExtJS的复杂,从动手开始,遇到问题,解决问题,积累经验 ...

  5. 使用DDMS中的内存监测工具Heap来优化内存

    最近在做一个照片墙的应用,涉及到很多知识,其中难点在于如何应对数量庞大的图片,这就涉及到内存管理的知识了.今天介绍的工具是DDMS中自带的Heap,它可以显示出当前引用占用的内存,剩余的内存等信息.下 ...

  6. 无需SherlockActionbar的SlidingMenu使用详解(一)——通过SlidingMenu设置容器并解决滑动卡顿的问题

    想必很多人都听过这个开源框架,一年前真的是风靡一时.只是它的配置较为繁琐,还需要sherlockActionbar的支持,我这里下载了最新的开源库,并且在实际用套用了AppCompat的官方库,这样就 ...

  7. securecrt 多窗口执行命令

    首先在SecureCRT里同时打开多个服务器session(必须是有相同目录结构) 选择菜单栏View –>Chat Windows 对号,此时所有服务器连接下方应该有个空白的部分 在空白的部分 ...

  8. 布拉格捷克理工大学研究团队:Prisma进化版

    原文链接  还记得 Prisma 吗?就是能把拍摄的照片转化为各种名画风格的修图软件,神经网络的深度学习后,想要波普还是梵高风的图片都不在话下. 现在,它的进化版本来了.这回是布拉格捷克理工大学的研究 ...

  9. Guava CaseFormat

    概述 CaseFormat用来转换各种不同的编程语言间的变量名命名格式, 主要用到的方法只有一个 CaseFormat.to(CaseFormat from, String s) CaseFormat ...

  10. SQL Server:使用 PIVOT 行转列和 UNPIVOT 列转行

    ylbtech-SQL Server:使用 PIVOT 行转列和 UNPIVOT 列转行 可以使用 PIVOT 和 UNPIVOT 关系运算符将表值表达式更改为另一个表.PIVOT 通过将表达式某一列 ...