Spring使用注解驱动开发一组件添加/Bean的注入

【1】@Configuration和@Bean

@Configuration

从Spring3.0开始,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContextAnnotationConfigWebApplicationContext类进行扫描,并用于构建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不同哦

关于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表达式。

配置类如下:

//配置类==配置文件
@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如何使用代码方式注入容器呢?

参考博文:

ServletContainerInitializer注册JAVA组件

Servlet3.0 - Servlet、Filter和Listener(注解注册)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值