上一篇文章里我们主要介绍了基于XML Schemal的配置方式。本篇文章里将介绍另外两种提供Bean定义的方式,基于注解和基于Java类的配置。
不管是XML还是注解,他们都是表达Bean定义的载体,其实质都是为Spring容器提供Bean定义的信息,表现形式上是将XML定义的东西通过类注解进行描述。Spring从2.0开始引入基于注解的配置方式,在3.0时得到进一步的完善。
下面是使用注解定义一个DAO的Bean:
package com.hhxs.bbt.dao;
import org.springframework.stereotype.Component;
// 1.通过Repository定义一个DAO的Bean
@Component("userDao")
public class UserDao {
...
}
在上面1
处,使用@Component注解在UserDao类声明处对类进行标注,它可以被Spring容器识别,Spring容器自动将POJO转换为容器管理的Bean。它和以下的XML配置是等效的:
"userDao" class="com.hhxs.bbt.dao.UserDao">
除了@Component以外,Spring提供了三个功能基本和@Component等效的注解,它们分别用于对DAO、Service及Web层的Controller进行注解,所以也称这些注解为Bean的衍型注解:
在@Component之外提供这三个注解,是为了让注解类本身的用途更加清晰,此外Spring还赋予了它们一些特殊功能。
Spring在2.5 后提供了一个context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式:
"1.0" encoding="UTF-8"?>
"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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd>
Spring公共配置
package="com.hhxs.bbt" />
在上面1
处通过context
命名空间的compnent-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。
如果仅希望扫描特定的类而非基包下的所有类,那么可以使用resource-pattern属性过滤特定的类,如下所示:
package="com.hhxs.bbt" resource-pattern="dao/*.class"/>
默认情况下resource-pattern属性的值为**/*.class
,即基包里的所有类。这里我们设置为dao/*.class
,则Spring仅会扫描基包里dao子包中的类。通过resource-pattern属性仅可按资源名称对基包中的类进行过滤,如果需要更详细的过滤,则需要用到
的过滤子元素:
package="com.hhxs.bbt">
"regex" expression="com.\hhxs.\bbt.*"/>
"aspectj" expression="com.hhxs.bbt..*Controller+"/>
表示要包含的目标类
表示要排除在外的目标类一个
下可以包含若干个
和
元素。这两个过滤元素均支持多种类型的过滤表达式,如下表所示:
类别 | 示例 | 说明 |
---|---|---|
annotation | com.hhxs.bbt.XxxAnnotation | 所有标注了XxxAnnotation的类。该类型采用目标类是否标注了某个注解进行过滤。 |
assignable | com.hhxs.bbt.XxxService | 所有继承或扩展XxxService的类。该类型采用目标类是否继承或扩展某个特定类进行过滤。 |
aspectj | com.hhxs.bbt..*Service+ | 所有类名以Service结束的类及继承或扩展它们的类。该类型采用AspectJ表达式进行过滤。 |
regex | com.hhxs.bbt..* | 所有com.hhxs.bbt类包下的类。该类型采用正则表达式根据目标类的类名进行过滤。 |
custom | com.hhxs.bbt.XxxTypeFilter | 采用XxxTypeFilter通过代码的方式根据过滤规则。该类必须实现org.springframework.core.type.TypeFilter接口 |
spring使用@Autowired注解实现Bean的依赖注入。@Autowired默认按类型匹配的方式,在容器查找匹配Bean,
**当且仅有一个匹配的**Bean时,Spring将其注入到@Autowired标注的变量中。
package com.hhxs.bbt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LogonService {
@Autowired
private LogDao logDao;
@Autowired
private UserDao userDao;
...
}
如果容器中没有一个和标注变量类型匹配的bean,Spring容器启动时将报NoSuchBeanDefinitionException
异常。如果希望不抛出异常可以使用@Autowired(required=false)进行标注。默认情况下,@Autowired的required属性值为true。
如果容器中有一个以上匹配的Bean时,可以通过@Qualifier注解限定Bean的名称。
package com.hhxs.bbt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class LogonService {
@Autowired
private LogDao logDao;
@Autowired
@Qualifier("userDao")
private UserDao userDao;
...
}
对集合类进行标注
如果对类中集合类的变量和方法入参进行@Autowired标注,Spring会将容器中类型匹配的所有Bean都自动注入进来。这是一个很好的特性,所以在这里介绍下。
package com.hhxs.bbt.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class MyComponent {
// Spring会将容器中所有类型为Plugin的Bean注入到这个变量中
@Autowired(required=false)
private List plugins;
public List getPlugins() {
return plugins;
}
}
Spring如果发现变量是一个集合类,则它会将容器中匹配集合元素类型的所有Bean都注入进来。这里,Plugin为一个接口,它拥有两个实现类,分别是OnePlugin和TwoPlugin,这两个实现类都通过@Component标注为Bean,则Spring会将这两个Bean都注入到plugins中。
JavaConfig是Spring的一个子项目,它旨在通过Java类的方式提供Bean的定义信息。Spring3.0基于Java类配置的核心即取材于JavaConfig。
普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息了,每个标注了@Bean的类方法都相当于提供一个Bean的定义信息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 1.将一个POJO标注定义为Bean的配置类
@Configuration
public class AppConf {
// 2.以下两个方法定义了两个Bean,并提供了Bean的实例化逻辑
@Bean
public UserDao userDao() {
return new UserDao();
}
@Bean
public LogDao logDao() {
return new LogDao()
}
@Bean
public LogonService logonService() {
LogonServcie logonService = new LogonService();
// 将上面2处定义的Bean注入到logonService的Bean
logonService.setLogDao(logDao());
logonService.setUserDao(userDao());
return logonService;
}
}
1
处在AppConf类的定义处标注了@Configuration注解,说明这个类可用于为Spring提供Bean信息。类的方法出可以标注@Bean注解,Bean的类型由方法返回值类型决定,名称默认和方法名相同,也可通过@Bean(name=”userDao”)显示指定Bean的名称。
上面的配置和以下XML配置等效:
id="userDao" class="com.hhxs.bbt.dao.UserDao" />
id="logDao" class="com.hhxs.bbt.dao.LogDao" />
id="userDao" class="com.hhxs.bbt.conf.LogonService"
p:logDao-ref="userDao" p:userDao-ref="logDao" />
Spring提供了一个AnnotationConfigApplicationContext类,它能够直接通过标注@Configuration的Java类启动Spring容器。以下两种方式是等效的:
第一种:
package com.hhxs.bbt.conf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);
LogonService logonService = ctx.getBean(logonService.class);
logonService.printHello();
}
}
第二种:
package com.hhxs.bbt.conf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// 注册多个@Configuration配置类
ctx.register(DaoConfig.Class);
ctx.register(ServiceConfig.class);
// 刷新容器以应用这些注册的配置类
ctx.refresh();
LogonService logonService = ctx.getBean(logonService.class);
logonService.printHello();
}
}
可以通过代码一个一个注册配置类,也可以通过@Import将多个配置类组装到一个配置类中,这样仅需要注册这个组装好的配置类就可以启动容器了。
package com.hhxs.bbt.conf;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Import;
@Configurable
@Import(DaoConfig.class)
public class ServiceConfig {
@Bean
public LogonService logonService() {
LogonService logonService = new LogonService();
return logonService;
}
}
我们既可以在XML配置文件中引用@Configuration的配置,也可以通过Configuration配置类中引用XML配置信息。只要不同形式的Bean定义信息能够加载到Spring容器中,Spring就能足够“智能”的完成Bean之间的装配。
配置方式比较:
– | 基于XML配置 | 基于注解配置 | 基于Java类配置 |
---|---|---|---|
Bean定义 |
|
Bean实现类出通过标注 @Component、@Repository、@Service、@Controller |
在标注了@Configuration的Java类中,通过在类方法上标注@Bean定义一个Bean。方法必须提供Bean的实例化逻辑。 |
Bean名称 | 通过的id或name属性定义 | 通过注解的value属性定义,如@Component(“userDao”)。默认名称为小写字母打头的类名(不带包名):userDao | 通过@Bean的name属性定义,如@Bean(“userDao”),默认名称为方法名。 |
Bean注入 | 通过子元素或通过p命名空间的动态属性 | 通过在成员变量或方法入参出标注@Autowired,按类型匹配自动注入 | 可以通过在方法处通过@Autowired使方法入参绑定Bean,然后在方法中通过代码进行注入,还可以通过调用配置类的@Bean方法进行注入 |
Bean生命过程方法 | 通过的init-method和destory-method属性指定Bean实现类的方法名。最多只能指定一个初始化方法和一个销毁方法 | 通过在目标方法上标注@PostConstruct和@PreDestroy注解指定初始化或销毁方法,可以定义任意多个方法 | 通过@Bean的initmethod或destoryMethod指定一个初始化或销毁方法。 |
Bean作用范围 | 通过的scope属性指定 | 通过在类定义出标注@Scope指定 | 通过在Bean方法定义处标注@Scope指定 |
Bean延迟初始化 | 通过的lazy-init属性指定,默认为default,继承与的default-lazy-init设置,该值默认为false | 通过在类定义处标注@Lazy指定,如@Lazy(true) | 通过在Bean方法定义处标注@Lazy指定 |
使用场景建议:
– | 基于XML配置 | 基于注解配置 | 基于Java类配置 |
---|---|---|---|
适合场景 | 1)Bean实现类来源于第三方类库 2)命名空间的配置 |
Bean实现类是当前项目开发的, 可以直接在Java类中使用基于注解的配置 |
基于Java类配置的优势在于 可以通过代码方式控制Bean初 始化的整体逻辑。 |