模拟实现Spring中的注解装配

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

本文原创,地址为http://www.cnblogs.com/fengzheng/p/5037359.html

在SPRing中,xml文件中的bean配置是实现Spring IOC的核心配置文件,在早版本的Spring中,只能基于XML配置文件,配置各个对象之间的依赖关系。在Spring 2.5以后出现了注解,使用注解结合XML的方式,简化了XML配置的复杂度。

老版本中纯XML配置实现IOC

在配置文件中配置如下:

<bean id="userDao" class="com.springapp.mvc.dao.UserDao">

</bean>

<bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl">

<property name="userDao" ref="userDao"></property>

</bean>

UserServiceImpl的实现如下:

public class UserServiceImpl implements UserService {

public UserDao getUserDao() {

return userDao;

}

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

private UserDao userDao;

public User getUserById(int id){

return userDao.getUserById(id);

}

public int getUserCount(){

return userDao.getUserCount();

}

}

配置的意思是:<property name="userDao" ref="userDao"></property>这行配置是为UserServiceImpl类中的userDao指定userDao这个bean,这样在UserServiceImpl类中调用userDao的方法,其实就是调用com.springapp.mvc.dao.UserDao的方法。

结合注解的实现方式

配置文件简化如下:

<bean id="userDao" class="com.springapp.mvc.dao.UserDao">

</bean>

<bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl">

</bean>

UserServiceImpl的实现如下:

public class UserServiceImpl implements UserService {

@Autowired

private UserDao userDao;

public User getUserById(int id){

return userDao.getUserById(id);

}

public int getUserCount(){

return userDao.getUserCount();

}

}

利用@Autowired注解,实现在xml中<property name="userDao" ref="userDao"></property>的配置,从而实现了自动注入。@Autowired自动注入的规则为byType,意思就是按照被注解字段的类型和xml中配置的bean的类型相匹配,即在UserServiceImpl 类中的userDao为UserDao类型,匹配的时候会在所有bean中查找类型同样为UserDao的bean。

那么既然是按照类型匹配,如果存在两个相同类型的bean呢,这时候,就会启用第二个匹配规则ByName,即根据字段的名字来匹配相同id的bean。如下XML配置:

<bean id="userDao1" class="com.springapp.mvc.dao.UserDao">

</bean>

<bean id="userDao2" class="com.springapp.mvc.dao.UserDao">

</bean>

<bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl">

</bean>

那么在UserServiceImpl类中应该使用以下这种方式来自动注入:

@Autowired

private UserDao userDao1;

@Autowired

private UserDao userDao2;

这样好像不是很灵活的样子,看起来有些不爽,有没有办法可以指定要匹配的bean呢?没错,是有的。可以使用这样的方式,通过@Autowired和@Qualifier相结合的方式,@Qualifier后跟的参数就是bean的名称:

@Autowired

@Qualifier("userDao1")

private UserDao userDao;

还有更常用的方式,@Resource:

@Resource(name = "userDao1")

private UserDao userDao;

关于注解IOC的内容可以参看这篇文章,写的很详细。

注解在Spring中的用法讲完了,下面来自己实习一个简单的类,来模拟Spring利用注解实现IOC的原理。

Spring IOC实现原理

1.首先Spring根据bean配置文件,收集所有bean的实例;

2.Spring根据配置文件中的context:component-scan,扫描需要被注入的包(递归包中的所有待注入类);

3.扫描待注入类时,判断是否有特定的注解(例如@Autowired、@Resource),如果有,则进行第4步,注入;

4.注入:根据注解类型或参数,利用反射,为被注解的字段或属性等设置对应的bean实例。

以上是我个人理解,可能和Spring真正的实现有些出入。

模拟利用注解实现注入

这里要定义一个类似于@Resource的注解,命名为@MyAutowired,定义如下:

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE,ElementType.FIELD})

@Documented

public @interface MyAutowired {

public String name() default "";

public String value() default "";

}

定义配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:context="http://www.springframework.org/schema/context">

<context:component-scan id="test" class="fengzheng.Test"/>

<bean id="tomoto" class="fengzheng.Tomoto"></bean>

</beans>

其中bean和Spring中bean的定义是一样的,而context:component-scan在Spring是定义属性base-package,之后根据这个属性,扫描这个包下的所有类,这里为做演示,也定义为一个类,之后会根据这个class属性,对这个类进行注入。

配置文件中的tomoto bean的定义:

package fengzheng;

public class Tomoto {

public void SayHello(){

System.out.println("hello I'm tomoto");

}

}

配置文件中fengzheng.Test类定义,这个即为要被注入的类:

package fengzheng;

import fengzheng.fzAnnotation.MyAutowired;

public class Test {

@MyAutowired(name = "tomoto")

private Tomoto tomoto;

public void Say(){

tomoto.SayHello();

}

}

核心注解分析并实现注入的类:

importjava.lang.reflect.Field;

import java.net.URL;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import org.dom4j.Document;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import fengzheng.fzAnnotation.MyAutowired;

import java.util.stream.*;

