【1】@Configuration和@Bean
從Spring3.0開始,
@Configuration
用于定義配置類,可替換xml配置檔案,被注解的類内部包含有一個或多個被@Bean注解的方法,這些方法将會被
AnnotationConfigApplicationContext
或
AnnotationConfigWebApplicationContext
類進行掃描,并用于建構bean定義,初始化Spring容器。
@Configuration
标注在類上,相當于把該類作為spring的xml配置檔案中的
<beans>
,作用為:配置spring容器(應用上下文)。
AnnotationConfigWebApplicationContext類繼承圖如下所示:

AnnotationConfigApplicationContext類繼承圖如下所示:
配置類執行個體如下:
package com.web.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ComponentScans;
import com.web.bean.Person;
//配置類==配置檔案
@Configuration //告訴Spring這是一個配置類
public class MainConfig {
//給容器中注冊一個Bean;類型為傳回值的類型,id預設是用方法名作為id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
測試類如下:
package com.web.test;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import com.web.config.MainConfig;
public class IOCTest {
/*用AnnotationConfigApplicationContext 替換ClassPathXmlApplicationContext。
如果加載spring-context.xml檔案:
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
*/
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig.class);
@SuppressWarnings("resource")
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig.class);
// 擷取容器中注冊的bean的資訊
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}
@Bean 注解
@Bean
是一個方法級别上的注解,效果等同于在xml中配置一個bean,并設定其屬性。
xml配置如下:
<bean id="person"
class="com.core.Person" scope="singleton"
init-method="init" destroy-method="cleanUp"
autowire="byName" lazy-init="true" >
</bean>
等同于如下:
//給容器中注冊一個Bean;類型為傳回值的類型,id預設是用方法名作為id
@Scope("singleton")
@Lazy
@Bean(name="person",initMethod="init",destroyMethod="cleanUp",
autowire=Autowire.BY_NAME)
public Person person01(){
return new Person("lisi", 20);
}
Spring容器中注冊的bean,預設是單執行個體的。其作用域說明如下:
ConfigurableBeanFactory#SCOPE_PROTOTYPE
ConfigurableBeanFactory#SCOPE_SINGLETON
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION sesssion
@Scope注解調整作用域
prototype:
多執行個體的:ioc容器啟動并不會去調用方法建立對象放在容器中。每次擷取的時候才會調用方法建立對象。
singleton:
單執行個體的(預設值):ioc容器啟動會調用方法建立對象放到ioc容器中。以後每次擷取就是直接從容器(map.get())中拿。
request: 同一次請求建立一個執行個體。
session: 同一個session建立一個執行個體。
需要注意的是,單執行個體bean 初始化方法在容器建立時被調用,銷毀方法在容器銷毀時被調用。多執行個體bean,初始化方法在第一次擷取bean的時候調用(非容器建立時,容器建立時會調用構造方法),銷毀方法容Spring 容器不負責管理。
懶加載說明如下:
單執行個體bean:預設在容器啟動的時候建立對象;
懶加載:容器啟動不建立對象。第一次使用(擷取)Bean建立對象,并初始化。
多執行個體懶加載測試如下:
@Test
public void test02(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("ioc容器建立完成....");
Object bean = applicationContext.getBean("person");
Object bean2 = applicationContext.getBean("person");
System.out.println(bean == bean2);
}
result as follows :
ioc容器建立完成....
給容器中添加Person....
給容器中添加Person....
false//兩個bean不同哦
關于Spring中Bean的作用域與生命周期參考博文:Spring中Bean的作用域。
【2】@ComponentScan注解
xml配置中必不可少的标簽,用來掃描bean并注冊到IOC容器中。
xml配置示例如下:
<!-- 包掃描、隻要标注了@Controller、@Service、@Repository,@Component 都被注入-->
<context:component-scan base-package="com.web" use-default-filters="false">
<context:include-filter type="annotation" expression=""/>
<context:exclude-filter type="annotation" expression=""/>
</context:component-scan>
其中,type有五種形式:
- annotation-注解,
- assignable-給定的類型,
- regex-正則指定,
- custom-自定義規則
- aspectj-ASPECTJ表達式。
其javadoc如下:
Controls the type of filtering to apply to the expression.
"annotation" indicates an annotation to be present at the type
level in target components;
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
"aspectj" indicates an AspectJ type pattern expression to be matched by the target components;
"regex"indicates a regex pattern to be matched by the target
components' class names;
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
Note: This attribute will not be inherited by child bean
definitions.
Hence, it needs to be specified per concrete bean
definition.
配置類如下:
//配置類==配置檔案
@Configuration //告訴Spring這是一個配置類
@ComponentScans(
value = {
@ComponentScan(value="com.web",includeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
}
)
//@ComponentScan value:指定要掃描的包
//excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些元件
//includeFilters = Filter[] :指定掃描的時候隻需要包含哪些元件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照給定的類型;
//FilterType.ASPECTJ:使用ASPECTJ表達式
//FilterType.REGEX:使用正則指定
//FilterType.CUSTOM:使用自定義規則
public class MainConfig {
//給容器中注冊一個Bean;類型為傳回值的類型,id預設是用方法名作為id
@Bean(name="person")
public Person person01(){
return new Person("lisi", 20);
}
}
FilterType.CUSTOM,classes={MyTypeFilter.class}中的MyTypeFilter類如下:
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:讀取到的目前正在掃描的類的資訊
* metadataReaderFactory:可以擷取到其他任何類資訊的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// TODO Auto-generated method stub
//擷取目前類注解的資訊
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//擷取目前正在掃描的類的類資訊
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//擷取目前類資源(類的路徑)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
// 這裡自定義規則
if(className.contains("er")){
return true;
}
return false;
}
}
【3】@Conditional條件判斷
@Conditional({Condition})
: 按照一定的條件進行判斷,滿足條件給容器中注冊bean。
配置類如下:
/**
* @Conditional({Condition}) : 按照一定的條件進行判斷,滿足條件給容器中注冊bean
*
* 如果系統是windows,給容器中注冊("bill")
* 如果是linux系統,給容器中注冊("linus")
*/
@Conditional(WindowsCondition.class)
@Bean("bill")
public Person person01(){
return new Person("Bill Gates",62);
}
@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}
Conditional接口如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
既可以作用于方法上面,也可以配置在類上面,參數為Condition的實作類。
Condition接口如下:
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked.
* @return {@code true} if the condition matches and the component can be registered
* or {@code false} to veto registration.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
條件類如下:
//判斷是否linux系統
public class LinuxCondition implements Condition {
/**
* ConditionContext:判斷條件能使用的上下文(環境)
* AnnotatedTypeMetadata:注釋資訊
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系統
//1、能擷取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、擷取類加載器
ClassLoader classLoader = context.getClassLoader();
//3、擷取目前環境資訊
Environment environment = context.getEnvironment();
//4、擷取到bean定義的注冊類
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
//可以判斷容器中的bean注冊情況,也可以給容器中注冊bean
boolean definition = registry.containsBeanDefinition("person");
if(property.contains("linux")){
return true;
}
return false;
}
}
如果該注解配置在類上面,則該配置類的所有方法都将使用該注解。示例如下:
@Conditional({WindowsCondition.class})
@Configuration
public class MainConfig2 {
//...
}
@Confitional擴充如下圖:
【4】@import導入元件
@Import ,快速給容器中導入一個元件。
其注解如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
其使用方式如下:
1)@Import(要導入到容器中的元件);容器中就會自動注冊這個元件,id預設是全類名。
@Configuration
@Import({Color.class,Red.class})
public class MainConfig2 {
//...
}
2)ImportSelector:傳回需要導入的元件的全類名數組;
//自定義邏輯傳回需要導入的元件
public class MyImportSelector implements ImportSelector {
//傳回值,就是到導入到容器中的元件全類名
//AnnotationMetadata:目前标注@Import注解的類的所有注解資訊
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//importingClassMetadata
//方法不要傳回null值
return new String[]{"com.web.bean.Blue","com.web.bean.Yellow"};
}
}
此時配置類如下:
@Configuration
@Import({Color.class,Red.class,MyImportSelector.class})
//@Import導入元件,id預設是元件的全類名
public class MainConfig2 {
//...
}
3)ImportBeanDefinitionRegistrar:手動注冊bean到容器中。
ImportBeanDefinitionRegistrar接口類如下:
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
實作類如下:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:目前類的注解資訊
* BeanDefinitionRegistry:BeanDefinition注冊類;
* 把所有需要添加到容器中的bean;調用
* BeanDefinitionRegistry.registerBeanDefinition手工注冊進來
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.web.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.web.bean.Blue");
if(definition && definition2){
//指定Bean定義資訊;(Bean的類型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注冊一個Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
此時配置類如下:
@Import({Color.class,Red.class,MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
//@Import導入元件,id預設是元件的全類名
public class MainConfig2 {
//...
}
【5】FactoryBean
FactoryBean
: 是一個Java Bean,但是它是一個能生産對象的工廠Bean,它的實作和工廠模式及修飾器模式很像。
自定義工廠bean實作該接口: 這裡為該類添加了@Component注解。
@Component
//建立一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//傳回一個Color對象,這個對象會添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Color.class;
}
//是單例?
//true:這個bean是單執行個體,在容器中儲存一份
//false:多執行個體,每次擷取都會建立一個新的bean;
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
//return true;
return false;
}
}
測試如下:
public class IOCTest {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MainConfig2.class);
@Test
public void testImport(){
//工廠Bean擷取的是調用getObject建立的對象
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的類型:"+bean2.getClass());
System.out.println(bean2 == bean3);
}
result as follows :
ColorFactoryBean...getObject...
ColorFactoryBean...getObject...
bean的類型:class com.web.bean.Color
false
可以看到根據id colorFactoryBean擷取的實際bean為com.web.bean.Color!并且兩次擷取的bean不等。
如果要擷取工廠bean本身,則如下:
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
result as follows :
class com.web.bean.ColorFactoryBean
原理如下圖:
那麼Servlet、Filter和Listener如何使用代碼方式注入容器呢?
參考博文: