从Spring3.0开始,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的
,作用为:配置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是一个方法级别上的注解,效果等同于在xml中配置一个bean,并设置其属性。
xml配置如下:
等同于如下:
//给容器中注册一个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的作用域。
xml配置中必不可少的标签,用来扫描bean并注册到IOC容器中。
xml配置示例如下:
其中,type有五种形式:
其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;
}
}
@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扩展如下图:
@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) {
// TODO Auto-generated method stub
//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.
* Note that {@link BeanDefinitionRegistryPostProcessor} types may not 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 {
//...
}
FactoryBean : 是一个Java Bean,但是它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像。
其接口如下:
/**
* Interface to be implemented by objects used within a {@link BeanFactory} which
* are themselves factories for individual objects. If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
*
* NB: A bean that implements this interface cannot be used as a normal bean.
* A FactoryBean is defined in a bean style, but the object exposed for bean
* references ({@link #getObject()}) is always the object that it creates.
*
*
FactoryBeans can support singletons and prototypes, and can either create
* objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
* interface allows for exposing more fine-grained behavioral metadata.
*
*
This interface is heavily used within the framework itself, for example for
* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
* custom components as well; however, this is only common for infrastructure code.
*
*
{@code FactoryBean} is a programmatic contract. Implementations are not
* supposed to rely on annotation-driven injection or other reflective facilities.
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
* the bootstrap process, even ahead of any post-processor setup. If you need access
* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
*
*
Finally, FactoryBean objects participate in the containing BeanFactory's
* synchronization of bean creation. There is usually no need for internal
* synchronization other than for purposes of lazy initialization within the
* FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 08.03.2003
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public interface FactoryBean {
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
*
If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
*
As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
*
This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
*
In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
*
This method can be called before this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
*
NOTE: Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
Class> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
*
NOTE: If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
*
The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
*
NOTE: This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
boolean isSingleton();
}
自定义工厂bean实现该接口:
@Component
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean {
//返回一个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如何使用代码方式注入容器呢?
参考博文:
ServletContainerInitializer注册JAVA组件;
Servlet3.0 - Servlet、Filter和Listener(注解注册)