读《Spring实战》第二章:装配bean

引言

创建应用对象之间的协作关系的行为叫做装配,这也是依赖注入的本质

装配bean的三种可选方式

spring容器负责创建bean,通过DI来协调管理这些bean的关系,spring提供了三种方式来装配bean:

  • 在XML中显式配置
  • 在Java配置类中显式配置
  • 隐式的bean方便机制和自动装配

这三种装配方式不是只能选择一种,可以选择多种搭配使用,以适应项目的需要。书籍中推荐的是自动装配,更加的便利。

自动化装配bean

spring如何实现自动化装配bean?

  • 组件扫描:Spring会自动发现应用上下文中创建的bean。
  • 自动装配: Spring会自动满足bean之间的依赖。

组件扫描

使用@ComponetScan注解,手动开启组件扫描,如果没有对@ComponetScan注解指定目录,默认扫描当前注解所在类的包中(包含子包)。在@ComponetScan注解中既可以指定目录,也可以指定类,指定类的话就是这个类所在的包作为基础包(包含子包)。

自动装配

简单来说自动装配就是让spring自动满足bean的依赖关系,也就是在一个bean中需要使用到另外的bean,spring会自动在spring应用上下文中找到满足要求的bean。为了声明要进行自动装配,可以使用@Autowired注解。

显示配置bean

准备使用第三方库的组件装配到自己的应用中的时候,就没办法使用@Autowired和@Component注解了,这个时候就需要使用XML或者使用java配置类的方式显示配置bean。

可以使用@Configuration注解和@Bean注解组合。spring会拦截所有对@Bean注解所修饰的方法的调用,并确保直接返回该方法所创建锅的bean,而不是每次都再走一遍创建的逻辑。

条件化的bean

如果希望一个bean在另外一个bean也声明了之后才创建,或者希望配置文件中有特定的配置属性才创建bean类似这种特定的要求,可以使用@Conditional注解,如果给定的条件为true,就会创建;如果为false,就不会创建。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	Class<? extends Condition>[] value();

}



@FunctionalInterface
public interface Condition {

	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}


public interface ConditionContext {

	BeanDefinitionRegistry getRegistry();

	@Nullable
	ConfigurableListableBeanFactory getBeanFactory();

	Environment getEnvironment();

	ResourceLoader getResourceLoader();

	@Nullable
	ClassLoader getClassLoader();

}

@Conditional注解中接收的是一个数组,数组的元素类型是Condition接口的实现类。而Condition接口中只包含了一个返回布尔值的方法。
Conditional接口中提供了几个方法,可以获取到BeanDefinitionRegistry,ConfigurableListableBeanFactory,Environment,ResourceLoader,ClassLoader几个属性,借助于ConditionContext:

  • 借助BeanDefinitionRegistry 可以检查bean定义
  • 借助ConfigurableListableBeanFactory可以检查bean是否存在,甚至探查bean的属性
  • 借助Environment可以检查环境变量是否存在以及它的值是什么
  • 借助ResourceLoader可以读取加载的资源
  • 借助ClassLoader可以加载并检查类是否存在

AnnotatedTypeMetadata 能够让我们检查带有@Bean注解的方法上还有什么其他的注解,AnnotatedTypeMetadata 也是一个接口,借助isAnnotated方法,我们能够判断带有@Bean注解的方法是不是还有其他特定的注解,借助AnnotatedTypeMetadata 接口的其他方法可以检查@Bean注解的方法上其他注解的属性。

从spring4开始,@Profile注解就是使用了@Conditional和Condition接口:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {

	/**
	 * The set of profiles for which the annotated component should be registered.
	 */
	String[] value();

}


class ProfileCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			for (Object value : attrs.get("value")) {
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}

}

自动装配歧义

在使用@Autowired注解注入某一个bean的时候,如果有多个bean符合要求(同一个接口的多个实现类),这个时候spring包抛出NoUniqueBeanDefinitionException。这个时候可以使用@Primary注解来表示首选,或者使用@Qualifier注解来指定某一个。

bean的作用域

  • singleton(单例):这是Spring的默认作用域。Spring容器在第一次获取Bean时创建实例,并在后续请求中返回同一个实例。这意味着对于所有的对象访问的都是同一个Bean实例。
  • prototype(原型):对于每次请求都会创建一个新的Bean实例。这意味着每次通过Spring容器获取Bean时,都会得到一个新的实例。
  • request:这个作用域仅在web应用程序中有效。对于每个HTTP请求,Spring都会创建一个新的Bean实例,并且这个Bean实例仅在当前HTTP请求内有效。当请求结束时,Bean实例会被销毁。
  • session:这个作用域也仅在web应用程序中有效。对于每个HTTP Session,Spring都会创建一个新的Bean实例,并且这个Bean实例仅在当前HTTP Session内有效。当Session结束时,Bean实例会被销毁。
  • application(全局作用域):这个作用域在web应用程序中表示在一个ServletContext内只有一个Bean实例。也就是说,这个Bean在整个web应用程序的生命周期内都是唯一的。
  • websocket:这个作用域也在web应用程序中有效。Bean的生命周期绑定到WebSocket的生命周期上,这意味着WebSocket的每个连接都会有一个与之关联的Bean实例。

使用@Scope注解来为一个bean指定作用域,@Scope中有一个ScopedProxyMode属性,ScopedProxyMode是一个枚举,主要用于Spring框架中配置Bean的代理模式

public enum ScopedProxyMode {

	DEFAULT,

	NO,

	INTERFACES,

	TARGET_CLASS

}

NO 和 DEFAULT 的作用是一样的,都表示不使用代理,如果需要就直接创建Bean实例。
INTERFACES 表示为Bean的所有接口创建一个基于JDK的动态代理。
TARGET_CLASS 表示为Bean的类本身创建一个基于CGLIB的代理。
通过配置ScopedProxyMode,可以实现对Bean的访问控制和生命周期管理,以满足不同的业务需求。例如,在Web应用程序中,可能需要在不同的请求或会话中保持独立的Bean状态,这时就可以通过配置ScopedProxyMode来实现。

运行时值注入

使用Environment中配置的值

Environment中的方法:

// 获取配置值为string类型
String getProperty(String key);

// 为空则使用默认值
String getProperty(String key, String defaultValue);

//  转换为指定的类型
<T> T getProperty(String key, Class<T> targetType);

//   转换为指定的类型,并且有默认值
<T> T getProperty(String key, Class<T> targetType, T defaultValue);

//  不存在报错
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

// 是否包含配置key
boolean containsProperty(String key);

属性占位符

属性占位符号: ${},一般配合@Value注解使用

使用Spring表达式语言(SpEL)进行值装配

spring3引入了Spring表达式语言(SqEL),它能够以强大和简洁的方式将值装配到bean属性和构造器参数中,在这个过程中所使用的表达式会在进行时计算得到值。包括的特性有:

  • 使用bean的ID来引用bean
  • 调用方法和访问对象的属性
  • 对值进行算数,关系和逻辑运算
  • 正则表达式匹配
  • 集合操作

SpEL表达式要放到 "#{…}"之中,类似的属性占位符要放到 "${…}"之中。

  • #{1}: 1

  • #{T(System).currentTimeMillis()}: 它的最终结果是表达式的那一刻的毫秒数。T()表达式会将java.lang.System视为Java中的类型,因此可调用其static修饰的currentTimeMillis方法。

  • #{beanID.field}: beanID通常就是类名全程,第一个字母小写,例如: #{dog.name},同样的,也可以调用bean的方法。

  • #{systemProperties[xxx.yy]} : 就是从配置文件中取出 xxx.yy的配置值

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值