博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring funcitons
阅读量:6820 次
发布时间:2019-06-26

本文共 11103 字,大约阅读时间需要 37 分钟。

1. java config test

@Configuration@ComponentScanpublic class CDPlayerConfig { }
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes=CDPlayerConfig.class)public class CDPlayerTest {  @Rule  public final StandardOutputStreamLog log = new StandardOutputStreamLog();  @Autowired  private MediaPlayer player;

2. java xml test

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations="classpath:META-INF/spring/soundsystem.xml")public class CDPlayerXMLConfigTest {  @Rule  public final StandardOutputStreamLog log = new StandardOutputStreamLog();  @Autowired  private MediaPlayer player;

3. java config ,xml 配置相互引用

@Configition@ComponentScan(basePackages = "com.young")public class config{}
@Configition@ComponentScan(basePackages = {"com.young","com.youn4j"})public class config{}
@Configition@ComponentScan(basePackageClasses = {CdPlayer.class,DVDPlayer.class})public class config{}

在xml配置中引用java config配置

@Configurationpublic class CDConfig { @Bean public CompactDisc compactDisc() { return new SgtPeppers(); }}

在java config配置中应用xml配置

@Configuration@Import(CDPlayerConfig.class)@ImportResource("classpath:cd-config.xml")public class SoundSystemConfig {}
Sgt. Pepper's Lonely Hearts Club Band
With a Little Help from My Friends
Lucy in the Sky with Diamonds
Getting Better
Fixing a Hole
public class BlankDisc implements CompactDisc { private String title; private String artist; private List
tracks; public BlankDisc(String title, String artist, List
tracks) { this.title = title; this.artist = artist; this.tracks = tracks; } public void play() { System.out.println("Playing " + title + " by " + artist); for (String track : tracks) { System.out.println("-Track: " + track); } }}

4. 环境与profile

@Configurationpublic class DataSourceConfig {    @Bean(destroyMethod = "shutdown")  @Profile("dev")  public DataSource embeddedDataSource() {    return new EmbeddedDatabaseBuilder()        .setType(EmbeddedDatabaseType.H2)        .addScript("classpath:schema.sql")        .addScript("classpath:test-data.sql")        .build();  }  @Bean  @Profile("prod")  public DataSource jndiDataSource() {    JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();    jndiObjectFactoryBean.setJndiName("jdbc/myDS");    jndiObjectFactoryBean.setResourceRef(true);    jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);    return (DataSource) jndiObjectFactoryBean.getObject();  }}

激活profile,在web.xml中配置,需要配置两个地方:为上下文设置profile,为servlet设置profile

spring.profile.default
dev
spring.profile.default
dev

使用profile进行测试

import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.test.context.ActiveProfiles;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.myapp.DataSourceConfig;public class DataSourceConfigTest {  @RunWith(SpringJUnit4ClassRunner.class)  @ContextConfiguration(classes=DataSourceConfig.class)  @ActiveProfiles("dev")  public static class DevDataSourceTest {    @Autowired    private DataSource dataSource;        @Test    public void shouldBeEmbeddedDatasource() {      assertNotNull(dataSource);      JdbcTemplate jdbc = new JdbcTemplate(dataSource);      List
results = jdbc.query("select id, name from Things", new RowMapper
() { @Override public String mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getLong("id") + ":" + rs.getString("name"); } }); assertEquals(1, results.size()); assertEquals("1:A", results.get(0)); } }

5. 条件化bean@Conditional,用在带有@Bean注解的方法上.给定的条件为true,创建,否则,忽略这个bean.

@Configurationpublic class MagicConfig {  @Bean  @Conditional(MagicExistsCondition.class)  public MagicBean magicBean() {    return new MagicBean();  }  }package com.habuma.restfun;import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.env.Environment;import org.springframework.core.type.AnnotatedTypeMetadata;public class MagicExistsCondition implements Condition {  @Override  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {    Environment env = context.getEnvironment();    return env.containsProperty("magic");  }  }

6.处理自动装配的歧义性

 同一个接口有多个类实现了这个接口,就会造成多种匹配结果,阻碍spring自动装配属性,构成参数,或方法参数。

