你的位置:首页 > Java教程

[Java教程]模拟实现Spring中的注解装配


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

在Spring中,

老版本中纯

在配置文件中配置如下:

<bean id="userDao" ></bean><bean id="userService" >   <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" ></bean><bean id="userService" > </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注解,实现在

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

<bean id="userDao1" ></bean><bean id="userDao2" ></bean><bean id="userService" ></bean>

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

@Autowiredprivate UserDao userDao1;@Autowiredprivate 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})@Documentedpublic @interface MyAutowired {	public String name() default "";		public String value() default "";}

定义配置文件:

<?

其中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();	}}

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

import java.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 FzClassPath

 

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

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.首先通过Read

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

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

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

最后调用实现如下:

FzClassPath

输出结果: