Spring IoC基于注解的配置

定义Bean

我们知道,Spring容器启动成功需要三个条件:Bean定义信息,Bean实现类以及Spring本身。在采用XML配置时,Bean的定义信息和Bean实现类是分离的,而基于注解的配置方式时,Bean定义信息则直接在实现类上进行配置。定义Bean的注解为@Component,如下:

1
2
3
4
5
6
import org.springframework.stereotyoe.Component;

@Component("userDao")
public class UserDao {
...
}

除了@Component以外,Spring还提供了3个功能基本和@Component等效的注解:

  • @Repository用于标注DAO类。
  • @Service用于标注Service类。
  • @Controller用于标注Web层的Controller类。

除了让注解更加清晰之外,Spring也会给这些类赋予一些特殊的功能(例如DAO层实现了线程安全的单例模式),所以推荐使用特定的注解进行标注。

Bean的作用域

通过注解定义的Bean,默认的作用域也是singleton。可以在Bean定义时通过注解@Scope来配置其他的作用域,如下:

1
2
3
4
5
6
7
import org.springframework.stereotyoe.Component;

@Component("userDao")
@Scope("prototype")
public class UserDao {
...
}

声明周期方法

在XML中配置Bean时,可以通过init-methoddestory-method指定初始化和销毁时执行的方法。在注解配置方法中,可以通过注解@PreConstruct@PreDestory达到同样的效果,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.stereotyoe.Component;
import javax.annotation.PreConstruct;
import javax.annotation.PreDestory;

@Component("userDao")
public class UserDao {
@PreConstruct
private void init1() {
System.out.println("init1.");
}

@PreConstruct
private void init2() {
System.out.println("init2.");
}

@PreDestory
private void destory1() {
System.out.println("destory1.");
}

@PreDestory
private void destory2() {
System.out.println("destory2.");
}
}

配置注解扫描

Spring在2.5版本以后提供了一个context命名空间,它提供了基于package扫描的方式来定义Bean,其使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
<?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-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="me.mzorro.demo.web" />
</beans>

如果需要扫描base-package下特定的类,可以使用resource-pattern属性。例如,下面的配置与上面的配置有相同的效果:

1
<context:component-scan base-package="me.mzorro.demo" resource-pattern="web/*.class"/>

resource-pattern的默认值是**/*.class,这里改为web/*.class,则只会扫描web子包下的所有类。

除此之外,使用过滤表达式会更加的灵活:

1
2
3
4
<context:component-scan base-package="me.mzorro.demo">
<context:include-filter type="regex" expression="me\.mzorro\.demo\.web.*" />
<context:exclude-filter type="aspectj" expression="me.mzorro.demo..*Controller+" />
</context:component-scan>

Spring支持5种过滤表达式:

type experssion example description
annotation me.mzorro.XxxAnnotation 所有拥有@XxxAnnotation注解的类
assignable me.mzorro.XxxService 所有实现或继承了XxxService的类。
aspectj me.mzorro.demo..*Controller+ AspectJ的语法,所有类名以Controller结尾的类以及其实现或继承了它们的类
regex me.mzorro.demo.web.* 按正则表达式进行匹配
custom me.mzorro.XxxTypeFilter 通过自定义代码的方式进行过滤,需要实现org.springframework.core.type.TypeFilter接口

Bean的自动注入

使用@AutoWired注入

Spring通过@AutoWired实现Bean的依赖注入(DI),来看一个例子:

1
2
3
4
5
@Service
public class UserService {
@AutoWired
private UserDao userDao;
}

@AutoWired默认按类型匹配的方式进行注入,需要有且仅有一个匹配的Bean时才能够注入成功。如果找不到匹配的Bean,则Spring会抛出异常。要使其在找不到匹配的Bean时不抛出异常,则需要使用@AutoWired(required=false)

如果相同类型的Bean在容器中不止一个,需要指定Bean的名称来唯一确定注入的Bean,则需要使用@Qualifier注解限定Bean的名称,如下:

1
2
3
4
5
6
@Service
public class UserService {
@AutoWired
@Qualifier("userDao")
private UserDao userDao;
}

@AutoWired也可以对方法的入参进行注入,例如:

1
2
3
4
5
6
7
8
9
@Service
public class UserService {
private UserDao userDao;

@AutoWired
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
this.userDao = userDao;
}
}

方法的入参也可以有多个。

集合属性的自动注入

@AutoWired注解也可以对集合进行自动注入,Spring会将容器中类型匹配的所有Bean都注入到集合中去。例如:

1
2
3
4
5
@Service
public class PluginService {
@AutoWired(required=false)
private List<Plugin> plugins;
}

Plugin是一个接口,上面代码的@AutoWired会将容器中所有实现了Plugin接口的对象都注入到集合中去。

标准注解的支持

Spring也支持标准的@Resource@Inject注解。其中@Resource默认按照名称进行匹配,在名称匹配失败的情况下才会按照类型进行匹配。@Inject则是没有required属性。可见这两个注解的功能都没有@AutoWired强大。