(来换项目系列)Spring学习笔记(2)

3.1.1 配置profile bean

使用@profile 注解

例如 @Configuration

@Profile("dev")

public class xxxConfig{

@Bean

public XXX xx(){

 

return xxx;

}

}

@Profile注解应用在了类级别上。 它会告诉

Spring这个配置类中的bean只有在dev profile激活时才会创建。 如果

dev profile没有激活的话, 那么带有@Bean注解的方法都会被忽略

掉。

 

Spring3.2开始,profile注解可以使用在方法级别上。

未声明在一个给定profile范围内的bean始终都会被创建,与激活哪个profile没有关系

 

XML中配置profile

通过<beans>元素的 profile属性来在XML中配置profile bean

可以通过在根元素<beans>里嵌套<beans>元素来将所有的profile bean定义放到一个XML文件中。

 

 

 

3.1.2 激活profile

spring.profiles.active的值来设置哪个profile被激活,

如果没有设置的话,spring将查找spring.profiles.default的值。如果都未设置,那就没有

激活的profile,因此只会创建那些没有定义在profilebean

有多种方式来设置这两个属性:

作为DispatcherServlet的初始化参数;

作为Web应用的上下文参数;

作为JNDI条目;

作为环境变量;

作为JVM的系统属性;

在集成测试类上, 使用@ActiveProfiles注解设置。

<context-param>

<param-name>spring.profiles.default </param-name>

<param-value>dev</param-value>

</context-param>

 

 

<servlet>

