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返回。
相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- Spring和ThreadLocal
- Spring Boot 开发微服务
- Spring整合Quartz(JobDetailBean方式)
- Spring整合Quartz(JobDetailBean方式)
- 模拟Spring的简单实现
- Spring整合WebSocket应用示例(上)
- spring+html5实现安全传输随机数字密码键盘
- Spring中属性注入详解
- springmvc 发送ajax出现中文乱码的解决方法汇总
- SpringMVC框架下JQuery传递并解析Json格式的数据是如何实现的
- struts2 spring整合fieldError问题
- spring的jdbctemplate的crud的基类dao
- 读取spring配置文件的方法(spring读取资源文件)
- Spring Bean基本管理实例详解
- java实现简单美女拼图游戏
- 详解Java的Spring框架中的事务管理方式