前面我们已经学会使用@Resource注解注入属性,并且我们还编码剖析了@Resource注解的实现原理。现在我们来学习使用@Autowire注解注入属性,本文是建立在编码剖析@Resource注解的实现原理的案例基础上的。
一、用@Autowire注解完成属性装配
@Autowire注解和@Resource一样,同样也可以标注在字段或属性的setter方法上,但它默认按类型装配。
我们将@Autowire注解标注在字段上,如将PersonServiceBean类的代码修改为:
public class PersonServiceBean implements PersonService {
@Autowired private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
接着将SpringTest类的代码改为:
public class SpringTest {
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService) ctx.getBean("personService");
personService.save();
ctx.close();
}
}
测试instanceSpring()方法,可发现Eclipse控制台打印:
如果我们想使用按名称装配,可以结合@Qualifier注解一起使用,如将PersonServiceBean类的代码修改为:
public class PersonServiceBean implements PersonService {
@Autowired @Qualifier("personDaoxxxx") private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
再次测试instanceSpring()方法,可发现Eclipse控制台打印:
注意:@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如:
@Autowired(required=true) @Qualifier("personDaoxxxx") private PersonDao personDao;
required=true代表字段personDao必须要注入值,也即是说在Spring容器中根据类型找不到对应的bean,那就会报异常;required=false意味着在Spring容器中根据类型找不到对应的的bean,就会把该字段设为null。
依赖注入——自动装配依赖对象
对于自动装配,大家了解一下就可以了,实在不推荐大家使用。例子:
<bean id="..." class="..." autowire="byType"/>
autowire属性取值如下:
- byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。
- byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。
- constructor与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
- autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
现在我们重点关注byType和byName这两个属性值。为了试验,我们将PersonServiceBean类的代码改为:
public class PersonServiceBean implements PersonService {
private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
此时要想自动装配依赖对象,试着将Spring的配置文件修改为:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:annotation-config/>
<bean id="personDaoxxxx" class="cn.itcast.dao.impl.PersonDaoBean"></bean>
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" autowire="byType"></bean>
</beans>
此时测试SpringTest类的instanceSpring()方法,可看到Eclipse控制台打印:
若将Spring的配置文件修改为:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:annotation-config/>
<bean id="personDaoxxxx" class="cn.itcast.dao.impl.PersonDaoBean"></bean>
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" autowire="byName"></bean>
</beans>
再次测试SpringTest类的instanceSpring()方法,发现报空指针异常,明显就是没有为属性注入值,所以我们将Spring的配置文件中的
<bean id="personDaoxxxx" class="cn.itcast.dao.impl.PersonDaoBean"></bean>
修改为:
<bean id="personDao" class="cn.itcast.dao.impl.PersonDaoBean"></bean>
此时再次测试SpringTest类的instanceSpring()方法,可发现Eclipse控制台打印:
二、让Spring自动扫描和管理Bean
前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些组件采用XML的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。Spring2.5为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进Spring容器中管理。它的作用和在XML文件中使用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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:component-scan base-package="cn.itcast" />
</beans>
其中<context:component-scan base-package="cn.itcast" />
这个配置隐式注册了多个对注解进行解析处理的处理器,包括<context:annotation-config/>
该配置注册的处理器,也就是说写了<context:component-scan base-package="cn.itcast" />
配置,就不用写<context:annotation-config/>
配置了,此外base-package为需要扫描的包(含子包)。
@Service用于标注业务层组件、 @Controller用于标注控制层组件(如Struts2中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
本文是建立在@Autowire注解与自动装配的案例基础上的。我们首先将Spring的配置文件改为:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<context:component-scan base-package="cn.itcast" />
</beans>
然后使用@Service注解标注PersonServiceBean类,如下:
@Service
public class PersonServiceBean implements PersonService {
private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
使用@Repository注解标注PersonDaoBean类,如下:
@Repository
public class PersonDaoBean implements PersonDao {
@Override
public void add() {
System.out.println("执行PersonDaoBean中的add()方法");
}
}
最后,我们修改SpringTest类的代码为:
public class SpringTest {
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService) ctx.getBean("personServiceBean");
PersonDao personDao = (PersonDao) ctx.getBean("personDaoBean");
System.out.println(personService);
System.out.println(personDao);
ctx.close();
}
}
测试instanceSpring()方法,可看到Eclipse控制台打印:
如果我们想使用按指定名称获取,可将PersonServiceBean类的代码修改为:
@Service("personService")
public class PersonServiceBean implements PersonService {
private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
这样,SpringTest类的代码应改为:
public class SpringTest {
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService) ctx.getBean("personService");
System.out.println(personService);
ctx.close();
}
}
测试instanceSpring()方法,可看到Eclipse控制台打印:
我们前面学过Spring管理的bean的作用域,我们就能知道以上Spring管理的两个bean的作用域默认是singleton。当然了,我们也可以更改Spring管理的bean的作用域,如将PersonServiceBean类的代码改为:
@Service("personService") @Scope("prototype")
public class PersonServiceBean implements PersonService {
private PersonDao personDao;
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
意味着Spring管理的PersonServiceBean这个bean的作用域变成prototype了,这时我们将SpringTest类的代码修改为:
public class SpringTest {
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService1 = (PersonService) ctx.getBean("personService");
PersonService personService2 = (PersonService) ctx.getBean("personService");
System.out.println(personService1 == personService2);
ctx.close();
}
}
测试instanceSpring()方法,可看到Eclipse控制台打印:
prototype作用域本来就意味着每次从Spring容器获取bean都是新的对象嘛。
若是通过在classpath路径下自动扫描方这种式把组件纳入Spring容器中管理,如何指定bean的初始化方法和销毁方法呢?这时我们就需要用到两个注解:@PostConstruct和@PreDestroy。为了试验,我们将PersonServiceBean类的代码修改为:
@Service("personService")
public class PersonServiceBean implements PersonService {
private PersonDao personDao;
@PostConstruct
public void init() {
System.out.println("初始化资源");
}
@PreDestroy
public void destroy() {
System.out.println("销毁、关闭资源");
}
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void save() {
personDao.add();
}
}
接下来还要将SpringTest类的代码修改为:
public class SpringTest {
@Test
public void instanceSpring() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService) ctx.getBean("personService");
ctx.close();
}
}
这样,测试instanceSpring()方法,Eclipse控制台会打印: