【0】README
0)本文部分文字描述转自:“Spring In Action(中/英文版)”,旨在review  spring(3)高级装配 的相关知识;

【1】环境与profile(考虑数据库配置)
1)使用嵌入式数据库
@Bean(destroyMethod="shutdown")
public DataSource dataSource() {return new EmbeddedDatabaseBuilder().addScript("classpath:schema.sql").addScript("classpath:test-data.sql").build();
}

2)使用JNDI从容器中获取DataSource

@Bean
public DataSource dataSource() {JndiObjectFactoryBean jndiObjectFactoryBean =new JndiObjectFactoryBean();jndiObjectFactoryBean.setJndiName("jdbc/myDS");jndiObjectFactoryBean.setResourceRef(true);jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);return (DataSource) jndiObjectFactoryBean.getObject();
}

3)选择不同的DataSource配置

@Bean(destroyMethod="close")
public DataSource dataSource() {BasicDataSource dataSource = new BasicDataSource();dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");dataSource.setDriverClassName("org.h2.Driver");dataSource.setUsername("sa");dataSource.setPassword("password");dataSource.setInitialSize(20);dataSource.setMaxActive(30);return dataSource;
}

Attention)我们必须要有一种方法来配置DataSource,使其在每种环境下都会选择最为合适的配置;其中一种方式是在单独的配置类(或XML 文件)中配置每个bean,然后在构建阶段(可能会使用Maven的profiles)确定要将哪一个配置编译到可部署的应用中;


problem+solution)
problem)从开发阶段迁移到QA 阶段,重新构建可能不会出大问题,但从QA阶段迁移到 生产阶段,重新构建可能会引入bug;
solution)Spring所提供 的 解决方案并不需要重新构建;

【1.1】配置 profile bean
1)intro:spring 在重新构建的过程中需要根据环境决定该创建那个 bean 和 不创建那个bean;不过spring 并不是在构建时做出决策,而是等待运行时再来确定;这样的结果就是同一个部署单元能够适用于所有的环境,没有必要进行重新构建;
2)spring 引入了bean profile:要使用profile,首先要将所有不同的 bean 定义整理到一个或多个profile中,在将应用部署到每个环境时,要确保对应的 profile 处于激活状态 ;
3)在javaConfig中,可以使用 @Profile 注解指定某个bean属于哪一个profile;

看个荔枝)注解@Profile(干货——注解@Profile的作用)
荔枝1)嵌入式数据库可能会配置为如下形式:
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {
@Bean(destroyMethod="shutdown")
public DataSource dataSource() {return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:schema.sql").addScript("classpath:test-data.sql").build();}
}

荔枝2)使用JNDI从容器中获取DataSource 的 Profile配置

@Configuration
@Profile("prod")
public class ProductionProfileConfig {@Beanpublic DataSource dataSource() {JndiObjectFactoryBean jndiObjectFactoryBean =new JndiObjectFactoryBean();jndiObjectFactoryBean.setJndiName("jdbc/myDS");jndiObjectFactoryBean.setResourceRef(true);jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);return (DataSource) jndiObjectFactoryBean.getObject();}
}

荔枝3)从spring3.2开始,@Profile注解既可以在方法级别上使用,也可以在类级别上使用;(还可以和 @Bean 注解一起使用)
Attention)
A1)没有定义在profile中的bean 都会被创建,无论profile 激活与否;
A2)而定义在 profile中的bean,当且仅当对应的 profile 被激活时才可以创建;

