Spring In Action学习记录(三)高级装配

3.1 环境与profile

开发到生产阶段会有不同的环境,Spring所提供的解决方案就可以让代码在不同环境下不用重新构建

3.1.1 配置profile bean

Spring为环境相关的bean,根据环境决定创建或者不创建哪个bean,Spring不是在构建时做出决定,而是运行时,所以没有必要重新构建

在java配置类上,加上@Profile(“xxx”) ,这告诉了Spring这个配置类中的bean只有在xxx profile激活时才会创建
当然 @Profile也可以用在bean的方法上,即当规定的profile被激活时,相应的bean才会被创建,但是其他并不在任何profile作用域下的bean,无论何时都会被创建

我们也可以通过在XML文件中 < beans profile=“xxx”>元素(根元素)的profile属性来配置profile bean

同样可以在根元素< beans>下嵌套定义< beans>元素来提供多个profile作用域,而不是定义多个profile XML文件

3.1.2 激活profile

Spring通过两个属性来判断激活哪个profile

  • spring.profiles.active:启用声明的profile
  • spring.profiles.default:若没有使用spring.profiles.active,则会启用default的profile
  • 如果两个都没有,那就只用创建哪些没有在profile中的bean

有多重方式可以来设置这两个属性:

  • 作为DispatcherServlet的初始化参数
  • 作为Web应用的上下文参数
  • 作为JNDI条目
  • 作为环境变量
  • 作为JVM的系统属性
  • 在测试类中使用@ActiveProfiles(“xxx”)

在web应用的web.xml文件中设置默认的profile:

<context-param>//为上下文指定默认的profile
<param-name>spring.profiles.default</param-name>
<param-value>dev<param-value>
</context-param>
<servlet>//为servlet指定默认的profile
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev<param-value>
</init-param>
</servlet>

3.2 条件化的bean

当我们想要一个bean,这个bean只有当某种条件存在时才会被创建,那就可以使用@Conditional(xxx.class),这个xxx必须实现Condition接口:

public interface Condition{
	boolean matches(ConditionContext ctxt,AnnotatedTypeMetadata metadata);
}

只需实现matches方法,返回true,则创建哪些带有@Conditional的bean,反之不创建
ConditionContext是一个接口,大致如下:

public interface ConditionContext{
	BeanDefinitionRegistry getRegistry();//检查bean定义
	ConfigurationListableBeanFactory getBeanFactory();//检查bean是否存在,探查bean属性
	Environment getEnvironment();//检查环境变量是否存在和它的值
	ResourcesLoader getResourceLoader();//检查它加载的资源
	ClassLoader getClassLoader();//加载并检查类是否存在
}

AnnotatedTypeMetadata也是一个接口,它能检查被Conditional修饰的bean上还有什么其他的注解

@profile注解本身实现也是用了@Condition注解,并且引用了ProfileCondition作为Condition实现,ProfileCondition实现了Condition接口,简化版如下:

@Conditionl(ProfileCOndition.class)
public @interface Profile{
	String[] value();
}

3.3 处理自动装配的歧义性

即当标识@Autowired是一个接口,或者超类时,当该接口,或者超类有多个实现类,并都注册为bean,那么Spring就不知道该自动装配哪一个,这就产生了歧义

3.3.1 标识首选的bean

在子类bean上添加@Primary来标识最喜欢的方案
或者在XML中使用<bean ...... primary="true" />

3.3.2 限定自动装配的bean

使用限定符(默认为bean id),在超类@Autowired的地方使用@Qualifier(“xxx”)可以将可选bean限定到xxx

也可以自定义限定符,在想自定义限定符的bean上加上@Qualifier(“name”)
于是name就成为了该类的限定符

3.4 bean的作用域

默认情况下Spring应用上下文中所有bean都是单例的
但如果所使用的的类是易变的,会保持一些状态,,那么单例重用就不安全
于是Spring提供了多种作用域:

  • 单例(Singleton):在整个应用只会创建一个bean实例
  • 原型(Prototype):每次出入货通过Spring应用上下文获取时,都会创建新的bean实例
  • 会话(Session):在Web应用中,为每一个会话创建一个新的bean实例
  • 请求(Request):在Web应用中,为每一个请求创建一个新的bean实例

如果想使用其他作用域,就可以使用@Scope注解,如:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

3.4.1 使用会话和请求作用域

有些时候我们需要使用会话作用域,如购物车ShoppingCart类,可以将设置为:
@Scope(value=WebApplicationContext.SCOPE_SESSION),ProxyMode=ScopedProxyMode.INTERFACES)
但是当需要把ShoppingCart类注入到单例类(如StoreService)时,就会出问题,这就使用了ProxyMode属性,该属性指定了一个代理,这个代理要实现ShoppingCart接口,然后就可以通过代理和单例类打交道

3.4.2 在XML中声明作用域代理

如:

<bean id="xxx"
	class="xxx"
	scope="session">
	<aop:scoped-proxy />//要先声明aop的命名空间
</bean>

3.5 运行时值注入

  • 属性值注入
  • Spring表达式语言(SpEL)
3.5.1 注入外部的值

1.使用@PropertySource注解和Environment,如:

@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")//声明属性源
puvlic class xxx{
	@Autowired
	Environment env;
	...
	...
	public BlankDisc disc(){
		return new BlankDisc(env.getProperty("disc.title"),
							env.getProperty("disc.artist"));//检索属性值
	}
}

app.properties:

disc.title=xxx
disc.artist=xxx

如果不仅想装配String属性就可以使用重载的getProperty()

  • T getProperty(String key,Class type,T defaultValue)

解析属性占位符
Spring一直支持将属性定义放到外部的属性文件中,并使用占位符将其插入到Spring bean中,如:${...}包装属性名称
XML中可以直接使用如:c:_title="${disc.title}"
自动装配或者JavaConfig使用@Value来使用:

public BlankDisc(@Value("$disc.title") Stirng title){
	this.title=title;
}

但是要使用占位符必须先配置一个PropertySourcesPlaceholderConfigurer类,或者在XML文件中加上<context:property-placeholder />

3.5.2 使用Spring表达式语言进行装配

使用SpEL表达式会在运行时计算得到值,SpEL表达式要放到:
#{...}中,这里面的就是SpEL的表达式体,如:

#{T(System).CurrentTimeMillis()}//获取当前时间
#{sgtPeppers.artist}//引用其他bean的属性或者方法返回值
#{systemProperties['disc.title']}//通过systemProperties引用配置文件属性
#{admin.email matches '.....'}//匹配正则表达式
#{xxx.nums[4].name}//引用集合中的数

3.6 总结

发布了28 篇原创文章 · 获赞 1 · 访问量 636

猜你喜欢

转载自blog.csdn.net/c630843901/article/details/103450297