《Spring实战》学习笔记-------Bean

1. Bean的生命周期

Bean装配到Spring应用上下文中的生命周期:

  1. 实例化
  2. 填充属性:将值和bean的引用注入到bean对应的属性中
  3. 调用BeanNameAware的setBeanName()方法:如果实现该接口,将bean的ID传递给方法
  4. 调用BeanFactoryAware的setBeanFactory()方法:如果实现该接口,将BeanFactory容器实例传入
  5. 调用ApplicationContextAware的setApplicationContext()方法:如果实现该接口,将bean的应用上下文的引用传进来
  6. 调用BeanPostProcessor的预初始化方法:如果实现BeanPostProcessor接口,调用BeanPostProcessorBeforeInitialization方法
  7. 调用InitializingBean的afterPropertiesSet()方法,如果实现InitializingBean接口,调用afterPropertiesSet方法
  8. 调用自定义的初始化方法
  9. 调用BeanPostProcessor的初始化后方法,如果实现了BeanPostProcessor接口,调用BeanPostProcessorAfterInitialization方法
  10. -----bean可以使用了
  11. -----容器关闭
  12. 调用DisposableBean的destroy()方法
  13. 调用自定义的销毁方法

2. Spring模块

《Spring实战》学习笔记-------Bean

3. 自动化装配Bean(重点记前两个)


3.1 隐式的bean发现机制和自动装配*

@Component(“自定义name”)注解:Spring会为这个类创建bean

@ComponentScan注解:Spring会扫描该类所在包的所有带有@Component的类,并创建一个bean。
可以指定要扫描的包:@ComponentScan(basePackages={“packagename”}或basePackgeClasses={xx.class})

@ContextConfiguration(classes=xxx.class):会告诉Spring在xxx中加载配置。

为了避免@Autowired注解抛出“没有发现匹配bean”异常,可以@Autowired(required=false)


3.2 Java中显式配置*

除了自动装配的方式还可以使用JavaConfig的方式(使用第三方组件时):在Config类上加上@Configuration注解表面该类是配置类。

使用@Bean注解表明得到bean实例:

@Bean 
public T xxx1(){
	return new xxx1();
}

需要有别的bean依赖时:

@Bean public T xxx2(){//使用调用上面bean方法的形式,但实际上Spring会拦截对它的调用
	return new xxx2(xxx1());
}@Bean 
public T xxx2(T xx){
	return new xxx2(xx);
}

所有的bean都是单例的,一个bean实例可以注入到多个其它bean中。


3.3.XML装配Bean(古老方式)

STS中创建 File->New->Spring Bean Configuration File

声明bean:<bean id="bean1" class="Package.Class" />

3.3.1 构造器注入bean引用:

<bean id="bean2" class="Package.Class" >
	<constructor-arg ref="bean1" />
</bean>

也可以使用c-命名空间:引入声明:xmlns:c="http://www.springframework.org/schema/c"

<bean id="bean2" class="Package.Class" 
	c:cd-ref="bean1"/> cd是构造器参数名,-ref表面这是一个引用
	c:_0-ref="bean1"/> 参数索引
	c:_-ref="bean1"/> 因为只有一个构造器参数

3.3.2 构造器注入字面量

<bean id="bean3" class="Package.Class" >
	<constructor-arg value="this is a string1" />
	<constructor-arg value="this is a string2" />
</bean>

3.3.3 构造器装配集合

例如:需要装配的Bean中有一个List属性

可以直接传入string

<bean id="bean4" class="Package.Class" >
	<constructor-arg value="this is a string1" />
	<constructor-arg>
		<list>
			<value>string</value>
			<value>string</value>
			<value>string</value>
		</list>
	</constructor-arg>
</bean>

可以引用别的bean

<bean id="bean5" class="Package.Class" >
	<constructor-arg value="this is a string1" />
	<constructor-arg>
		<list>
			<ref bean="bean1">
			<ref bean="bean2">
		</list>
	</constructor-arg>
</bean>

3.3.4 属性注入(自动调用setter方法)

例如对它进行属性注入:

public class Bean1 {
	private Bean2 bean2;
	
	@Autowried
	public void setBean2(Bean2 bean2){
		this.bean2 = bean2;
	}

	public void play(){
		bean2.method();
	}
}
<bean id="bean2" class="Package.Bean2" />

<bean id="bean1" class="Package.Bean1" >
	<property name="bean2" ref="bean2"/>
</bean>

还有一种p-命名空间的方式,类似c-命名空间,先引入声明,p:bean2-ref="bean2" :p(前缀)bean2(属性名)ref(表面引用)“bean2”(所注入bean的id)

同理,还可以将字面量注入属性中,只需将上面的代码块中property标签中的"ref"属性改成"value"。

3.4 导入和混合配置

(个人理解:类似于java中在类中引用别的Package的类要先import一样)

在一个Config类(@Configuration)中引入另一个或多个Config类(@Configuration):

@Configuration
@Import({BConfig.class})
public class AConfig{
	@Bean
	...
}

在JavaConfig中引入XML配置的bean:

@ImportResource("classpath:xxxx.xml")

在XML配置中引用XML:

在根标签下: <import resoune="xxxx.xml" />

在XML配置中引用JavaConfig:只能将config类作为bean配置进来

在根标签下: <bean class="xxxx.JavaConfig" />

4. profile注解和标签

@profile(“xx”)注解:只有当xx profile处于**时才创建该bean(可以写在类级别和方法级别)

Spring 在确定哪个profile处于**状态时,需要依赖两个独立的属性:Spring.profiles.active和Spring.profiles.default。如果设置了Spring.profiles.active的话,那么它的值就会用来确定哪个profile是**的。但如果没有设置该属性,那Sping会查找Spring.profiles.default的值。如果这两个属性均没有设置,那就没有**的profile,因此指挥创建那些没有定义在profile中的bean。

书上给了在web.xml中设置默认的profile代码:

.....
<context-param>
	<param-name>spring.profiles.default</param-name>
	<param-value>dev</param-value>
</context-param>

<servlet>
	...
	<init-param>
		<param-name>spring.profiles.default</param-name>
		<param-value>dev</param-value>
	</init-param>
	...
</servlet>

测试时可以使用@ActiveProfiles(“dev”)**"dev"profiles。

5. 条件化的Bean*

如果需要限定bean的特定创建条件,可以使用@Conditional注解:如果给定的条件计算为true就会创建bean,否则不会创建。

假设有一个MagicBean的类,我们希望设置了magic属性它才被创建:

@Bean
@Conditional(MagicExistsCondition.class)---在本例中它指明的条件为MagicExistsCondition
public MagicBean magicBean(){
	return new MagicBean();
}

@Conditional注解通过Condition接口进行条件对比:

public interface Condition(){
	boolean matches(ConditionContext ctxt, AnnotatedTypeMeteadata metedada);
}

@Conditional中设置的类必须实现了Condition接口。通过matches的返回值判断。

在本例中的MagicExistsCondition:

public class MagicExistsCondition implements Condition{
	
		public boolean matches(ConditionContext ctxt, AnnotatedTypeMeteadata metedata){
			Environment env = context.getEnvironment();//通过给定的上下文获取当前环境
			return env.containsProperty("magic");//检查环境中有没有magic属性
		}
}

ConditionContext是一个接口:

public interface ConditionContext{
	BeanDefinitionRegistry  getRegistry();
	ConfigurableListableBeanFactory  getBeanFactory();
	Environment  getEnvironment();
	ResourceLoader  getResourceLoader();
	ClassLoader  getClassLoader();
}
  • getRegistry()可以用来检查bean定义
  • getBeanFactory()可以用来检查bean是否存在,探查bean的属性
  • getEnvironment()可以用来检查环境变量是否存在以及它的值
  • getResourceLoader()可以用来检查ResourceLoader加载的资源
  • getClassLoader()可以用来检查ClassLoader加载或检查类是否存在

AnnotatedTypeMetedata也是一个接口,可以检查带有@Bean注解的方法上还有什么其它的注解

public interface AnnotatedTypeMetedata{
	boolean isAnnotated(String annotationType); //判断这个方法上面有没有annotationType这个注解
	Map<String, Object>  getAnnotationAttributes(String annotationType);
	Map<String, Object>  getAnnotationAttributes(String annotationType, boolean classValuesAsString);
	MutiValueMap<String, Object>  getAllAnnotationAttributes(String annotationType);
	MutiValueMap<String, Object>  getAllAnnotationAttributes(String annotationType, boolean classValuesAsString);
}

6. 处理自动装配的歧义性*(需要装配的bean有多个选择时)

如果有多个bean符合@AutoWired装配,会抛出NoUniqueBeanDefinitionException。

出现这类问题可以将bean中的某一个设为首选(primary)的bean,也可以使用限定符(qualifier)。

6.1 标示首选的bean

自动装配式bean:

@Component
@Primary
public bean1 xxx(){}

Java配置式bean:

@Bean
@Primary
public bean1 xxx(){return new xxx();}

XML配置bean:

<bean id="bean1" class="Package.Class" primary="true" />

6.2 限定符方式

这是利用bean的id的方式:

@Autowired
@Qualifier("beanId")//创建bean时如果没有指定bean的id,会默认将首字母小写的类名作为id
public void setBean(Bean bean){
	this.bean = bean;
}

@Qualifier标签的声明位置不同,相应的作用也不同:声明在@Bean/@Component时代表给这个bean添加限定符;声明在@Autowired时代表使用这个限定符的bean。如下所示:

@Component
@Qualifier("cold") //添加了一个cold的限定符
public class IceCream implements Dessert{....}@Bean
@Qualifier("cold")
public Dessert iceCream(){
	return new IceCream();
}
@Autowired
@Qualifier("cold")//使用cold限定符的bean注入
public void setDessert(Dessert dessert){
	this.dessert = dessert;
}

如果此时又出现了一个带有相同限定符的bean:

@Component
@Qualifier("cold") 
public class Popsicle implements Dessert{....}

java中不允许在同一个条目上重复出现相同的注解,这时候我们可以自定义一个限定符注解:

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, 
		 ElementType.METHOD, ElementType.TYPE})//可作用的范围,构造器、属性、方法、类
@Retention(RetentionPolicy.RUNTIME)//程序运行时可见,生命周期最长
@Qualifier
public @interface IceCream{};//这里为了方便阅读直接设置为icecream,在实际应用中尽量不要和bean名相同

另一个注解
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, 
		 ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Popsicle{};

上层的cold注解
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, 
		 ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold{};

声明bean时:

@Component
@Cold //添加了一个cold的自定义限定符
@IceCream //添加了一个IceCream的自定义限定符
public class IceCream implements Dessert{....}

@Component
@Cold //添加了一个cold的自定义限定符
@Popsicle //添加了一个Popsicle的自定义限定符
public class Popsicle implements Dessert{....}

使用时:

@Autowired
@Cold
@IceCream //注入icecream bean
public void setDessert(Dessert dessert){
	this.dessert = dessert;