4)在XML中配置 profile
4.1)通过<beans>元素的profile属性,在XML 中配置profile bean;
4.2)还可以在根<beans>元素中嵌套定义 <beans> 元素,而不是为每个环境都创建一个 profile XML 文件;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans><beans profile="qa">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:url="jdbc:h2:tcp://dbserver/~/test"p:driverClassName="org.h2.Driver" p:username="sa" p:password="password"p:initialSize="20"p:maxActive="30" />
</beans>
<beans profile="prod">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase"
resource-ref="true" proxy-interface="javax.sql.DataSource" />
</beans>
</beans>
【1.2】激活profile
1)spring在确定哪个 profile处于激活状态时,需要依赖两个独立的属性: spring.profiles.active and spring.profiles.default ; 如果设置了 active属性的话,相应的Profile被激活,否则,查看default属性的值,如果default的值 没有设置的话,那就会忽略掉 profile中的bean的创建;
2)多种方法设置上述两个属性;
method1)作为 DispatcherServlet 的初始化参数;
method2)作为web 应用的上下文参数;
method3)作为 JNDI条目;
method4)作为环境变量;
method5)作为JVM的 系统属性;
method6)在集成测试类上, 使用 @ActiveProfiles 注解设置;(干货——注解@ActiveProfile的作用)
原书作者的设置方法:喜欢用DispatcherServlet的参数将spring.profiles.default属性设置为开发环境的profile,会在Servlet上下文中进行设置;
看个荔枝)在web.xml 中设置 spring.profiles.default 属性
Attention) spring.profiles.active and spring.profiles.default 中的profile都是 复数形式: 这意味着可以同时激活多个 profile,列出多个profile 名称,以逗号分隔来实现;

3)使用profile进行测试
3.1)intro:spring提供了 @ActiveProfiles注解,来指定测试时要激活哪个profile;
3.2)下面的荔枝展示了 使用 @ActiveProfiles注解 激活 dev profile;


【2】条件化bean
1)intro:当满足给定条件时,才装配相应的bean;组合@Conditional注解 和 @Bean注解;(干货——注解@Conditional的作用)
对以上代码的分析(Analysis):
A1)matches() 方法:会得到 ConditionContext 和 AnnotatedTypeMetadata 对象用来做出决策;
A2)ConditionContext是一个接口:
public interface ConditionContext {  BeanDefinitionRegistry getRegistry();  ConfigurableListableBeanFactory getBeanFactory();  Environment getEnvironment();  ResourceLoader getResourceLoader();ClassLoader getClassLoader();
}

A3)通过ConditionContext,可以做到如下几点(works):

work1)借助getRegistry方法返回的BeanDefinitionRegistry检查bean的定义;
work2)借助getBeanFactory方法返回的ConfigurableListableBeanFactory 检查bean是否存在,查看bean的属性
work3)借助getEnvironment方法返回的Environment检查环境变量是否存在以及它的值是什么
work4)读取并检查getResourceLoader方法返回的ResourceLoader所加载的资源
work5)借助getRegistry方法返回的BeanDefinitionRegistry检查bean的定义;
work6)借助getClassLoader方法返回的ClassLoader加载并检查类是否存在
A4)AnnotatedTypeMetadata能够让我们检查带有 @Bean 注解的方法上还有什么其他的注解;
public interface AnnotatedTypeMetadata {   boolean isAnnotated(String annotationName);  // 判断带有@Bean注解的方法是不是还有其他的注解.Map<String, Object> getAnnotationAttributes(String annotationName);   Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);   MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);  MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}

2)从spring4 开始,@Profile注解进行了重构,使其基于@Conditional 和 Condition实现:@Profile注解如下所示:
对以上代码的分析(Analysis):
A1)ProfileCondition 通过 AnnotatedTypeMetadata得到了用于 @Profile 注解的所有属性;
A2)根据 通过ConditionContext得到的 Environment 来检查(acceptProfiles()方法)该profile 是否处于激活状态;

【3】处理自动装配的歧义性
1)problem+solution
1.1)problem测试用例:(error info: No qualifying bean of type [com.spring.chapter3.Disc] is defined: expected single matching bean but found 2: jayChou,leehom)
1.2)solution:spring 提供了多种可选方案来解决这样的问题。你可以将可选的bean 中的某个设为首选(primary)的bean,或者使用限定符来帮助 spring 将可选的bean的范围缩小到只有一个bean;
1.3)如果在XML 配置bean的话,设置primary属性为 true来指定;
Attention)如果标识了多个首选(Primary)bean的话,@Primary注解就无法正常工作了;

【3.2】限定自动装配的bean
1)problem+solution
1.1)problem:设置首选bean的 局限性: 在于@Primary 无法将可选方案的范围限定到唯一一个无歧义性的可选项中。当首选bean的数量超过一个四,我们并没有其他的方法来进一步缩小可选范围;
1.2)solution:spring的限定符能够在所有可选的bean上进行缩小范围的操作,最终能够达到一个bena 满足所规定的限制条件;(使用@Qualifier注解)
看个荔枝)确保将jaychou 注入到CDPlayer中
2)创建自定义限定符:我们可以为bean设置自己的限定符,而不是依赖于将bean ID 作为限定符;
@Component
@Qualifier("cold")
public class IceCream implements Dessert { ... }

3)@Qualifier注解也可以和 @Bean注解一起使用

@Bean
@Qualifier("cold")
public Dessert iceCream() {return new IceCream();
}
4)使用自定义的限定符注解(干货——开发人员自己创建限定符注解)
4.1)problem+solution
@Component
@Qualifier("fashion")
public class JayChou implements Disc { ... }
@Component
@Qualifier("fashion")
public class Leehom implements Disc { ... }
4.1.1)problem:现在我们有两个带有“fashion”限定符的唱片,在自动装配Disc bean的时候,我们再次遇到了歧义性问题;
4.1.2)solution:需要更多的限定符来将可选范围限定到只有一个bean;(多个 @Qualifier 注解)
@Component
@Qualifier("fashion")
@Qualifier("cool")
public class JayChou implements Disc { ... }
@Component
@Qualifier("fashion")
@Qualifier("handsome")
public class Leehom implements Disc { ... }

5)problem+solution:

5.1)problem:java不允许在同一个条目上重复使用出现相同类型的多个注解;因为这样的话,编译器会报错;
5.2)solution:自定义限定符注解;(不能再干货——创建自定义的限定符注解)
6)how to build diy @Qualifier annotaion.
step1)不再使用 @Qualifier("fashion") 注解,使用自定义的 @Fashion这,该注解 的定义如下:
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Fashion{ }

Attention)
A1)自定义注解本身实际上就成为了 限定符注解;
A2)通过声明自定义的限定符注解,我们可以同时使用多个限定符,不会再有java 编译器的限制或错误了;
7)所以,我们可以添加
@Component
@Qualifier("fashion")
@Qualifier("cool")
public class JayChou implements Disc { ... }
// 改为
@Component
@Fashion // a qualifier annotaion.
@Cool// a qualifier annotaion.
public class JayChou implements Disc { ... }
(干货——说白了,自定义限定符注解 就是多个标识而已,用于区别不同bean 和 对bean 进行分组(因为他们都属于fashion 组))

【4】 bean的作用域
1)intro: default case下,spring应用上下文中所有 bean都是以单例的形式创建的;也就是说,不管给定一个bean被注入到其他bean中多少次,每次所注入的都是 同一个实例;
2)problem+solution:显然,让多个对象引用同一个bean 是不合理的,因为对象中的内容是易变的,一个对象对bean做了修改,这会波及到其他bean的;
3)spring定义了多种作用域,可以基于这些作用域创建bean,包括(scope):
scope1)单例(Singleton):在整个应用中,只创建bean的一个实例;(干货——默认情况下,bean以单例模式创建)
scope2)原型(Prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的 bean 实例;
scope3)会话(Session):在web 应用中,为每个会话创建一个 bean实例;
scope4)请求(Request):在web 应用中,为每个请求创建一个 bean实例;
Attention)
A1)default case下: 是单例作用域;
A2)如果选择其他作用域,要使用 @Scope注解,它可以和 @Component 或 @Bean 联用;(干货——@Scope注解的作用
A3)不管用哪一种方式来声明原型作用域,每次注入或从 spring 应用上下文中检索该bean的时候,都会创建新的实例;
看个荔枝)将其声明为原型bean:
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad { ... }用XML 来配置的话,代码形式如下:
<bean id="notepad"class="com.myapp.Notepad"scope="prototype" />



【4.1】使用会话和请求作用域
1)指定会话作用域: 使用 @Scope 注解,它的使用方式与 指定原型作用域是相同的;
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart() { ... }
对以上代码的分析(Analysis):
A1)这会创建多个 ShoppingCartbean的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean 实际上相当于单例的;
A2)@Scope注解:同时还有一个 proxyMode 属性,它被设置成了 proxyMode=ScopedProxyMode.INTERFACES;这个属性解决了将会话或请求作用域的bean 注入到单例bean中所遇到的问题;
2)proxyMode 代理模式所要解决的问题:
2.1)假设我们要将 ShoppingCart bean在注入到 单例 StoreService bean 的Setter 方法中,如下所示:
@Component
public class StoreService {@Autowiredpublic void setShoppingCart(ShoppingCart shoppingCart) {this.shoppingCart = shoppingCart;}...
}
对以上代码的分析(Analysis):
A1)因为StoreService 是一个单例bean,会在spring 应用上下文加载的时候创建;
A2)当它创建的时候,spring会试图 将 ShoppingCart bean 注入到 setShoppingCart() 方法中;但是ShoppingCart bean 是会话作用域的,此时并不存在。直到某个client 进入系统,创建了会话后,才会出现ShoppingCart 实例;(干货——我此时才体会到了为什么需要懒加载)
A3)而且,系统中有多个 ShoppingCart 实例(多个购物车):每个用户一个。我们并不想让spring注入 到某个固定的 ShoppingCart 实例到 StoreService中。我们希望的是当StoreService 处理ShoppingCart 功能时,它所使用的 ShoppingCart 实例恰好是当前会话所对应的那一个;
A4)spring 并不会将实际的ShoppingCart bean 注入到 StoreService 中。spring 会注入一个到 ShoppingCart bean 的代理,如下图所示。这个代理会暴露与 ShoppingCart 相同的方法,所以 StoreService 会认为他是一个ShoppingCart。但是,当StoreService 调用 ShoppingCart 的方法时,代理会对其进行懒解析并将调用委托给会话作用域内真正的 ShoppingCart bean;
(干货——懒加载的调用过程,引入了代理proxy)

3)如果 ShoppingCart 是接口不是类的话,这是可以的;但如果ShoppingCart 是具体的类,那么spring无法创建基于接口的代理了。这时,spring必须使用CGLIB 来生成基于类的代理;
3.1)所以,如果bean类型是具体的类的话,我们必须要将 proxyMode 属性设置为 ScopedProxy- Mode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理;
【4.2】在XML 中声明作用域代理
1)要设置代理模式,需要使用 spring aop 命名空间的一个新元素(<aop:scoped-proxy /> ):
<bean id="cart"class="com.myapp.ShoppingCart"scope="session"><aop:scoped-proxy />
</bean>

2)为了使用 <aop:scoped-proxy /> 新元素,我们必须在 XML 配置中声明spring 的aop 命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">...
</beans>
【5】运行时值注入
1)problem+solution
1.1)problem:在实现的时候将值硬编码在配置类中。用XML 装配bean的话,同样值也会是硬编码的;
1.2)solution:为了避免硬编码,而是想让这些值在运行时再确定。spring 提供了两种在运行时求值的方式(ways):
way1)属性占位符(Property placeholder)
way2)spring 表达式语言(SpEL)

【5.1】注入外部的值
对以上代码的分析(Analysis):这个属性文件(app.properties)会加载到 spring 的 Environment中,稍后可以从这里通过 getProperty()方法 检索属性。

2)深入学习Spring的Environment
2.1)getProperty()方法有4个重载的变种形式(variant):
v1)String getProperty(String key)
v2)String getProperty(String key, String defaultValue)
v3)T getProperty(String key, Class<T> type)
v4)T getProperty(String key, Class<T> type, T defaultValue)
2.2)稍微修改下源码,当指定属性不存在时,使用一个default value:
2.3)如果我们从属性文件中得到的是一个String类型的值,那么在使用前还需要将其转换为 integer,但是使用 getProperty()方法的重载形式,就能便利地解决这个问题:(干货——getProperty()重载方法的作用)
int connectionCount =  env.getProperty("db.connection.count", Integer.class, 30);

