读《Spring 实战(第四版)》第三章 高级装配

配置profile bean

Spring为环境相关的bean所提供的解决方案,3.1引入
在Java配置中,可以使用@Profile注解指定某个bean属于哪个profile

@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {
	@Bean
	public DataSource dataSource() {
	}
}
//这个配置类中的bean只有在dev profile激活时才会创建,否则忽略
·················································
@Configuration
@Profile("prod")
public class ProductionProfileConfig {
	@Bean
	public DataSource dataSource() {
	}
}

从3.2开始@Profile可以用于方法级别

public class DatasourceConfig {
	@Bean
	@Profile("dev")
	public Datasource devDatadource() {
	}
	@Bean
	@Profile("prod")
	public Datasource prodDatadource() {
	}
}

也可以在XML中配置profile

//在顶部声明中加入profile
<?xml version="1.0" encoding="UTF-8"?>
<beans
*******************************
profile="dev">

</beans>
//只有profile属性与当前激活profile相匹配的配置文件才会被用到
·························································
//可以嵌套定义<beans>元素,将环境配置都放到一个文件中
<beans>
	<beans profile="dev">
		<bean id="dataSource"/>
	</beans>
	<beans profile="prod">
		<bean id="dataSource"/>
	</beans>
</beans>
激活profile

Spring在确定哪个profile处于激活状态时,需要依赖两个独立属性:spring.profiles.activespring.profiles.default。设置了active属性,就根据active的值确定激活哪个profile,没设置就看default的值,如果都没设置,就没有激活的profile,就只会创建没有定义profilebean
可以同时激活多个,用,分隔

  • 设置方法1
    • 作为DispatcherServlet的初始化参数
    • 最为Web应用的上下文参数
    • 作为JDNI条目
    • 作为环境变量
    • 作为JVM的系统属性
    • 在集成测试上,使用@ActiveProfiles注解设置

在web.xml中设置默认的profile

<!-- 为上下文设置默认profile -->
<context-param>
	<param-name>spring.profiles.default</param-name>
	<param-value>dev</param-value>
</context-param>
<!-- 为Servlet设置默认profile -->
<servlet>
   	<servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
		<param-name>spring.profiles.default</param-name>
		<param-value>dev</param-value>
	</init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

当应用程序部署到其他环境中时,负责人根据情况使用系统属性、环境变量或JDNI设置spring.profiles.active即可。

Spring提供了@ActiveProfiles注解,可以使用他来指定运行测试时要激活哪个profile

条件化bean

比如说想要某个bean只有在应用的类路径下包含特定的库时才创建,或者当另外某个bean也声明后才创建,或者某个特定环境变量设置后才创建。
Spring 4之后引入了@Conditional注解

@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
	return new MagicBean();
}
//MagicExistsCondition 是一个实现了Condition接口的类
public interface Condition {
	boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
}
//实现matches方法,返回true,创建bean,否则不创建
//ConditionContext 可以得到很多对象来判断条件
//AnnotatedTypeMetadata 可以检查还有什么其他注解

public class MagicExistsCondition implements Condition {
	public boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata) {
		Environment env = ctxt.getEnvironment();
		return env.containsProperty("magic"); // 检查环境中是否存在名为 magic 的属性
	}
}

2
在Spring 4中,@Profile定义的时候也使用了@Conditional注解,通过实现matches方法,通过ConditionContext得到的Environment检查该profile是否处于激活状态。

自动装配的歧义

相同类型的bean,可以将其中一个设为首选

@Bean
@Primary
public Dessert iceCream() {
}
<bean id="iceCream" class="com.desserteater.IceCream" primary="true" />

但当有多个bean都设置了首选就没有了意义,还是会有歧义。
还可以使用@Qualifier限定符,在声明和注入时都可以使用

@Bean
@Qualifier("cold")
public Dessert iceCream() {
}

@Autowired()
@Qualifier("cold")
public void setDessert(Dessert dessert) {
}

