【1】@Configuration和@Bean
@Configuration
从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.Configuration; 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不同哦
【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表达式。
配置类如下:
//配置类==配置文件 @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如何使用代码方式注入容器呢?