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