讨论依赖注入的时候,我们通常所讨论的是将一个Bean的引用注入到另一个Bean的属性或构造器参数中。它通常来讲指的是将一个对象与另一个对象关联。
另一个方面,将一个值注入到Bean的属性或者构造器参数中!
public class SgtPeppers implements CompactDisc, BeanNameAware
{
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing" + title + "by" + artist);
}
public void setBeanName(String s) {
System.out.println(s);
}
}
之前我们是使用硬编码的模式,把string写死。
我们有的时候想要在运行的时候再确定到底想要什么值注入进去。
Spring有两种在运行的时候求值的方式:
- 属性占位符
- spring表达式
3.5.1 注入外部的值
需要注入的值可以放在一个外部的properties文件当中,这些属性文件会加载到Spring的Environment中
disc.title=Sgt. Pepper's Lonely Hearts Club Band
disc.artist=The Beatles
运行是注入:
@Configuration
@PropertySource("classpath:app.properties")
public class ExpressiveConfig {
@Autowired
Environment environment;
@Bean
public BlankDisc disc (){
return new BlankDisc(environment.getProperty("disc.title"), environment.getProperty("disc.artist"));
}
}
深入学习Spring的environment
getProperty()并不是唯一一个获取属性的方法:
String getProperty(String key)
String getProperty(String key, String defalutValue) //在获取不到的时候返回默认值
T getProperty(String key, Class<T> type) //可以进行类型转换,传入Integer.class就可以返回Int
T getProperty(String key, Class<T> type, T defaultValue)
除了属性相关功能之外,
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ExpressiveConfig.class)
@ActiveProfiles("dev") //我们开启dev
//@ComponentScan
public class TestAdv {
@Autowired
@Qualifier("disc")
CompactDisc compactDisc;
@Test
public void testFun() {
System.out.println(compactDisc);
compactDisc.play();
}
}
@Configuration
@PropertySource("classpath:app.properties")
public class ExpressiveConfig {
@Autowired
Environment environment;
@Bean
public BlankDisc disc (){
System.out.println(Arrays.deepToString(environment.getActiveProfiles()));
System.out.println(environment.acceptsProfiles("dev"));
return new BlankDisc(environment.getProperty("disc.title"), environment.getProperty("disc.artist"));
}
}
运行结果
[dev]
true
SPEL.BlankDisc@6c80d78a
Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
--------track------
我们开启了dev这个环境后,在env里面就可以获得profile [dev], acceptProfiles 这个方法则是用来判断你传入的字符串数组,这里是【dev】是不是被激活的。
之前那个conditional的例子里面用到过,
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((String[]) value)) {
return true;
}
}
return false;
}
return true;
}
}
从context中可以拿到env,就像我们上面看的那样,是环境。
从metaData,可以获取这个Bean上所有的注解,getAllAnnotationAttributes拿到所有profile相关的注解,然后再拿value,就可以拿到profile的数组
最后调用acceptProfile来判断@Profile上面标注的环境和context里面的环境是不是一样的,一样的才产生这个bean。
3.5.2解析属性占位符
Spring一直支持将属性定义到外部的属性文件中,并使用占位符值将其插入到SpringBean中。${...}是占位符的形式,以下是例子,
自动扫描
public BlankDisc(@Value("${disc.title}") String title, @Value("${disc.artist}") String artist) {
this.title = title;
this.artist = artist;
}
手动声明
@Bean
public BlankDisc disc (@Value("${disc.title}") String title, @Value("${disc.artist}") String artist){
System.out.println(Arrays.deepToString(environment.getActiveProfiles()));
System.out.println(environment.acceptsProfiles("dev"));
// return new BlankDisc(environment.getProperty("disc.title"), environment.getProperty("disc.artist"));
return new BlankDisc(title, artist);
}
3.5.3 使用Spring表达式语言进行装配
SpEL拥有很多特性,包括:
使用bean的id来引用bean;
调用方法和访问对象的属性;
对值进行算数,关系和逻辑运算;
正则表达式匹配;
集合操作
和占位符不同的是,Spring表达式需要放在 #{....}中
例子:这个systemProperties是自带的变量,似乎需要放在虚拟机参数里面。-Ddisc.artist=li 放在外部的文件中似乎不管用???
SPEL 基础
- 表示字面值 #{3.1415}
- 引用bean、属性和方法
- 在表达式中使用类型
- Spel运算符
- 计算正则表达式
- 计算集合
2,引用bean属性和方法
@Component
public class CDPlayer {
public CompactDisc compactDisc;
public CDPlayer(@Value("#{blankDisc}") CompactDisc compactDisc) {
this.compactDisc=compactDisc;
}
}
public CDPlayer(@Value("#{blankDisc}") CompactDisc compactDisc, @Value("#{blankDisc.getArtist()?.toUpperCase()}") String artist) {
this.compactDisc=compactDisc;
this.artist=artist;
}
3,在表达式中使用类型
public CDPlayer(@Value("#{blankDisc}") CompactDisc compactDisc, @Value("#{T(java.lang.Math).PI}") String artist) {
this.compactDisc=compactDisc;
this.artist=artist;
}
4,SpEL运算符
public CDPlayer(@Value("#{blankDisc ?: 'Rattle and Hum'}") CompactDisc compactDisc, @Value("#{T(java.lang.Math).PI}") String artist) {
this.compactDisc=compactDisc;
this.artist=artist;
}
这个好像没有什么特别的,就只有上面的那个可以判断 ?:是不是空 如果是空的话就会返回'Rattle and Hum'
5,正则表达式
太复杂了,不赘述
6, 计算集合
@Value("#{jukeBox.songs[2].title}")
String value;
@Value("#{jukeBox.songs.$[artist == 'qu']}")
Song list;
@Value("#{jukeBox.songs.^[artist == 'qu']}")
Song list;
@Value("#{jukeBox.songs.![artist]}")
List<String> names;
@Value("#{jukeBox.songs.?[artist == 'qu']}")
List<Song> names;
SPEL可以通过【】来获取集合中的元素,第二个是可以获取艺术家是qu的最后一个元素,第三个是第一个元素,第四是投影,可以用song.artist新建一个list,第五个是过滤一个艺术家是qu的list。
Spring 表达式很强大,但是也不要整的太复杂,毕竟不要debug。。。。