@Componentpublic class Cake implements Dessert{...}@Componentpublic class Cookies implements Dessert{...}@Componentpublic class IceCream implements Dessert{...}

Spring 会抛出NoUniqueBeanDefinitionException异常

Spring提供了多种可选方案来解决这样的问题。你可以将可选bean中的某一个设为首选(primary)的bean,或者使用限定符(qualifier)来帮助Spring将可选的bean的范围缩小到只有一个bean。

@Primary能够与@Component组合用在组件扫描的bean上,也可以与@Bean组合用在Java配置的bean声明中。

@Component@Primarypublic class IceCream implements Dessert{...}

如果你通过Java配置显式地声明IceCream,那么@Bean方法应该如下所示:

@Bean@Primarypublic Dessert iceCream(){    return new IceCream();}

使用XML配置bean的话,同样可以实现这样的功能。<bean>元素有一个primary属性用来指定首选的bean:

标识首选bean的目的都是为了在发生歧义的情况下告诉spring哪些bean要去首选,但是多个实现同一接口的bean如果都标记了@primary,name就会出错了。

@Qualifier注解是使用限定符的主要方式。它可以与@Autowired和@Inject协同使用,在注入的时候指定想要注入进去的是哪个bean,从而来缩小选择范围。

@Autowired@Qualifier("iceCream")public void setDessert(Dessert dessert){    this.dessert = dessert;}

@Qualifier注解所设置的参数就是想要注入的bean的ID。所有使用@Component注解声明的类都会创建为bean,并且bean的ID为首字母变为小写的类名。因此,@Qualifier("iceCream")指向的是组件描时所创建的bean,并且这个bean是IceCream类的实例。如果你重构了IceCream类,将其重命名为Gelato的话,那此时会发生什么情况呢?如果这样的话,bean的ID和默认的限定符会变为gelato,这就无法匹配setDessert()方法中的限定符。自动装配会失败。

为bean设置自己的限定符,而不是依赖于将bean ID作为限定符。

@Component@Qualifier("cold")public class IceCream implements Dessert(){...}@Autowired@Qualifier("cold")public Dessert iceCream(){    return new IceCream()}

如果同一接口的不同实现同样有上述限定符,同样也会出现问题,为了解决问题就是再次将范围缩小

@Component@Qualifier("cold")@Qualifier("fruity")public class IceCream implements Dessert(){...}@Autowired@Qualifier("cold")@Qualifier("fruity")public void setDessert(Dessert dessert){    this.dessert = dessert;}

这里只有一个小问题:Java不允许在同一个条目上重复出现相同类型的多个注解。

@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface Cold {}
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface Creamy{}
 
@Component@Cold @Creamy
public class IceCream implements Dessert(){...}
@Autowired
@Cold @Creamy
public void setDessert(Dessert dessert){ this.dessert = dessert; }

声明与注入使用相同的注解(基于@Qualifier)来限定,为最佳选择方案。

7.bean的作用域

Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。让对象保持无状态并且在应用中反复重用这些对象可能并不合理。有时候,可能会发现,你所使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是什么好主意了,因为对象会被污染,稍后重用的时候会出现意想不到的问题。

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

原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Rquest):在Web应用中,为每个请求创建一个bean实例。

使用@Scope注解,它可以与@Component或@Bean一起使用

@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class Notepad {  ...}
@Bean  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  public Notepad notepad() {    return new Notepad();  }

使用会话和请求作用域

如果能够实例化在会话和请求范围内共享的bean,那将是非常有价值的事情。例如,在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。就购物车bean来说,会话作用域是最为合适的,因为它与给定的用户关联性最大。要指定会话作用域,我们可以使用@Scope注解,它的使用方式与指定原型作用域是相同的:

@Component@Scope(value = "sesson",proxyMode = ScopedProxyMode.INTERFACES)public ShoppingCart cart(){}

