您的位置:首页 > 编程语言 > Java开发

spring从头开始(三)----bean织入进阶

2016-05-14 22:00 549 查看

部署环境

在软件开发中,部署环境的改变对软件本身的影响是一件很棘手的事情。如开发环境和生成环境等等。

spring的策略是在运行时做出决定,而不是编译时。

使用@Profile注解去指明该bean属于哪一个环境。

@Profile("dev")


在spring3.1版本中,@Profile还只能用在class级别上,从3.2版本开始@Profile可以用在方法级别上了,控制粒度更细了。

在xml配置文件中使用profile属性配置,

<beans profile="dev">


是beans级别的属性

问题:如何使某个profile处于active状态

spring提供了两个参数控制Profile的状态:

spring.profiles.active 和 spring.profiles.default

spring.profiles.active 这个值设置了谁,谁就是active的。

如果spring.profiles.active没有设置,那么spring.profiles.default设置了谁,谁就是active的。

如果两个都没有设置,那么全都不是active的。所有被@Profile注解的都会无效。

问题:这两个参数的设置方法

DispatcherServlet的初始化参数

web application的context 参数

jndi

环境变量

jvm环境变量

在单元测试方法中使用@ActiveProfiles注解

如,在web.xml中配置:

<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>


或者servlet中配置初始化参数

<servlet>
<servlet-name>appServlet</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>


如单元测试中:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={PersistenceTestConfig.class})
@ActiveProfiles("dev")
public class PersistenceTest {
... }


Spring4提供的更强大的@Conditional注解

应用在bean级别的注解,粒度更加细化。只有@Conditional注解的条件判断为true时,bean才会被创建。

如:

@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}


如何使用呢?@Conditional和Condition接口是成对出现的。

public interface Condition {
boolean matches(ConditionContext ctxt,AnnotatedTypeMetadata metadata);
}


我们要做的就是实现matches方法,它判断为true就创建,判断为false就不创建bean。

public class MagicExistsCondition implements Condition {
public boolean matches(
ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("magic");
}
}


注意到matches方法有两个参数,其中ConditionContext是一个接口

public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
ClassLoader getClassLoader();
}


从方法名来看,我们应该可以得到这些信息:

- 得到bean注册信息,getRegistry

- 得到bean工厂

- 得到环境变量

- 得到资源加载器

- 得到类加载器

AnnotatedTypeMetadata也是一个接口,是获取注解信息的。

public interface AnnotatedTypeMetadata {
boolean isAnnotated(String annotationType);
Map<String, Object> getAnnotationAttributes(String annotationType);
Map<String, Object> getAnnotationAttributes(
String annotationType, boolean classValuesAsString);
MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType);
MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString);
}


解决自动注入的ambiguity问题

前面讲到,在自动注入的时候,当发现有多于1个的合适的bean可以注入时,spring是无法做决定的,报ambiguity错误。

1. 使用@Primary注解

在类上使用@Component注解为bean时,同时使用@Primary注解,表明这是最优秀的bean。

在bean上使用@Bean注解时,加上@Primary。多个时选择最优先的bean注入。

xml中配置:bean标签中设置属性primary=”true”

2. 使用限定注解@Qualifier

可以和@Autowired连用

如:

@Autowired
@Qualifier("iceCream")


意思是注入beanId是iceCream的bean,准确的说是beanId中有这个字符串的bean。

sprin
4000
g在创建bean是,默认使用它的小写类名作为beanId。

这样会导致的一个问题是,当改变类名时,beanId会改变,注入的地方的@Qualifier如果忘记改了,就会出错。

有个办法是,在声明bean时,也加上@Qualifier注解,此时的作用是定义自己的限定条件。如:

@Component
@Qualifier("cold")


这样定义了自己的限定,使用时就可以

@Autowired
@Qualifier("cold")


@Qualifier和@Bean这种确定性注解放在一起是不起作用的。

@Qualifier可以同时使用多个。

但是,注意,java是不允许同时使用多个相同的注解的。java8允许,但该注解必须被@Repeatable注解,but我们的@Qualifier并没有。

所有有个变通的方式是,自定义注解。使用@Qualifier注解。如:

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


可以再定义一个

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


这样就可以同时用多个了。

Scope

Singleton 。整个application范围单例。默认。

Prototype。每次注入都创建新的。

Session。在web应用中,每个session创建一个新的。

Request。在web应用中,每个request创建一个新的。

配置scope:

在javaConfig中使用
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
注解

在xml中为
<bean>
使用
scope="prototype"


session的scope:

@Scope(
value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES)


注意proxyMode是用来解决将session或者request级别的bean注入singleton的bean时会遇到的问题。因为一个singleton的bean是在spring的application context 加载的时候创建的,而session或者request级别的bean是在session或者request创建时才创建出来的,所以此时还不存在。spring的解决办法是为这个session或者request级别的bean创建一个代理,将其注入singleton的bean。

注意上面的例子中我们使用的是接口ScopedProxyMode.INTERFACES。

也就是说此时注解是使用在一个接口上面。

如果是class类,要用ScopedProxyMode.TARGET_CLASS。spring会使用CGLib去创建基于类的代理。



在xml中配置代理:

使用

<aop:scoped-proxy />


默认情况下是使用CGLIB生成基于类的代理。如果要使用接口,用

<aop:scoped-proxy proxy-target-class="false" />


spring的EL表达式

spring的el表达式主要是为了运行时动态的注入数据,而不是写死。

在看spel之前,还有另一种方法也可以达到这个目的:属性占位符(property placeholders)。

例子:

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("classpath:/com/soundsystem/app.properties")
public class ExpressiveConfig {
@Autowired
Environment env;
@Bean
Declare a property source

public BlankDisc disc() {
return new BlankDisc(
env.getProperty("disc.title"),
env.getProperty("disc.artist"));
} }


Environment还有很多有用的方法,具体看api。

在xml中使用,${xxx}占位

spel提供的增强能力:

通过id访问bean

在对象上执行方法或者获取属性

数学,关系,逻辑运算

正则表达

集合操作

使用 #{ … },里面的就是spel表达式

#{T(System).currentTimeMillis()}
得到系统时间。T()操作符,返回的是class。所以只能去访问静态方法和常量。

#{sgtPeppers.artist}
得到id是sgtPeppers的artist属性值

#{systemProperties['disc.title']}
得到系统参数值

#{artistSelector.selectArtist()?.toUpperCase()}
这个例子selectArtist返回string,然后对其Upper,?是为了解决空指针问题。

操作符:



选择操作符

#{jukebox.songs.?[artist eq 'Aerosmith']}
jukebox.songs返回list,.?[]操作符根据后面的表达式进行选取,true的都会返回。

.^[]返回第一个匹配的。

.$[] 返回最后一个匹配的。

投射操作符.![]

#{jukebox.songs.![title]}
jukebox.songs.返回的是一个Song类型的list,对其使用.![]作用是取每一个的title属性值,组成新的String的list返回。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring