Spring Bean装配
Posted chan_ai_chao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Bean装配相关的知识,希望对你有一定的参考价值。
1.Bean的配置项及作用域
(1)Bean配置项
- id:在整个IOC容器中这个bean的唯一标识
- class:具体要实例化的类(只有它是必须的)
- scope:范围,作用域
- constructor arguments:构造器参数(bean的构造注入方式)
- properties:属性(bean的设值注入方式)
- autowiring mode:自动抓配的模式
- lazy-initialization mode:懒加载模式
- initialization/destruction method: 初始化和销毁的方法
(2)Bean作用域(scope)
- singleton:单例,指一个Bean容器中只存在一份(是spring bean的默认模式)
- prototype:每次请求(每次使用)创建新的实例,destroy方式不生效
- request:每次http请求创建一个实例且仅在当前request内有效
- session:同上,每次http请求创建,当前session内有效
- global session:基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
2.Bean的生命周期
(1)定义
- 在spring的配置文件xml中配置的bean,定义了id以及对于的class
(2)初始化
1)定义:
- 当IOC容器(context start)启动的时候去加载并配置文件里面的bean,生成bean的实例
2)两种方式:
- 实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
<bean id="exampleInitBean" class="example.ExampleBean"/>
public class ExampleInitializingBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception{ //do something } }
- 配置init-method
<bean id="exampleInitBean" class="example.ExampleBean" init-method="init"/>
public class ExampleBean { public void init(){ //do some initialization work } }
(3)使用
- 在单元测试或实际开发中从bean容器中取出一个bean的实例,调用它的方法
(4)销毁
1)定义:
- 在bean容器停止的时候销毁由当前bean容器创建的所有实例
2)两种方式:
- 实现org.springframework.beans.factory.DisposableBean接口,覆盖destroy方法
public class ExampleDisposableBean implements DisposableBean { @Override public void destroy() throws Exception{ //do something } }
- 配置destroy-method
<bean id="exampleInitBean" class="example.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup(){ //do some destruction work(like releasing pooled connections) } }
(5)配置全局默认初始化、销毁方法
<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="..." default-init-method="init" default-destroy-method="destroy"> </beans>
-
如果同时存在全局默认,覆盖接口和配置这三种方法,覆盖接口优先于配置,全局默认不生效
3.Aware接口
(1)Aware定义:
- Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化之后,可以获取相应资源
- 通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
- 为对Spring进行简单的扩展提供了方便的入口
(2)常用Aware:
1)ApplicationContextAware:
- 声明ApplicationContext,会向实现了这个接口的bean提供IOC容器的上下文信息
- 实现了这个接口的bean必须配置到Spirng的bean配置文件中,并且由Spring的bean容器去加载
2)BeanNameAware:
- 会向实现了这个接口的bean提供关于beanname定义的内容
3)ApplicationEventPublisherAware:
- 用于事件的发布
4)BeanClassLoaderAware:
- 类加载器用来加载bean classes
5)其他:
- BeanFactoryAware、BootstrapContextAware、LoadTimeWeaverAware、MessageSourceAware
- NotificationPublisherAware、PortletConfigAware、PortletContextAware、ResourceLoaderAware
- ServletConfigAware、ServletContextAware
4.自动装配(Autowiring)
(1)no
- 不做任何操作
(2)byName
- 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配
1)AutoWiringService.java中(引用的bean中,被引用的bean无需操作)添加setter方法
public class AutoWiringService { private AutoWiringDAO autoWiringDAO; public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) { this.autoWiringDAO = autoWiringDAO; } }
2)spring-autowiring.xml文件,有了byName,通过第二个bean的id自动装配,第一个bean中不需要加property设值了
<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="..." default-autowire="byName"> <bean id="autoWiringService" class="com.gc.autowiring.AutoWiringService"></bean> <bean id="autoWiringDAO" class="com.gc.autowiring.AutoWiringDAO"></bean> </beans>
(3)byType
- 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配
- 如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配
- 如果没有找到相匹配的bean,则什么事都不发生
- byType的方式与bean的id没有直接关系,是根据class来装配的
1)AutoWiringService.java中添加setter方法
public class AutoWiringService { private AutoWiringDAO autoWiringDAO; public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) { this.autoWiringDAO = autoWiringDAO; } }
2)spring-autowiring.xml文件,有了byType,通过第二个bean的class自动装配(第二个bean的id删掉也可正常装配),第一个bean中不需要加property设值了
<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="..." default-autowire="byType"> <bean id="autoWiringService" class="com.gc.autowiring.AutoWiringService"></bean> <bean id="autoWiringDAO" class="com.gc.autowiring.AutoWiringDAO"></bean> </beans>
(4)constructor
- 与byType方式类似,不同之处在于它应用于构造器参数
- 如果容器中没有找到与构造器参数类型一直的bean,那么抛出异常
- constructor的方式与bean的id没有直接关系,是根据class来装配的
1)AutoWiringService.java中添加构造方法
public class AutoWiringService { private AutoWiringDAO autoWiringDAO; public AutoWiringService(AutoWiringDAO autoWiringDAO) { this.autoWiringDAO = autoWiringDAO; } }
2)spring-autowiring.xml文件,有了constructor,通过第二个bean的class自动装配(第二个bean的id删掉也可正常装配),第一个bean中不需要加constructor-arg了
<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="..." default-autowire="constructor"> <bean id="autoWiringService" class="com.gc.autowiring.AutoWiringService"></bean> <bean id="autoWiringDAO" class="com.gc.autowiring.AutoWiringDAO"></bean> </beans>
5.Resources
(1)Resources定义
- 针对于资源文件的统一接口
(2)Resources分类
- UrlResource:URL对应的资源,根据一个URL地址即可构建
- ClassPathResource:获取类路径下的资源文件
- FileSystemResource:获取文件系统里面的资源
- ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
- InputStreamResource:针对于输入流封装的资源
- ByteArrayResource:针对于字节数组封装的资源
(3)ResourceLoader
- ResourceLoader是对Resource进行加载的接口
- 在springIOC容器中,所有的application context都实现了ResourceLoader这个接口,也就是所有的application context都可以获取Resource实例
- ResourceLoader接口的声明如下:
public interface ResourceLoader { Resource getResource(String location); }
- 前缀类型classpath:、file:、http:、(none)
- classpath:com/myapp/config.xml //从classpath中加载
- file:/data/config.xml //从文件系统中作为URL加载
- http://myserver/logo.png //作为URL(网址)加载
- /data/config.xml //依赖于底层的ApplicationContext
6.Bean的定义及作用域的注解实现
(1)Classpath扫描与组件管理
- 从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是XML定义bean,比如@Configuration,@Bean,@Import,@DependsOn
- @Configuration是一个通用注解,可用于任何bean
- @Repository,@Service,@Controller是更有针对性的注解
- @Repository通常用于注解DAO类,即持久层
- @Service通常用于注解Service类,即服务层
- @Controller通常用于Controller类,即控制层(MVC)
(2)元注解(meta-annotations)
- 许多Spring提供的注解可以作为自己的代码,即“元数据注解”,元注解是一个简单的注解,可以应用到另一个注解(注解的注解)
@Component //Spring will see this and treat @Service in the same way as @Component public @interface Service{ //... }
- 除了value(),元注解还可以有其它的属性,允许定制
@Scope("session") public @interface SessionScope{ ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT
(3)类的自动检测与注册Bean
1)自动扫描
- Spring可以自动检测类并注册Bean到ApplicationContext中。有如下两个例子
@Service public class SimpleMovieLister{ private MovieFinder movieFinder; @Autowired public SimpleMovieLister(MovieFinder movieFinder){ this.movieFinder=movieFinder; } }
@Repository public class ChineseMovieFinder implements MovieFinder{ //implementation elided for clarity }
- 为了能够检测这些类并注册相应的Bean,需要下面内容
<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.xsd" > <!--back-package:扫描这个包下的所有类--> <context:component-scan base-package="org.example"/> </beans>
- <context:annotation-config/>
- <context:component-scan>扫描基于类的注解
- <context:annotation-config>只能在完成bean的注册之后去处理bean中的方法或成员变量的注解
<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.xsd" > <context:annotation-config/> </beans>
-
<context:component-scan> 包含<context:annotation-config>,通常在使用前者后,不用再使用后者
- <context:component-scan>扫描基于类的注解
- <context:annotation-config>只能在完成bean的注册之后去处理bean中的方法或成员变量的注解
- 在使用了<context:component-scan> 之后,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来
2)使用过滤器进行自定义扫描
- 默认情况下,类被自动发现并注册bean的条件是:使用@Component,@Repository,@Service,@Controller注解或者使用@Component的自定义注解
- 可以通过过滤器修改上面的行为,如:下面例子的XML配置忽略所有的@Repository注解并用“Stub”代替
<beans> <!--base-package:扫描这个包下的所有信息--> <context:component-scan base-package="org.example"> <!--include-filter:包含过滤器 type:通配符的形式 即找到Stub Repository的类--> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <!--exclude-filter:排除过滤器 type:注解的形式 即排除Repository注解的类--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
- 还可使用user-default-filters="false"禁用自动发现与注册
3)定义bean
- 扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component,@Repository,@Service,@Controller都会有个那么熟悉用于显式设置Bean Name)
a)显式设置
//Service括号中即为name属性,即xml的bean容器中对应的id值 @Service("myMovieLister") public class SimpleMovieLister{ //... }
b)未显式设置,由BeanNameGenerator自动生成
//通常的生成规则是类名第一个字母改成小写的字符串movieFinderImpl作为bean的id @Repository public class MovieFinderImpl implements MovieFinder{ //... }
- 可自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参数构造器
<beans> <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator"/> </beans>
4)作用域(scope)
- 通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了一个标识scope的注解@Scope
@Scope("prototype") @Repository public class MovieFinderImpl implements MovieFinder{ //... }
- 也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器(何时用到自定义?例如在每一个线程中使用一个bean的实例,定义这样一种作用域只针对每一个线程有效)
<beans> <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/> </beans>
- 代理方式:可以使用scoped-proxy属性指定代理,有三个值可选:no,interfaces,targetClass
<beans> <context:component-scan base-package="org.example" scoped-proxy="interfaces"/> </beans>
(7)@Required
- @Required注解适用于bean属性的setter方法
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder){ this.movieFinder=movieFinder; } //... }
(8)@Autowired
- 可以将@Autowired注解为“传统”的setter方法
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder){ this.movieFinder=movieFinder; } //... }
- 可用于构造器或成员变量
public class MovieRecommender{ @Autowired private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } }
- 默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下面的方式避免
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder(MovieFinder movieFinder){ this.movieFinder=movieFinder; } }
- 每个类只能有一个构造器被标记为required=true
- @Autowired的必要属性,建议使用@Required注解
- 可以使用@Autowired注解那些众所周知的解析依赖性接口,比如:BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource
public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender(){ } }
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中的所有特点类型的bean
public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } }
- 可以用于装配key为String的Map(key是所有bean的id,value是bean的对象)
public class MovieRecommender { private Map<String,MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String,MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } }
- 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用的@Order注解,@Order注解只针对数组list有效,对map无效
- @Autowired是由Spring BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@Bean注解加载
(9)@Qualifier
- 按类型自动装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数
- 可用于注解结合类型变量
1)成员变量
public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; }
2)方法
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main")MovieCatalog movieCatalog,CustomerPreferenceDao customerPreferenceDao){ this.movieCatalog=movieCatalog; this.customerPreferenceDao=customerPreferenceDao; } }
3)xml文件中
<beans> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier value="main"/> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier value="action"/> </bean> </beans>
- 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@Qualifier指定bean的名字),替代方式是使用[email protected]注解,它是通过其独特的名称来定义识别特定的目标(这是一个与所声明的类型无关的匹配过程)
- 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的bean,为这些bean使用@Resource注解,通过唯一名称引用集合或Map的bean
- @Autowired适用于fields,constructors,multi-argument methods这些允许在参数级别使用@Qualifier注解缩小范围的情况
- @Resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用@Qualifier
- 定义自己的qualifier注解并使用
1)成员变量和方法中,先自定义Genre作为qualifier注解,即可使用@Genre做注解了
@Qualifier public @interface Genre{ String value(); }
public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy")MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } }
2)xml文件中
<beans> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="Genre" value="Action"/> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="exmaple.Genre" value="Comedy"/> </bean> </beans>
7.基于java的容器注解说明
(1)@Bean
- @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的<bean/>
- 可以在Spring的@Component注解的类中使用@Bean注解任何方法(仅仅是可以)
- 上一点中,通常使用的是@Configuration
@Configuration public class AppConfig{ @Bean public MyService myService(){ return new MyServiceImpl(); } }
相当于在XML中配置如下
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
- 自定义Bean name
@Configuration public class AppConfig{ @Bean(name="myFoo") public Foo foo(){ return new Foo(); } }
- init-method和destroy-method
public class Foo{ public void init(){ //initialization logic } } public class Bar{ public void cleanup(){ //destruction logic } } @Configuration public class AppConfig{ @Bean(initMethod="init") public Foo foo(){ return new Foo(); } @Bean(destroyMethod="cleanup") public Bar bar(){ return new Bar(); } }
(2)资源文件读取@ImportResource和@Value
<beans> <context:annotation-config/> <!--property-placeholder其作用是加载资源文件--> <context:property-placeholder location="classpath:/com/acme/jdbc.prperties"/>
- 使用xml
<beans> <context:annotation-config/> <!--property-placeholder其作用是加载资源文件--> <context:property-placeholder location="classpath:/com/acme/jdbc.prperties"/> <bean class="com.acme.AppConfig"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc:username}"/> <property name="password" value="${jdbc:password}"/> </bean> </beans>
使用注解
@Configuration @ImportResource("classpath:/com/acme/properties-config.xml") public class AppConfig{ @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource(){ return new DriverManagerDataSource(url,username,password); } }
(3)@Bean和@Scope
- 默认@Bean是单例的,改变其范围用@Scope
- Bean的作用域包括singleton,prototype,request,session,global session
@Configuration public class MyConfiguration{ @Bean @Scope("prototype") public Encryptor encryptor(){ //... } }
@Bean
@Scope(value="session",proxyMode=ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences(){
return new UserPreferences();
}
@Bean
public Service userService(){
UserService service=new SimpleUserService();
service.setUserPreferences(userPreferences());
return service;
}
(4)基于泛型的自动装配
1)
@Configuration public class MyConfiguration{ @Bean public StringStore stringStore(){ return new StringStore(); } @Bean public IntegerStore integerStore(){ return new IntegerStore(); } }
2)Autowired自动装配
private Store<String> s1;//<String> qualifier,injects the stringStore bean private Store<Integer> s2;//<Integer> qualifier,injects the integerStore bean
3)基于泛型的自动装配
//Inject all Store beans as long as they have an <Integer> generic //Store<String> beans will not appear in this list @Autowired private List<Store<Integer>> s;
- CustomAutowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的qualifier注解类型(即使没有使用Spring的@Qualifier注解)
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean>
- 该AutowireCandidateResolver决定自动装配的候选者:
- 每个bean定义的autowire-candidate值
- 任何<bean/>中的default-autowire-candidates
- @Qualifier注解及使用CustomAutowireConfigurer的自定义类型
8.Spring对JSR支持的说明
(1)@Resource
- Spring还支持使用[email protected]注解的变量或setter方法,这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持这种模式
- @Resource有一个那么属性,并且默认Spring解释该值作为被注入bean的名称
public class SimpleMovieLister{ private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
- 如果没有显式地指定@Resource的name,默认的名称是从属性名或者setter方法得出
public class SimpleMovieLister{ private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
- 注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
(2)@PostConstruct and @PreDestory
- CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的(只有在IOC容器中注册了才能使用它来处理@Resource,@PostConstruct,@PreDestory)
public class CachingMovieLister{ @PostConstruct public void populateMovieCache(){ //populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache(){ //clears the movie cache upon destruction... } }
(3)使用JSR330标准注解
- 从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致
- 使用JSR330需要依赖javax.inject包
- 使用maven引入方式
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
1)@Inject
- @Inject等效于@Autowired,可以使用于类、属性、方法、构造器
import javax.inject.Inject; public class SimpleMovieLister{ private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
2)@Named
- 如果想使用特定名称进行依赖注入,使用@Named
- @Named与@Component是等效的,也可以注解在类上,也可以指定某一种名称的bean
- 注解在类上如下:
import javax.inject.Inject; import javax.inject.Named; public class SimpleMovieLister{ private MovieFinder movieFinder; @Inject public void setMovieFinder(@Named("main") MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
- 指定某一种名称的bean如下:
@Named("movieListener") public class SimpleMovieLister{ private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
以上是关于Spring Bean装配的主要内容,如果未能解决你的问题,请参考以下文章