这里,我们将value设置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉Spring为Web应用中的每个会话创建一个ShoppingCart。这会创建多个ShoppingCart bean的实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean实际上相当于单例的。,@Scope同时还有一个proxyMode属性,它被设置成ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题.因为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属性。如配置所示,proxyMode属性被设置成了ScopedProxyMode.INTERFACES,这表明这个代理要实现ShoppingCart接口,并将调用委托给实现bean。如果ShoppingCart是接口而不是类的话,这是可以的(也是最为理想的代理模式)。但如果ShoppingCart是一个具体的类的话,Spring就没有办法创建基于接口的代理了。此时,它必须使用CGLib来生成基于类的代理。所以,如果bean类型是具体类的话,我们必须要将proxyMode属性设置为ScopedProxyMode.TARGET_CLASS,以此来表明要以生成目标类扩展的方式创建代理。尽管我主要关注了会话作用域,但是请求作用域的bean会面临相同的装配问题。因此,请求作用域的bean应该也以作用域代理的方式进行注入。

运行时注入:

为了避免硬编码,让这些值在运行的时候在确定,spring提供了两种在运行时求值的方法:属性占位符(Propert placeholder),spring表达式(SpEL)

注入外部的值:

注入外部的值最简单的方法就是声明属性源,通过spirng的Environment来检索属性,getPropertiey有四个重载方法

String getProperty(String key);

String getProperty(String key, String defaultValue);

<T> T getProperty(String key, Class<T> targetType);

<T> T getProperty(String key, Class<T> targetType, T defaultValue);

如果希望这个属性必须要定义,可以使用getRequiredProperty(String key),如果没有定义则抛出IllegalStateException异常。

如果要验证这个属性是否存在,可以调用containsProperty(String key)方法。

@Configuration@PropertySource("classpath:app.properties")public class ExpressiveConfig {    @Autowired    Environment evn;    public void test(){        String title = evn.getProperty("desc.title");        String content = evn.getProperty("desc.content","");        int money = evn.getProperty("desc.money",Integer.class,0);    }    }

 8. 运行时注入

  将一个值注入到bean属性或者构造器参数中,我们希望避免硬编码值,而是想让这些值在运行时在确认。Spring提供了两种在运行时确认值得方式:属性占位符,Spring表达式语言(SpEL);

  处理外部值的最简单方式就是声明属性源并通过Spring的Environment来检索属性。

  

@Configuration@PropertySource("classpath:/com/soundsystem/app.properties")public class EnvironmentConfig {  @Autowired  Environment env;    @Bean  public BlankDisc blankDisc() {    return new BlankDisc(        env.getProperty("disc.title"),        env.getProperty("disc.artist"));      env.getProperty("disc.title", "Rattle and Hum"),         env.getProperty("disc.artist", "U2"));  }  }

这个属性文件会加载到Spring的Environment中,稍后可以从这里检索属性。

 

转载于:https://www.cnblogs.com/yangfei-beijing/p/6607517.html

你可能感兴趣的文章
AndroidStudio用Cmake方式编译NDK代码(cmake配置.a库)
查看>>
Kafka入门
查看>>
【Infragistics教程】Sketch Prototypes的可用性研究和用户视频
查看>>
移植Modbus到STM32F103(4):串口数据长度和校验的支持
查看>>
linux命令,如何根据关键字查询,如何替换某个关键字,vi中如何复制
查看>>
IT兄弟连 JavaWeb教程 Servlet会话跟踪 Cookie技术原理
查看>>
js算法: 图的两种表示方法以及广度优先算法
查看>>
CSS定位问题(3):相对定位,绝对定位
查看>>
如何给网站加入优雅的实时反爬虫策略
查看>>
手动配置无线网卡
查看>>
OSChina 周四乱弹 ——黑丝短裙java程序员同事
查看>>
设置iptables之后不能正常访问ftp解决方法
查看>>
maven使用国内镜像
查看>>
移动端rem布局
查看>>
改变状态栏的颜色
查看>>
UIImagePickerController说明
查看>>
01.C语言入门
查看>>
Spring-Batch中MapJobExplorerFactoryBean的配置方式
查看>>
jsp与iframe跨域访问的一个方法
查看>>
ViewPager + Fragment 取消预加载
查看>>