标签:
@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)从开发阶段迁移到QA 阶段,重新构建可能不会出大问题,但从QA阶段迁移到 生产阶段,重新构建可能会引入bug;solution)Spring所提供 的 解决方案并不需要重新构建;
荔枝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 { @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)从spring3.2开始,@Profile注解既可以在方法级别上使用,也可以在类级别上使用;(还可以和 @Bean 注解一起使用)
A1)没有定义在profile中的bean 都会被创建,无论profile 激活与否;A2)而定义在 profile中的bean,当且仅当对应的 profile 被激活时才可以创建;
<?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>
method1)作为 DispatcherServlet 的初始化参数;method2)作为web 应用的上下文参数;method3)作为 JNDI条目;method4)作为环境变量;method5)作为JVM的 系统属性;method6)在集成测试类上, 使用 @ActiveProfiles 注解设置;(干货——注解@ActiveProfile的作用)
3.1)intro:spring提供了 @ActiveProfiles注解,来指定测试时要激活哪个profile;3.2)下面的荔枝展示了 使用 @ActiveProfiles注解 激活 dev profile;
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); }
A1)ProfileCondition 通过 AnnotatedTypeMetadata 得到了用于 @Profile 注解的所有属性;A2)根据 通过ConditionContext得到的 Environment 来检查(acceptProfiles()方法)该profile 是否处于激活状态;
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来指定;
1.1)problem:设置首选bean的 局限性: 在于@Primary 无法将可选方案的范围限定到唯一一个无歧义性的可选项中。当首选bean的数量超过一个四,我们并没有其他的方法来进一步缩小可选范围;1.2)solution:spring的限定符能够在所有可选的bean上进行缩小范围的操作,最终能够达到一个bena 满足所规定的限制条件;(使用@Qualifier注解)
@Component @Qualifier("cold") public class IceCream implements Dessert { ... }3)@Qualifier注解也可以和 @Bean注解一起使用
@Bean @Qualifier("cold") public Dessert iceCream() { return new IceCream(); }
@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 注解)
5)problem+solution:@Component @Qualifier("fashion") @Qualifier("cool") public class JayChou implements Disc { ... }@Component @Qualifier("fashion") @Qualifier("handsome") public class Leehom implements Disc { ... }
5.1)problem:java不允许在同一个条目上重复使用出现相同类型的多个注解;因为这样的话,编译器会报错;
5.2)solution:自定义限定符注解;(不能再干货——创建自定义的限定符注解)
step1)不再使用 @Qualifier("fashion") 注解,使用自定义的 @Fashion这,该注解 的定义如下:@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Fashion{ }Attention)
A1)自定义注解本身实际上就成为了 限定符注解;A2)通过声明自定义的限定符注解,我们可以同时使用多个限定符,不会再有java 编译器的限制或错误了;
@Component @Qualifier("fashion") @Qualifier("cool") public class JayChou implements Disc { ... } // 改为 @Component @Fashion // a qualifier annotaion. @Cool// a qualifier annotaion. public class JayChou implements Disc { ... }
scope1)单例(Singleton):在整个应用中,只创建bean的一个实例;(干货——默认情况下,bean以单例模式创建)scope2)原型(Prototype):每次注入或者通过spring应用上下文获取的时候,都会创建一个新的 bean 实例;scope3)会话(Session):在web 应用中,为每个会话创建一个 bean实例;scope4)请求(Request):在web 应用中,为每个请求创建一个 bean实例;
A1)default case下: 是单例作用域;A2)如果选择其他作用域,要使用 @Scope注解,它可以和 @Component 或 @Bean 联用;(干货——@Scope注解的作用)A3)不管用哪一种方式来声明原型作用域,每次注入或从 spring 应用上下文中检索该bean的时候,都会创建新的实例;
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Notepad { ... } 用XML 来配置的话,代码形式如下: <bean id="notepad" class="com.myapp.Notepad" scope="prototype" />
@Component @Scope( value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES) public ShoppingCart cart() { ... }
A1)这会创建多个 ShoppingCart bean的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean 实际上相当于单例的;A2)@Scope注解:同时还有一个 proxyMode 属性,它被设置成了 proxyMode=ScopedProxyMode.INTERFACES;这个属性解决了将会话或请求作用域的bean 注入到单例bean中所遇到的问题;
@Component public class StoreService { @Autowired public void setShoppingCart(ShoppingCart shoppingCart) { this.shoppingCart = shoppingCart; } ... }
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)
<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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> ... </beans>
1.1)problem:在实现的时候将值硬编码在配置类中。用XML 装配bean的话,同样值也会是硬编码的;
1.2)solution:为了避免硬编码,而是想让这些值在运行时再确定。spring 提供了两种在运行时求值的方式(ways):
way1)属性占位符(Property placeholder)way2)spring 表达式语言(SpEL)
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);
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.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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder /> </beans>
A1)解析外部属性能够将值的处理推迟到 运行时,但是它的关注点在于根据名称解析来自于 spring Environment和属性源的属性;A2)而 spring 表达式语言提供了一种更通用的方式在 运行时计算所要注入的值;
c1)使用bean的ID 来引用bean;c2)调用方法和访问对象的属性;c3)对值进行算术,关系和逻辑运算 ;c4)正则表达式匹配;c5)集合操作;
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.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']}" />
#{3.14159} == 表示浮点值; #{9.87E4} ==98700 #{'Hello'} ==计算String类型的字面值; #{false} ==boolean类型的值;
#{jaychou} #{jaychou.artist} // 对属性(artist)的引用; #{jaychou.selectArtist()} // 调用bean上的方法; #{jaychou.selectArtist().toUpperCase()} // 对方法的连续调用; #{artistSelector.selectArtist()?.toUpperCase()} // 使用类型安全的运算符,当返回不为null时,才调用后面的方法;
T(java.lang.Math) // 为了在SpEL 中表达java的Math类,可以像左侧这样使用 T() 运算符; T(java.lang.Math).PI // 将PI 值装配到bean的属性中; T(java.lang.Math).random() // 计算得到一个0~1 间的随机数;
#{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的话,计算结果是 后面的字符串;
#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'} // 判断一个字符串是否包含有效的邮件地址;
#{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...
标签:
原文地址:http://blog.csdn.net/pacosonswjtu/article/details/51530519