3)Environment方法概览(methods)
method1)getRequiredProperty()方法:如果你希望这个属性必须要定义;没有定义会抛出异常;
@Bean
public Disc disc() {return new JayChou(env.getRequiredProperty("disc.title"),env.getRequiredProperty("disc.artist"));
}

method2)containsProperty()方法:检查该属性是否存在;
boolean titleExists = env.containsProperty("disc.title");

method3)getPropertyAsClass()方法:如果想要吧属性解析为类的话;
Class<CompactDisc> cdClass =  env.getPropertyAsClass("disc.class", CompactDisc.class);

method4)String[] getActiveProfiles():返回激活的profile名称数组;
method5)String[] getDefaultProfiles():返回默认的 profile 名称 的数组;
method6)boolean acceptsProfiles(String... profiles):如果 environment 支持给定的 profile的话,返回ture;

4)解析属性占位符
4.1)intro:spring 一直支持将属性定义到 外部的属性文件中,并使用占位符将其插入到 spring bean中;
4.2)在spring装配中,占位符的形式为 使用 "${...}" 包装的属性名称;
<bean id="sgtPeppers"class="soundsystem.BlankDisc"c:_title="${disc.title}"c:_artist="${disc.artist}" />

4.3)如果我们依赖于组件扫描和自动装配来创建和初始化应用组建的话,就没有XML配置文件或者类了。此时,可以使用 @Value 注解,如下所示:

public JayChou(@Value("${disc.title}") String title,@Value("${disc.artist}") String artist) {
this.title = title;
this.artist = artist;
}

4.4)为了使用占位符,必须要配置一个 PropertySourcesPlaceholderConfigurer(spring3.1推荐使用),因为它能够基于spring Environment 及其属性源来解析占位符;如下的@Bean方法配置了 PropertySourcesPlaceholderConfigurer

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

4.5)如果使用了XML配置 PropertySourcesPlaceholderConfigurer,推荐使用新元素 <context:property-placeholder>:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder />
</beans>

Attention)
A1)解析外部属性能够将值的处理推迟到 运行时,但是它的关注点在于根据名称解析来自于 spring Environment和属性源的属性;
A2)而 spring 表达式语言提供了一种更通用的方式在 运行时计算所要注入的值;
【5.2】 使用spring 表达式语言进行装配
1)intro to spring表达式语言==Spring Expression Language, SpEL;
2)SpEL 拥有很多特性(characters):
c1)使用bean的ID 来引用bean;
c2)调用方法和访问对象的属性;
c3)对值进行算术,关系和逻辑运算 ;
c4)正则表达式匹配;
c5)集合操作;
3)SpEL 荔枝
3.1)first task : 要知道将 SpEL 表达式放到 " #{...} "之中,这与属性占位符有些类似,属性占位符需要放到 " ${...} " 之中;
3.2)荔枝组团来袭
#{T(System).currentTimeMillis()} : 计算表达式的那一刻当前时间的毫秒数;T() 表达式会将java.lang.System 视为 java中对应的类型;因此可以调用其 static 修饰的currentTimeMillis方法;
#{jaychou.artist}:得到id 为 jaychou 的bean 的artist属性;
#{systemProperties['disc.title']}:引用系统属性;

4)装配bean的时候如何使用这些表达式
4.1)通过组件扫描 创建bean的话,在注入属性和构造器参数时,我们可以使用 @Value 注解,这与之前看到的属性占位符有点类似,但现在我们要使用 SpEL表达式;
看个荔枝):从系统属性中获取专辑名称和艺术家的名字;
public BlankDisc(@Value("#{systemProperties['disc.title']}") String title,@Value("#{systemProperties['disc.artist']}") String artist) {this.title = title;this.artist = artist;
}

4.2)在XML配置中,可以将SpEL 表达式传入 <property> or <constructor-arg> 的value属性中,或者将其作为 p-命名空间或c-命名空间条目的值;

