Spring注解是从2.5版本开始提供的,基于注解的依赖注入功能延续了Spring框架内在IoC容器设计与实现上的一致性。除了依赖关系的“表达”方式上的不同,底层的实现机制基本保持一致。
Spring2.5提供的基于注解的依赖注入方式主要有@Autowired和@Qualifier;下面看看看看他们是如何使用的以及如何实现的?
- @Autowired:byType
package com.mengma.ioc;
public interface PersonService {
public void addPerson();
}
- 构造方法上
package com.mengma.ioc;
public class PersonServiceImpl implements PersonService {
// 定义接口声明
private PersonDao personDao;
// 提供有参构造器,用于依赖注入
@Autowired
public PersonServiceImpl(PersonDao personDao) {
this.personDao = personDao;
}
// 实现PersonService接口的方法
@Override
public void addPerson() {
personDao.add(); // 调用PersonDao中的add()方法
System.out.println("addPerson()执行了...");
}
}
2.属性上
package com.mengma.ioc;
public class PersonServiceImpl implements PersonService{
// 定义接口声明
@Autowired
private PersonDao personDao;
// 实现PersonService接口的方法
public void addPerson() {
personDao.add(); // 调用PersonDao中的add()方法
System.out.println("addPerson()执行了...");
}
}
3.setter或其他方法上
package com.mengma.ioc;
public class PersonServiceImpl implements PersonService{
// 定义接口声明
private PersonDao personDao;
// 实现PersonService接口的方法
@Autowired
public void addPerson() {
personDao.add(); // 调用PersonDao中的add()方法
System.out.println("addPerson()执行了...");
}
}
现在,我们在上面3个地方都可以标注@Autowired,希望被@Autowired标注的依赖能被注入,但是,仅将@Autowired标注于类定义中并不能让Spring的IoC容器自己去找这些注解,然后注入符合条件的依赖对象。容器需要某种方式去了解,哪些对象标注了@Autowired,哪些对应可以作为可选的依赖对象来注入给需要的对象。在考虑使用什么方式实现这一功能之前,我们先比较一下原有的自动绑定功能与使用@Autowired之后产生了哪些差异。
使用自动绑定时,我们需要配置所有相关的bean到容器的配置文件中,然后使用default-autowire或者autowire告知容器,依照这两种属性指定的绑定方式,将容器中各个对象绑定到一起,如下面的配置信息:
<beans default-autowire="byType">
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl" autowird="byType"/>
<bean id="personDao" class="" />
</beans>
在使用@Autowired之后,default-autowire或者autowire的职责就转给了@Autowired,所以,容器的配置就只剩下孤零零的bean定义,如下所以:
<beans>
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl" />
<bean id="personDao" class="" />
</beans>
为了给容器中定义的每个bean定义对应的实例注入依赖,可以遍历他们,然后通过反射,检查每个bean定义对应的类上各种可能位置上的@Autowired,如果存在的话,就可以从当前容器管理的对象中获取符合条件的对象,设置给@Autowired所标注的属性域、构造方法或者方法定义。如果按照这个实现逻辑,我们是否可以提供一个Spring的IoC容器使用的BeanPostProcessor自定义实现,让这个BeanPostProcessor在实例化bean定义的过程中,来检查当前对象是否有@Autowired标注的依赖。Spring类AutowiredAnnotationBeanPostProcessor已经实现了它。那么最终的配置如下:
<beans>
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl" />
<bean id="personDao" class="" />
</beans>
- @Qualifier : byName
与 @Autowired 注解配合使用,当@Autowired标注的依赖在容器中找到多个实例与之对应时,需要通过@Qualifier去byName自动绑定 .
Spring自身除了注解外,Java 从JDK 5.0 以后,提供了@Resource和@PostConstruct以及@PreDestroy的 Annotation(注解)功能,Spring 也提供了对 Annotation 技术的全面支持。
- @Resource:byName
@Resource和@Autowired一样都可以标注在属性,构造方法和普通方法上,同样的,就像@Autowired需要AutowiredAnnotationBeanPostProcessor为它与IoC容器牵线搭桥一样,JDK5.0的这些注解也同样需要一个BeanPostProcessor帮助他们实现自身的价值。这个BeanPostProcessor就是org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,只有将CommonAnnotationBeanPostProcessor添加到容器,注解才能发挥作用,通常如下配置:
<beans>
<bean class="org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor"/>
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl" />
<bean id="personDao" class="" />
</beans>
既然不管@Autowired还是@Resource都需要添加相应的BeanPostProcessor到容器,那么我们就可以在基于XSD的配置文件中使用一个<context:annotation-config>配置高度以上所有的BeanPostProcessor配置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl" />
<bean id="personDao" class="" />
</beans>
<context:annotion-config>不到帮我们把AutoWiredAnnotionBeanPostProcessor和CommonAnnotationBeanPostProcessor注册到容器,同事还会把PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor一并进行注册。
到这里,我们是不是觉得,这是什么注解呀,一半分散在Java源代码中(@Autowired标注的信息),一半依然留在XML配置文件中,能否有一种更彻底的方法吗?答案是肯定的,它就是classpath-scanning,使用相应的注解对组成应用程序的相关类进行标注后,classpath-scanning功能可以从某一顶层包开始扫描,当扫描到某个类标注了相应的注解之后,就会提取该类的相关信息,构建对应的BeanDefinition,然后把构建完的BeanDefinition注册到容器。
classpath-scanning功能的触发是由<context:component-scan>决定的。添加XML配置文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package=“com.mengma”/>
</beans>
现在<context:component-scan>将遍历扫描com.mengma路径下的所有类型定义,寻找标注了相应注解的类,并添加到IoC容器。 <context:component-scan>默认扫描的注解类型是@Component。不过,在@Component语义基础上细化后的@Contoller、@Service和@Repository也同样能获得,而且<context:component-scan>,同时会将AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor一并注册到容器中,所以,@Autowired和@Resource也能满足依赖注入。
- @Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
- @Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
- @Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
- @Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
后面会继续补充后面版本的注解