<servlet-name>```

````

<init-param>

<param-name>spring.profiles.default </param-name>

<param-value>dev</param-value>

</init-param>

 

在集成测试类上, 使用@ActiveProfiles注解设置。

@ActiveProfiles("dev")

 

3.2条件化的bean

@Conditional(XXClassExistCondition.class)

XXClassExistCondition是任意实现了Condition接口的类

public interface Condition(){

boolean matches(COnditionContext ctxt,AnnotatedTypeMetadata metadata);

}

 

如果matches返回true,将会创建带有@Conditional注解的bean,如果返回为false,将

不会创建这些bean

 

 

3.3 处理自动装配的歧义行

 

@Autowired

public void serDessert(Dessert dessert){

this.dessert=dessert;

}

 

Dessert是一个接口,有cakecookies,Icecream 3个类实现了这个接口

@Conponent

public class Cake implements Dessert(){}

@Conponent

····

Spring自动为setDessert装配Dessert参数时,没有唯一、无歧义的可选值,

所以会抛出NoUniqueBeanDefinitionExcepetion

此时可以将bean中的某一个设为首选(primary)bean,或者使用限定符(qualifier)来帮助

spring缩小范围。

 

3.3.1 标识首选的bean

@Conponent

@Primary

 

Java配置

@Bean

@Primary

 

XML配置

<bean id="xxx"

class="xx"

primary="true"

/bean>

 

 

如果你标识了多个首选bean,那么就无法正常工作。

 

3.3.2限定自动装配的bean

@Autowired

@Qualifier("icecream") //bean 默认ID为首字母变小写的类名 Icecream->icecream

public void serDessert(Dessert dessert){

this.dessert=dessert;

}

 

创建自定义的限定符

@Conponent

@Qualifier("自定义限定符")

 

 

当通过Java配置显式定义bean的时候, @Qualifier

也可以与@Bean注解一起使用

@Bean

@Qualifier("自定义限定符")

 

Java不允许在同一个条目上重复出现相同类型

的多个注解

 

 

 

 

所以我们可以创建自定义的限定符注解,这里我们创建一个注解,它本身用Qualifier注解来标注,这  样我们就不再使用@Qualifier("自定义限定符"),而是直接使用自定义的注解@自定义限定符

@Qualifier

public @interface 自定义限定符

 

@Conponent

@自定义限定符

 

最终, 在注入点, 我们使用必要的限定符注解进行任意组合, 从而将

可选范围缩小到只有一个bean满足需求。

 

 

默认情况下, Spring应用上下文中所有bean都是作为以单例(singleton) 的形式创建的,也就是说一个bean被注入到其他bean多少次都是同一个实例。

 

Spring定义了多种作用域, 可以基于这些作用域创建bean, 包括:

单例(Singleton) : 在整个应用中, 只创建bean的一个实例。

原型(Prototype) : 每次注入或者通过Spring应用上下文获取的

时候, 都会创建一个新的bean实例。

会话(Session) : 在Web应用中, 为每个会话创建一个bean

例。

请求(Rquest) : 在Web应用中, 为每个请求创建一个bean

例。

 

单例是默认的作用域, 但是正如之前所述, 对于易变的类型, 这并不

合适。 如果选择其他的作用域, 要使用

@Scope注解

@Scope ConfigurableBeanFactory.SCOPE_PROTOTYPE或者@Scope ("prototype")。                                            

                           

Java配置中与@Bean同时使用时同理。

 

XML配置

<bean id="xx"

class="xxx"

scope="prototype"/>

3.4.1 使用会话和请求作用域

购物车bean来说, 会话作用域是最为合适的

@Conponent

@Scope( value=WebApplicationContext.SCOPE_SESSION,

proxyMode=ScopedProxyMode.INTERFACES)

public ShopipingcCart cart(){

}

SpringWeb应用中的每个会话创建一个ShoppingCart

这会创建多个ShoppingCart bean的实例, 但是对于给定的会话只会创建一个

实例, 在当前会话相关的操作中, 这个bean实际上相当于单例的。

 

@Scope同时还有一个proxyMode属性, 它被设置成了ScopedProxyMode.INTERFACES。 这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。

 

假设我们要将ShoppingCart bean注入到单例StoreService bean

Setter方法中

@Conponent

public class ShoppingService{

public void setshoppingcart(ShoppingCart shoppingcart){

this.shoppingcart=shopingcart;

}

···

}

 

StoreService是一个单例bean,会在Spring应用上下文加载

的时候创建。 当它创建的时候, Spring会试图将ShoppingCart bean

注入到setShoppingCart()方法中。 但是ShoppingCart bean

会话作用域的, 此时并不存在。 直到某个用户进入系统, 创建了会话

之后, 才会出现ShoppingCart实例

 

系统中将会有多个ShoppingCart实例: 每个用户一个。 我

们并不想让Spring注入某个固定的ShoppingCart实例

StoreService中。 我们希望的是当StoreService处理购物车

功能时, 它所使用的ShoppingCart实例恰好是当前会话所对应的

那一个。

 

Spring并不会将实际的ShoppingCart bean注入到StoreService中,

Spring会注入一个到ShoppingCart bean的代理, 如图3.1所示。 这

个代理会暴露与ShoppingCart相同的方法, 所以StoreService

会认为它就是一个购物车。 但是, StoreService

ShoppingCart的方法时, 代理会对其进行懒解析并将调用委托

给会话作用域内真正的ShoppingCart bean

 

proxyMode属性被设置成了

ScopedProxyMode.INTERFACES, 这表明这个代理要实现

ShoppingCart接口, 并将调用委托给实现bean

 

如果ShoppingCart是接口而不是类的话, 这是可以的(也是最为

理想的代理模式) 但如果ShoppingCart是一个具体的类的话,

Spring就没有办法创建基于接口的代理了。 此时, 它必须使用CGLib

来生成基于类的代理。 所以, 如果bean类型是具体类的话, 我们必须

要将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS, 以此来表明要以生成目标类扩展的方式创建代理。

 

3.4.2 在XML中声明作用域代理

<bean id="cart"

class="com.myapp.ShoppingCart"

scope="session"

<aop:scoped-proxy />

</bean>

默认情况下, 它会使用CGLib创建目标类的代理。

但是我们也可

以将proxy-target-class属性设置为false, 进而要求它生成基

于接口的代理:<aop:scoped-proxy proxy-target-class="false" />

 

为了使用<aop:scoped-proxy>元素, 我们必须在XML配置中声明

Springaop命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"

 

3.5.1 注入外部的值

Spring中, 处理外部值的最简单方式就是声明属性源并通过Spring

Environment来检索属性

 

 

 

 

 

 

 

 

int connetctioncount = env.getProperty("db.Connnetction.count",Integer.class,30)

 

如果你在使

getProperty()方法的时候没有指定默认值, 并且这个属性没有定义的话, 获取到的值是null。 如果你希望这个属性必须要定义, 那么可以使用getRequiredProperty()方法

 

在此时,如果disc.title 或者disc.artist属性没有定义的话,将会抛出异常。

 

如果想检查一下某个属性是否存在的话,那么可以调用EnvironmentcontainsProperty()方法,boolean titleExists = env.containsProperty("dist.title")

 

最后, 如果想将属性解析为类的话, 可以使

getPropertyAsClass()方法:

 

 

占位符的使用形式为${···}  

 

 

 

依赖于组件扫描和自动装配来创建和初始化应用组件的话,

那么就没有指定占位符的配置文件或类了 在这种情况下, 我们可以

使用@Value注解, 它的使用方式与@Autowired注解非常相似。 比

如, BlankDisc类中, 构造器可以改成如下所示:

 

 

为了使用占位符, 我们必须要配置一

PropertyPlaceholderConfigurer bean

PropertySourcesPlaceholderConfigurer bean。 从Spring

3.1开始, 推荐使

PropertySourcesPlaceholderConfigurer, 因为它能够基

Spring Environment及其属性源来解析占位符。

如下的@Bean方法在Java中配置了

PropertySourcesPlaceholderConfigurer

 

如果你想使用XML配置的话, Spring context命名空间中的

<context:propertyplaceholder>元素将会为你生

PropertySourcesPlaceholderConfigurer bean

 

 

 

SpEL表达式 格式:#{···}

例:#{T(System).currentTimeMillis()}

T()表达式将java.lang.System 视为java中对应的类型,因此可以条用其static修饰的currentTimeMillis()方法

 

SpEL也可以引用其他bean或其他bean的属性

如下的表达式会计算得到IDsgtPeppersbeanartist属性

#{sgtPeppers.artist}

 

还可以通过systemProperties对象引用系统属性

#{systemProperties['disc.title']}

 

让我们看一下在bean装配的时候如何使用这些表达式,与占位符非常相似

 

 

XML

 

 

SpEL支持的基础表达式

1)表示字面值

#{3.1415}

 

#{"hello"}

字面值truefalse的计算结果就是它们对应的Boolean类型的值。   

#{false}

 

引用bean、 属性和方法

你可以使用SpEL将一个bean装配到另外一个bean的属性中, 此时要使用bean ID作为SpEL表达式

#{sgtPeppers}

 

假设我们想在一个表达式中引用sgtPeppersartist

性: #{sgtPeppers.artist}

 

我们还可以调用bean上的方法

#{artistSelector.selectArtist().toUpperCase()}

 

如果selectArtist()的返回值不是null的话, 这没有什么问题。

为了避免出现NullPointerException, 我们可以使用类型安全的

运算符:

 

与之前只是使用点号(.) 来访问toUpperCase()方法不同, 现在我们使用了“?.”运算符。 这个运算符能够在访问它右边的内容之前, 确保它所对应的元素不是null。 所以, 如果selectArtist()的返回值是null的话, 那么SpEL将不会调用toUpperCase()方法。 表达式的返回值会是null

 

 

在表达式中使用类型

如果要在SpEL中访问类作用域的方法和常量的话, 要依赖T()这个关键的运算符。 例如, 为了SpE中表达JavaMath类, 需要按照如下的方式使用T()运算符:

T(java.lang.Math)

这里所示的T()运算符的结果会是一个Class对象, 代表了java.lang.Math

T()运算符的真正价值在于它能够访问目标类型的静态方法和常量

 

 

 

 

SpEL还提供了三元运算符(ternary

 

三元运算符的一个常见场景就是检查null

 

计算正则表达式

 

计算集合

 

 

 

SpEL还提供了查询运算符(.?[]

 

SpEL还提供了另外两个查询运算符: “.^[]”“.$[]”, 它们分别用来在集合中查询第一个匹配项和最一个匹配项。 例如, 考虑下面的表达式, 它会查找列表中第一个artist属性为Aerosmith的歌曲

 

 

SpEL还提供了投影运算符(.![]) , 它会从集合的每个成员中选择特定的属性放到另外一个集合中。 作为样例, 假设我们不想要歌曲对象的集合, 而是所有歌曲名称的集合。 如下的表达式会将title属性投影到一个新的String类型的集合中:

 

投影操作可以与其他任意的SpEL运算符一起使用。 比如,我们可以使用如下的表达式获得Aerosmith所有歌曲的名称列表:

 

posted @ 2017-12-26 10:47  zqm233  阅读(163)  评论(0编辑  收藏  举报