这里是使用"cold"作为IceCream的一个属性来作为限定名称,假如又有个Popsicle(雪糕) bean,它也使用"cold"作为限定符,为了区分于IceCream,再添加一个限定符名称,IceCream-"cream"PopSicle-"fruity",但Javaz中不允许在同一个条目上相同类型的注解有多个,所以不能加两个限定符@Qualifier
可以将"cold"“fruity”作为两个自定义注解

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.Type})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {}

创建的自定义限定符注解要使用@Qualifier来标注
以后就可以使用@Cold来代替@Qualifier("cold")
同样有@Cream@Fruity

@Bean
@Cold
@Cream
public Dessert iceCream() {
}

@Autowired()
@Cold
@Cream
public void setDessert(Dessert dessert) {
}
bean的作用域

默认情况下,Spring应用上下文中的bean都是单例形式创建的。
Spring中定义了多种作用域:

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

可以使用@Scope注解,和@Component@Bean一起使用

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 原型
public Notepad notepad() {
	return new Notepad();
}

<bean id="notepad" class="com.myapp.Notepad" scope="prototype" />
会话作用域 和 请求作用域
@Bean
//会话
@Scope(value=WebapplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() {
	return new ShoppingCart();
}

proxyMode的作用:
假如有一个单例bean,创建的时候需要注入ShoppingCart bean,但此时会话作用域的bean还没有创建,知道某个用户进入系统才会创建;还有一点是系统将会有多个ShoppingCartbean实例,每个用户一个。
在上面的设置中,Spring会将一个ShoppingCartbean的代理注入到需要创建的bean中,这个代理会暴露相同的方法,当调用这些方法时,代理会进行懒解析3,并将调用委托给会话作用域内真正的ShoppingCartbean。
上面的ScopedProxyMode.INTERFACES表示代理要实现ShoppingCart接口;
ScopedProxyMode.TARGET_CLASSShoppingCart是一个类时使用,(会使用CGLib来生成基于类的代理),表示要以生成目标类扩展的方式创建代理4

在xml中设置:
需要顶部声明aop命名空间

<bean id="cart" class="com.myapp.ShoppingCart" scope="session">
	<aop:scoped-proxy />
</bean>
<!-- 默认情况下会使用CGLib创建目标类代理 -->
<aop:scoped-proxy proxy-target-class="false" />
<!-- 生成基于接口的代理 -->
使用外部属性来装配bean
@Configuration
@PropertySource("classpath:app.properties")
public class ExpressiveConfig {
	@Autowired
	Environment env;
	
	public BlankDisc disc() {
		return new BlankDisc(env.getProperty("disc.title"), env.getProperty("disc.artist"));
	}
}

app.properties 文件会加载到Spring的Environment中5,然后检索出属性。

Java中使用占位符:

@Bean
public BlankDisc disc() {
		return new BlankDisc(@Value("${disc.title}") String title, @Value("${disc.artist}") String artist);
	}
························································································································
@Component
public class BlankDisc {
	@Value("${disc.title}")
	private String title;
	@Value("${disc.artist}")
	private String artist;
}

xml中使用占位符:

<bean id="blankDisc" class="soundsystem.BlankDisc" c:_title="${disc.title}" c:_artist="${disc.artist}" />

为了使用占位符,必须要配置一个PropertySourcesPlaceholderConfigurer
Java中:

@Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

xml中:

<context:property-placeholder />

如果是只使用xml配置,使用占位符需要<context:property-placeholder location="classpath:config.properties" /> location=

使用Spring表达式语言

格式:#{…}

#{T(System).currentTimeMillis()}
//T()表达式会将java.lang.System是为Java中的对应类型,因此可以调用static方法

#{blankDisc.title} 
//ID为blankDisc的bean的title属性

#{systemProperties['disc.title']}
//通过systemProperties对象引用系统属性
@Value("#{systemProperties['java.version']}")
//1.8.0_181

  1. p73 ↩︎

  2. p77 ↩︎

  3. p87 ↩︎

  4. p87 ↩︎

  5. p90 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值