public class FzClassPathXMLapplication{

//xml配置文件中 bean定义的集合

private List<BeanDefine> beanList = new ArrayList<BeanDefine>();

// 存储bean实例和bean的id的对应关系 以便可以根据注解名称找到对应的实例

Map<String, Object> beanInstanceList = new HashMap<String, Object>();

//xml配置文件中 被扫描类的定义的集合 在Spring框架中 直接扫描一个或多个包

List<ScanDefine> scanList = new ArrayList<ScanDefine>();

// 存储被扫描的待注入的实体集合

Map<String, Object> annotationInstanceList = new HashMap<String, Object>();

public FzClassPathXMLApplication(String xmlName) {

ReadXml(xmlName);

//实例化所有定义的bean

BeanInstance();

//实例化所有的待注入类

ScanClassInstance();

//开始根据注解实现依赖注入

InjectAnnotation();

}

/**

* 读取配置文件 收集bean集合和待注入类的集合

* @param xmlFileName

*/

public void ReadXml(String xmlFileName) {

URL xmlPath = this.getClass().getClassLoader().getResource(xmlFileName);

System.out.println(xmlPath);

SAXReader reader = new SAXReader();

try {

Document dom = reader.read(xmlPath);

Element root = dom.getRootElement();

List<Element> iters = root.elements();

Stream<Element> beans = iters.stream().filter(bean -> bean.getName().equals("bean"));

Iterator<Element> iterBeans = beans.iterator();

while (iterBeans.hasNext()) {

Element bean = iterBeans.next();

String id = bean.attributeValue("id");

String clsName = bean.attributeValue("class");

//System.out.println("id:" + id + "\nclass:" + clsName);

BeanDefine bd = new BeanDefine();

bd.setId(id);

bd.setClsName(clsName);

beanList.add(bd);

}

Stream<Element> scanClasses = iters.stream().filter(scan -> scan.getName().equals("component-scan"));

//iters.stream().forEach(scan -> System.out.println(scan.getName()));

Iterator<Element> iterScans = scanClasses.iterator();

while (iterScans.hasNext()) {

Element scan = iterScans.next();

String id = scan.attributeValue("id");

String clsName = scan.attributeValue("class");

ScanDefine sd = new ScanDefine();

sd.setId(id);

sd.setClassName(clsName);

scanList.add(sd);

}

System.out.println("scanList.size():"+scanList.size());

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 收集bean实例

*/

private void BeanInstance() {

for (BeanDefine bd : beanList) {

try {

Object beanInstance = Class.forName(bd.getClsName()).newInstance();

System.out.println(beanInstance.getClass().getName());

beanInstanceList.put(bd.getId(), beanInstance);

} catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 收集被扫描的待注入的类的实例

*/

private void ScanClassInstance(){

for(ScanDefine sd:scanList){

try {

Object scanInstance = Class.forName(sd.getClassName()).newInstance();

System.out.println(scanInstance.getClass().getName());

annotationInstanceList.put(sd.getId(), scanInstance);

} catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 循环遍历待注入的类

*/

public void InjectAnnotation() {

Iterator<Map.Entry<String, Object>> iters = annotationInstanceList.entrySet().iterator();

while (iters.hasNext()) {

Map.Entry<String, Object> iter = iters.next();

Object scanInstance = iter.getValue();

InjectField(scanInstance);

}

}

/**

* 注入:把需要注入类中的注解为MyAutowired的字段 注入bean实例

* @param injectClass

*/

private void InjectField(Object injectClass) {

try {

Field[] fields = injectClass.getClass().getDeclaredFields();

for (Field field : fields) {

if (field != null && field.isAnnotationPresent(MyAutowired.class)) {

System.out.println(field.getName());

MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);

String beanName = myAutowired.name();

Object value = null;

if (beanName != null && !beanName.equals("")) {

value = beanInstanceList.get(beanName);

} else {

Class<?> fType = field.getType();

for (String key : beanInstanceList.keySet()) {

if (fType.isAssignableFrom(beanInstanceList.get(key).getClass())) {

value = beanInstanceList.get(key);

break;

}

}

}

field.setaccessible(true);

field.set(injectClass, value);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

public Object getScan(String scanName){

return this.annotationInstanceList.get(scanName);

}

}

注解处理类中用到的两个实体类:

package fengzheng.simpleSpring;

public class BeanDefine {

private String id;

private String clsName;

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getClsName() {

return clsName;

}

public void setClsName(String clsName) {

this.clsName = clsName;

}

}

package fengzheng.simpleSpring;

public class ScanDefine {

public String id;

public String className;

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getClassName() {

return className;

}

public void setClassName(String className) {

this.className = className;

}

}

对这段程序逻辑的解释:

1.首先通过ReadXml()方法读取配置文件beans.xml,找到其中的bean节点和context:component-scan节点,然后把bean节点实例化为BeanDefine,并加入beanList集合,把component-scan节点实例化为ScanDefine,并加入scanList集合;

2.通过BeanInstance()方法,把配置文件中的bean都实例化存储到Map集合beanInstanceList中;

3.通过ScanClassInstance()方法,把配置文件中的component-scan节点(即待注入的类)实例化并处处到Map集合annotationInstanceList中;

4.通过InjectAnnotation()方法,遍历annotationInstanceList集合,为其中的被@MyAutowired注解的字段赋值(即对应的bean的实例)

最后调用实现如下:

FzClassPathXMLApplication ctx = new FzClassPathXMLApplication("beans.xml");

Test test =(Test) ctx.getScan("test");

test.Say();

输出结果:

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航