看个荔枝)构造器参数通过SePL 表达式设置;
<bean id="sgtPeppers"class="soundsystem.BlankDisc"c:_title="#{systemProperties['disc.title']}"c:_artist="#{systemProperties['disc.artist']}" />

5)SePL 表达式 表示字面量
组团荔枝)
#{3.14159} == 表示浮点值;
#{9.87E4} ==98700
#{'Hello'} ==计算String类型的字面值;
#{false} ==boolean类型的值;
6)引用bean,属性和方法(通过ID 引用其他bean)
组团荔枝)使用bean ID(jaychou) 作为SpEL 表达式;
#{jaychou}
#{jaychou.artist} // 对属性(artist)的引用;
#{jaychou.selectArtist()} // 调用bean上的方法;
#{jaychou.selectArtist().toUpperCase()} // 对方法的连续调用;
#{artistSelector.selectArtist()?.toUpperCase()} // 使用类型安全的运算符,当返回不为null时,才调用后面的方法;
7)在表达式中使用类型
7.1)T()运算符:如果要在 SpEL 中访问类作用域的方法和常量的话,要依赖T() 这个关键的运算符;(干货——T() 是 SpEL中一个关键运算符
组团荔枝,我们继续)
T(java.lang.Math) // 为了在SpEL 中表达java的Math类,可以像左侧这样使用 T() 运算符;
T(java.lang.Math).PI // 将PI 值装配到bean的属性中;
T(java.lang.Math).random() // 计算得到一个0~1 间的随机数;
8)SpEL 运算符
8.1)SpEL 提供了多个运算符,如下表所示:
组团荔枝来袭)
#{2 * T(java.lang.Math).PI * circle.radius}//计算圆周长;
#{T(java.lang.Math).PI * circle.radius ^ 2}//计算面积;
#{disc.title + ' by ' + disc.artist} // 连接字符串;
#{counter.total == 100} or #{counter.total eq 100}// 数字比较;
#{scoreboard.score > 1000 ? "Winner!" : "Loser"} //三元运算符的应用;
#{disc.title ?: 'Rattle and Hum'}// Elvis运算符,表达式判断disc.title是否为null,若是null的话,计算结果是 后面的字符串;
9)计算正则表达式
9.1)intro:SpEL 通过matches 运算符支持表达式中的模式匹配, 且matches() 函数会返回一个boolean 类型的值;
#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'} // 判断一个字符串是否包含有效的邮件地址;
10)计算集合
组团荔枝来袭)
#{jaychou.songs[4].title}//计算songs集合中第5个(基于零开始)元素的title属性;(干货——SpEL 计算集合时的start index等于0);
#{jaychou.songs[T(java.lang.Math).random() * jaychou.songs.size()].title} // 随机取集合某个下标的song的title属性;
#{'This is a test'[3]} //返回第4个字母(s);
#{jaychou.songs.?[album eq '十二新作']}//SpEL 提供了查询运算符,用于对集合的过滤,得到集合的一个子集;(返回jaychou的十二新作专辑下的所有songs)
10.1)SpEL 提供了两个查询运算符: ".^[]", ".$[]";分别用于查询第一个匹配项和最后一个匹配项;
#{jaychou.songs.^[album eq '十二新作']}//查找列表中第一个属于十二新作专辑的歌曲;

10.2) SpEL提供了投影运算符:(.![]),它会从集合中的每个成员中选择特定的属性放到另一个集合中;
#{jaychou.songs.![title]}//将title属性投影到一个新的String类型的集合中;

10.3)投影操作可以和其它任意的 SpEL 运算符一起使用;
#{jaychou.songs.?[album eq '十二新作'].![title]} // 获得十二新作专辑下的所有歌曲名称;

// 难道没有发现,上述表达式等同于  select ... where...

