概述
Bean的高级装配
一.环境与profile
Spring为环境相关的bean所提供的解决方案其实与构建时的方案没有太大差别,在此过程中要根据环境决定该创建哪个bean和不创建哪个bean,不过Spring并不是在构建时做出这样的决策,而是等到运行时再来确定,,这样的结果就是同一个部署单元(可能会是WAR文件)能够适用于所有的环境,没用必要进行重新构建。
Spring3.1版本中引入bean profile功能,使用profile,需要讲所有不同的bean定义整理到一个或多个profile之中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)的状态。
Java配置中,可使用@Profile注解指定某个bean属于哪一个profile,例如数据源的配置:
package com.huang.configuration;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Configuration
@Profile("daemon")
public class Datasourceconfing {
@Bean
public DataSource datasource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/library");
ds.setUsername("root");
ds.setPassword("709498");
return ds;
}
}
要如何激活Profile呢,spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性spring.profiles.active和spring.profiles,defaut。前面active的优先级大于default的优先级,若都没设置则只会创建没有定义profile中的bean,有多种方式来设置这两个属性:
- 作为DispatcherServlet的初始化参数;
- 作为web应用的上下文参数;
- 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试类上,使用@ActiveProfiles注解设置;
例如在Servlet上下文设置,在Web应用中:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 为servlet激活或设置默认profile以此条件装配bean-->
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>daemon</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 为上下文激活或设置默认profile以此条件装配bean-->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>daemon</param-value>
</context-param>
</web-app>
使用测试类来判断是否创建和装配成功:
package com.huang;
import com.huang.configuration.Datasourceconfing;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Datasourceconfing.class})
@ActiveProfiles("daemon")
public class test {
@Autowired
private Datasourceconfing ds;
@Test
public void testdemo(){
System.out.println(ds);
}
}
最后输出创建并注入的bean地址:
com.huang.configuration.Datasourceconfing$$EnhancerBySpringCGLIB$$8a313bb0@776aec5c
由上可知,使用测试类注解@ActiveProfiles激活profile没有问题。
二.条件化的Bean
若希望一个或多个bean只有在应用的类路径下包含特定的库时才创建,或者我们希望某个bean只有当另外某个特定的bean也声明之后才会创建,我们还可能也要求某个特定的环境变量设置之后,才会创建某个bean。
Spring 4之前很难实现这种级别的配置,但是Spring 4引入新的@Conditional注解,它可以应用到带有@Bean注解的方法上,若给定的条件计算结构为true就会创建这个bean,否则就会忽略这个bean。
有意思的是Spring 4开始@Profile注解也是基于@Conditional和Condition实现。即@Profile本身也使用了@Conditional注解,并且引用ProfileCondition作为Condition实现。ProfileCondition实现Condition接口,并在做出决策的过程中,即matches()方法中考虑到了ConditionContext和AnnotatedTypeMetadata多个因素。
配置类中声明如下:
package com.huang.configuration;
import com.huang.domain.MagicBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Datasourceconfing {
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean(){
return new MagicBean();
}
}
通过实现Condition接口的matches方法判断环境变量中是否含有magic属性
package com.huang.configuration;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext ct, AnnotatedTypeMetadata at) {
Environment evn=ct.getEnvironment();
//检查环境变量中是否含有magic属性
return evn.containsProperty("magic");
}
}
测试:
package com.huang;
import com.huang.configuration.Datasourceconfing;
import com.huang.domain.MagicBean;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Datasourceconfing.class})
public class test {
@Autowired
private MagicBean mb;
@Test
public void testdemo(){
System.out.println(mb);
}
}
三.处理自动装配的歧义性
使用自动装配会让Spring完全负责将bean引用注入到构造参数和属性中,自动装配很有效,它会减少装配应用程序组件所需要的显示配置的数量。
不过,仅有一个bean匹配所需结果时,自动装配才有效。若不仅只有一个bean匹配结果的话,会阻碍Spring自动装配属性、构造器参数或方法参数。
1.标识首选bean
选择其中一个为首选的(primary)bean能够避免自动装配时的歧义性。使用@Primary注解与@Component组合在组件扫描的bean上,也可在Java配置中使用@Primary和@Bean组合使用为首选的bean。
实例实现类:
//接口
package com.huang.service;
public interface Dessert {
void shape();
}
//实现类一
package com.huang.service.DessertImpl;
import com.huang.service.Dessert;
public class Cookie implements Dessert {
@Override
public void shape() {
System.out.println("圆的");
}
}
//实现类二
package com.huang.service.DessertImpl;
import com.huang.service.Dessert;
public class IceCream implements Dessert {
@Override
public void shape() {
System.out.println("锥形的");
}
}
示例Java配置代码:
package com.huang.configuration;
import com.huang.service.Dessert;
import com.huang.service.DessertImpl.Cookie;
import com.huang.service.DessertImpl.IceCream;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration
@Profile("daemon")
public class Datasourceconfing {
//首选bean,primary解决歧义
@Bean
@Primary
public Dessert icecream(){
return new IceCream();
}
@Bean
public Dessert cookie(){
return new Cookie();
}
}
可在测试中进行自动装配
package com.huang;
import com.huang.configuration.Datasourceconfing;
import com.huang.domain.MagicBean;
import com.huang.service.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Datasourceconfing.class})
@ActiveProfiles("daemon")
public class test {
//测试自动装配的1歧义性
@Autowired
private Dessert dt;
@Test
public void testdemo(){
System.out.println(ds);
System.out.println(mb);
//测试输出默认主对象注入
dt.shape();
}
}
自动装配会在spring容器中自动选择带有注解primary的bean。
输出显示为:
锥形的
但问题是,标记多个首选的bean时又会产生新的歧义,此时相当于没有首选了,解决歧义性问题而言,限定符是一种更强大的机制。
2.限定自动装配的bean
Spring限定符能在所有可选bean的范围内缩小范围,最终只有一个bean符合条件。
@Qualifier注解是使用限定符的重要方式,可与@Autowired和@Inject协同使用。
三个Dessert接口的实现类如下:
package com.huang.service.DessertImpl;
import com.huang.service.Dessert;
public class Cookie implements Dessert {
@Override
public void shape() {
System.out.println("圆的");
}
}
package com.huang.service.DessertImpl;
import com.huang.service.Dessert;
public class IceCream implements Dessert {
@Override
public void shape() {
System.out.println("锥形的");
}
}
package com.huang.service.DessertImpl;
import com.huang.service.Dessert;
public class Lollipop implements Dessert {
@Override
public void shape() {
System.out.println("棒棒糖型的");
}
}
详细实体配置代码如下:
package com.huang.configuration;
import com.huang.service.Dessert;
import com.huang.service.DessertImpl.Cookie;
import com.huang.service.DessertImpl.IceCream;
import com.huang.service.DessertImpl.Lollipop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class Domainconfing {
//首选bean,解决歧义
@Bean
@Primary
public Dessert icecream(){
return new IceCream();
}
@Bean
public Dessert cookie(){
return new Cookie();
}
@Bean
public Dessert Lollipop(){
return new Lollipop();
}
}
相比于@Primary,@Qualifier具有更高的优先级,具体代码及输出如下:
package com.huang;
import com.huang.configuration.Domainconfing;
import com.huang.service.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Domainconfing.class})
public class test1 {
@Autowired
//限定符确定唯一bean
@Qualifier("Lollipop")
private Dessert dt;
@Test
public void testdemo(){
//测试输出默认主对象注入
dt.shape();
}
}
输出为:
八月 31, 2020 9:10:26 上午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
八月 31, 2020 9:10:26 上午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@47c62251, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@3e6fa38a, org.springframework.test.context.support.DirtiesContextTestExecutionListener@66a3ffec, org.springframework.test.context.transaction.TransactionalTestExecutionListener@77caeb3e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@1e88b3c, org.springframework.test.context.event.EventPublishingTestExecutionListener@42d80b78]
棒棒糖型的
所有使用@Component注解声明的bean默认的限定符名称为首字母改为小写,使用显式配置则与返回bean的方法名相同。
创建自定义限定符只需要在声明bean时使用@Qualifier即可,即@Qualifier与@Component和@Bean联用,如:
//Java显式配置中
@Bean
@Qualifier("XiaoHuang")
public Dessert cookie(){
return new Cookie();
}
//使用隐式配置
@Component
@Qualifier("XiaoHuang")
public class Cookie implements Dessert {
@Override
public void shape() {
System.out.println("圆的");
}
}
若两个bean具有相同的限定符,通常限定符不是随便取名,而是根据其特性,上述例子中,甜品有冷热之分,对于两个bean都有冷@Qualifier(“cold”)限定,我们可以在其下一级再次使用不同的限定符,例如具体的名称,实例,由于java不允许出现同类型的多个注解,故使用自定义注解代码如下:
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
package com.huang.annotions;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cream {
}
package com.huang.annotions;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Sicle {
}
将自定义注解用于显式java配置中,如下:
package com.huang.configuration;
import com.huang.annotions.Cold;
import com.huang.annotions.Cream;
import com.huang.annotions.Sicle;
import com.huang.service.Dessert;
import com.huang.service.DessertImpl.Cookie;
import com.huang.service.DessertImpl.IceCream;
import com.huang.service.DessertImpl.Lollipop;
import com.huang.service.DessertImpl.Popsicle;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class Domainconfing {
//首选bean,解决歧义
@Bean
//使用自定义限定注解
@Cold
@Cream
public Dessert icecream(){
return new IceCream();
}
@Bean
@Qualifier("XiaoHuang")
public Dessert cookie(){
return new Cookie();
}
@Bean
public Dessert lollipop(){
return new Lollipop();
}
@Bean
//使用自定义注解
@Cold
@Sicle
public Dessert popsicle(){
return new Popsicle();
}
}
在注入点使用自定义的限定符注解,测试装配是否成功:
package com.huang;
import com.huang.annotions.Cold;
import com.huang.annotions.Sicle;
import com.huang.configuration.Domainconfing;
import com.huang.service.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={Domainconfing.class})
public class test1 {
@Autowired
//限定符确定唯一bean
@Cold
@Sicle
private Dessert dt;
@Test
public void testdemo(){
//测试输出默认主对象注入
dt.shape();
}
}
输出:
八月 31, 2020 10:23:32 上午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
八月 31, 2020 10:23:32 上午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@42e26948, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@57baeedf, org.springframework.test.context.support.DirtiesContextTestExecutionListener@343f4d3d, org.springframework.test.context.transaction.TransactionalTestExecutionListener@53b32d7, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@5442a311, org.springframework.test.context.event.EventPublishingTestExecutionListener@548e7350]
哈哈,冰棍
在本节和前面章节中,讨论了几种通过自定义注解扩展Spring的方式,为了创建自定义的条件化注解,我们创建·一个性的注解并在这个注解上添加@Conditional。为创建自定义限定符注解,我们创建一个新的注解,并在这个注解上添加了@Qualifier注解。这种技术可以用到很多Spring注解中去,从而形成特定功能的自定义注解。
四.bean的作用域
默认Spring上下文中,bean为单例形式,即每次注入都是同一实例。Spring定义了多种定义域,可基于这些定义域创建bean。
- 单例(Singleton):在整个应用中,只创建一个bean的实例。
- 原型(Prototype):每次注入或者通过Spring上下文获取的时候,都会创建一个新的bean实例。
- 会话(Session):在Web应用中,为每个会话创建一个bean实例。
- 请求(Request):在Web应用中,为每个请求创建一个bean实例。
单例为默认的作用域,要使用其它作用域,要使用@Scope注解,与@Component和@Bean一起使用。
使用隐式@Component和@ComponentScan,创建的bean和配置如下
//weapon接口
package com.huang.service;
public interface Weapon {
void Operation();
}
//实现一
package com.huang.service.WeaponImpl;
import com.huang.annotions.Weapon.ModernArms;
import com.huang.service.Weapon;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@ModernArms
//定义域为原型,这里可以写为@Scope("prototype"),但以下方式更为安全
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Gun implements Weapon {
@Override
public void Operation() {
System.out.println("射击...");
}
}
//实现二
package com.huang.service.WeaponImpl;
import com.huang.annotions.Weapon.ColdArms;
import com.huang.service.Weapon;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@ColdArms
//会话和请求作用域
@Scope(.SCOPE_SINGLETON)
public class Spear implements Weapon {
@Override
public void Operation() {
System.out.println("刺...");
}
}
//配置
package com.huang.configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.huang.service.WeaponImpl")
public class WeaponConfing {
}
使用测试:
package com.huang;
import com.huang.annotions.Weapon.ColdArms;
import com.huang.configuration.WeaponConfing;
import com.huang.service.Weapon;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=WeaponConfing.class)
public class test2 {
@Autowired
@ColdArms
private Weapon wp;
@Test
public void testdemo(){
//测试输出默认主对象注入
TestCase.assertNotNull(wp);
wp.Operation();
}
}
测试结果:
八月 31, 2020 4:05:34 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
八月 31, 2020 4:05:34 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@7d9d1a19, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@39c0f4a, org.springframework.test.context.support.DirtiesContextTestExecutionListener@1794d431, org.springframework.test.context.transaction.TransactionalTestExecutionListener@42e26948, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@57baeedf, org.springframework.test.context.event.EventPublishingTestExecutionListener@343f4d3d]
刺...
对于Web应用来说,对于典型的电子商务来说,将购物车作为一个bean的话,如果采用单例模式,那么所有的用户都共用一个购物车,这显然是不行的,若采用原型,用户在应用的一处加入购物车后,另一场加入后,前面加入的就没用了,相当于又创建了一个性的购物车,数据不能共存。使用WebApplicationContext的会话属性即
SCOPE_SESSION,它会为当前会话创建一个bean,对于整个应用来说会有多个会话即可产生多个bean,但每个单独的bean对于当前会话来说就是单例的。
值得注意的@Scope中还有另外一个属性即proxyMode,简单了解一下应用场景,Spring上下文中的一个单例bean,例如StoreService,有会话作用域ShoppingCourt注入,由于没有当时没有会话登入,Spring并不会将实际的一个ShoppingCourt bean注入,即使用一个该bean的代理接口,当,真真有会话登入时,StoreService调用ShoppingCourt方法时,代理才会对其进行懒解析并将调用委托给会话作用域内真正的ShoppingCourt bean。
proxyMode的ScopedProxyMode.INTERFACES表明要代理要实现这个接口,ScopedProxyMode.TARGET_CLASS表明是创建基于类的代理。
五.运行时注入
依赖注入通常是将一个bean的引用注入到另一个bean的属性或构造器参数中去,通常为对象之间的关联,但也有将值注入到bean的属性或构造器参数中,这些这可采用硬编码的方式,但有时,我们又想让这些值在运行时确定,Spring提供两种方式:
- 属性占位符。
- Spring表达式语言(SpEL)。
1.注入外部的值
最简单的方式就是通过声明属性源,并通过Spring的Enviroment来检索属性,具体例子用外部属性来装配bean。
//实体类
package com.huang.domain;
public class BlankDisk {
private String title;
private String artist;
public BlankDisk(String title,String artist) {
this.title=title;
this.artist=artist;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
}
//Java配置
package com.huang.configuration;
import com.huang.domain.BlankDisk;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
//外部属性文件路径
@PropertySource(value="classpath:/com/huang/properties/MusicList.properties",encoding = "UTF-8")
//可能会有乱码,故再次将文本编码设为utf-8
public class Diskconfing {
@Autowired
Environment ev;
@Bean
public BlankDisk blankDisk(){
return new BlankDisk(ev.getProperty("blankdisk.title"),ev.getProperty("blankdisk.artist"));
}
}
要读取的的properties文件如下:
blankdisk.title=烟火里的尘埃
blankdisk.artist=华晨宇
测试输出Bean的信息如下:
package com.huang;
import com.huang.configuration.Diskconfing;
import com.huang.domain.BlankDisk;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= Diskconfing.class)
public class test3 {
@Autowired
private BlankDisk bl;
@Test
public void testdemo(){
TestCase.assertNotNull(bl);
System.out.println("歌名:"+bl.getTitle()+"n歌手:"+bl.getArtist());
}
}
输出:
歌名:烟火里的尘埃
歌手:华晨宇
2.使用Spring表达式语言进行装配
SpEL特性:
- 使用bean的ID来引用bean。
- 调用方法和访问对象的属性。
- 对值进行算术、关系和逻辑运算。
- 正则表达式匹配。
- 集合操作。
当然,SpEL还可以应用在依赖注入的其它地方,例如Spring Security支持使用SpEL表达式定义安全限制规则。另外,如果你在Spring MVC中使用thymeleaf模板作为视图的话,那么这些模板可以使用SpEL表达式引用模型数据。
其基本格式为:#{Expression}
表达式中使用类型:T(),例如:
#{T(java.lang.Math).PI}
基本运算符:
一些其它运算符:
- 查询运算符:(.?[]),对集合进行过滤,得到集合的子集合。(.1)和(.$[])分别为查询第一个匹配和最后一个匹配的。
- 投影运算符:(.![]),从集合选择特定属性的成员放到另一集合中。
六.总结
第二章基本的bean装配之外,我们又学习了强大的高级装配。首先,我们学习了Spring Profile,解决了跨环境的部署问题,运行时,通过将环境相关的bean与当前激活的Profile进行匹配,Spring能够让相同的部署单元跨多种环境运行,而不需要重写构建。
Spring 4提供一种够味通用的方式,通过这种方式能够声明某些bean的创建是否要依赖于给定条件的输出结果,,即结合使用@Conditional注解和Spring Conditon接口,为开发人员提供了一种强大而灵活的机制,实现条件话的创建bean.。
以及两种解决自动装配歧义的方法,即首选bean和限定符即@Primary和@Qualifier,以及解决某些限定符相同时,多级限定符的自定义限定符解决方案,将范围缩小到只有一个bean符合范围。
大多数bean都是以单例方式创建的,但有时其它创建策略更为合适。Spring能够让bean以单例、原型、会话、请求作用域的方式来创建。声明请求和会话作用域的bean时,还要学会如何创建作用域代理,它分为基于类的代理和基于接口的代理。
最后就是SpEL,可在运行时计算注入到bean中的值。
参考文献:[Spring in action fourth edition] CraigWalls
张卫滨 译
↩︎
最后
以上就是耍酷月光为你收集整理的Spring Bean的高级装配以及简单实现Bean的高级装配的全部内容,希望文章能够帮你解决Spring Bean的高级装配以及简单实现Bean的高级装配所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复