spring(3)高级装配相关推荐

  1. Spring之高级装配(二)

    上一节提到Spring之装配bean(一),我们已经了解到了装配的基础知识,这部分是更为高级的bean装配技术. 高级装配内容: spring profile 有条件的bean 处理自动装配的歧义性 ...

  2. 《Spring实战》读书笔记-第3章 高级装配

    <Spring实战>是学习Spring框架的一本非常经典的书籍,之前阅读了这本书,只是在书本上写写画画,最近整理了一下<Spring实战>的读书笔记,通过博客的方式进行记录分享 ...

  3. 《Spring实战》读书笔记-第3章 高级装配,zookeeper原理图

    文章目录 3.1 环境与profile 3.2 条件化的bean 3.3 处理自动装配的歧义性 3.4 Bean的作用域 3.5 运行时植注入 3.6 小结 本章内容: Spring profile ...

  4. 《Spring实战》读书笔记-第3章 高级装配,字节跳动四面技术面

    当自动装配bean时,遇到多个实现类的情况下,就出现了歧义,例如: @Autowired public void setDessert(Dessert dessert) { this.dessert ...

  5. 《Spring实战》读书笔记-第3章 高级装配,最新Java大厂高频面试题

    <jee:jndi-lookup id="dataSource" lazy-init="true" jndi-name="jdbc/myData ...

  6. Spring Boot自动装配原理详解

    目录 1.环境和依赖 1.1.spring boot版本 1.2.依赖管理 2.自动装配 2.1.流程概述 2.2.三大步前的准备工作 2.2.1.注解入口 2.2.2.获取所有配置类 2.3.获取过 ...

  7. spring Bean自动装配

    spring Bean自动装配 自动装配是使用spring满足bean依赖的一种方式. spring会在应用上下文中为某个bean寻找其依赖的bean. spring自动装配需要从两个角度来实现,或者 ...

  8. (Spring)自动装配bean

    文章目录 自动装配bean 1. 环境搭建 2. byName自动装配 3. byType自动装配 4. 使用注解自动装配 4.1 @Autowired和@Qualifier 4.2 @Resourc ...

  9. Spring autowire 自动装配简介

    Spring autowire 自动装配简介 注意本文与一般spring 标注@Autowire 无关 如下例子定义了3个类 Dperson Dcar & Daddress 其中Dperson ...

最新文章

  1. 在ubuntu12.04 的QT安装
  2. highcharts使用教程
  3. 第三章:3.5 傅里叶变换
  4. SQL update select结合语句详解及应用
  5. 二、Go语言基础入门
  6. [转]Android中dp,px,sp概念梳理以及如何做到屏幕适配
  7. Ubuntu 更改ROOT密码的方法
  8. 机器人 工具坐标系的标定
  9. 在批处理文件中启动MediaPlayer播放制定文件
  10. Python成长之路_装饰器
  11. Qt之布局管理——分割窗口
  12. 谷歌,IE,火狐浏览器内核
  13. 开心电视助手v3.8最新绿色版,安卓设备远程管理工具神器
  14. 谈谈面向对象分析和设计
  15. 固体发动机内弹道matlab,固体火箭发动机内弹道性能的仿真研究
  16. matlab矩阵特征值分解,矩阵特征值分解与奇异值分解含义解析及应用
  17. linux音频设备接口,OSS--跨平台的音频接口简介
  18. P4147 玉蟾宫 题解
  19. android ndk开发
  20. 项目新增commitLint 和 husky 步骤

热门文章

  1. Codeforces Round #703 (Div. 2) B.Eastern Exhibition 中位数结论
  2. 牛客挑战赛47 D Lots of Edges(最短路+递归枚举子集)
  3. 牛客题霸 [没有重复项数字的所有排列] C++题解/答案
  4. [luogu-P4299] 首都(并查集 + LCT动态维护树的重心 / 维护虚儿子信息)
  5. [AtCoder Beginner Contest 215] A-G题解
  6. 周末狂欢赛1(玩游戏/Game,函数,JOIOI王国)
  7. 不止代码:迷宫问题(bfs)
  8. P1082-扩欧模板同余方程【扩欧,数论】
  9. 【模拟】【递归】电子表格(jzoj 2127)
  10. 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)