💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 环境与profile 在软件开发中,我们的配置,会根据环境变化而不同。每次上线都要更改配置文件,不仅不可控制而且十分不方便。值得庆幸的是,Spring提供了完美的解决方案。 ### 配置profile bean spring 为环境相关的bean 所提供的解决方案其实与构建时的方案没有太大不同。不过Spring并不是在构建的时候做出这样的决策。而是等到运行时再来确定 。这样的设置使同一个部署单元能够适应不同的环境。 在3.1 版本中。Spring引入bean profile 的功能。要使用profile 首先要将所有不同的baen定义整理到一个或多个profile之中。在应用程序部署到每个环境时,确保对应不同的profile处于激活状态。 > Java配置中profile - 在java配置中,我们使用`@Profile`注解指定某个bean属于哪一个prfile. ```java @Configuration @Profile(value = "dev") public class DevelopmentProfileConfig { @Bean public DataSource dataSource(){ return new DataSource() {}; ``` 在这例子中,`@Profile`注解应用在了类级别上。它会告诉Spring这个配置类中的bean只有dev profile激活时才会创建。如果dev profile没有激活的话,那么带有`@Bean`注解的方法都会被忽略掉。 在Spring 3.1中,只能在类级别使用`@Profile`注解。不过在Spring3.2 开始,你可以在方法级别上使用`@Profile`注解。与`@Bean`注解一起使用。 ```java @Configuration public class DevelopmentProfileConfig { @Bean @Profile("dev") public DataSource dataSource(){...} @Bean @Profile("prod") public DataSource jndiDataSource(){...} } ``` > 在XML中装配profile - 在`<beans>` 中通过profile属性配置 ```xml <?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd" profile="dev"> <jdbc:embedded-database id="dataSource"> </jdbc:embedded-database> </beans> ``` - 在`<beans>` 中嵌套`<beans>` 设置profile属性 ```XML <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beans profile="dev"> </beans> <beans profile="prod"> </beans> </beans> ``` > 激活profile Spring 在确定那个profile处于激活状态时,需要依赖两个独立的属性:`spring.profiles.active`和`spring.profiles.default`。如果设置了`spring.profiles.active`属性的话。那么他的值就会用来确定那个profile是激活的。 如果没有设置`spring.profiles.active`属性的话,那Spring将会查找`spring.profiles.default`的值。如果这两个值都没设置,那么就没有激活的profile,因此只会创建那些没有定义profile中的bean - 怎么设置这两个值 1. 作为DispatcherServlet的初始化参数; 2. 作为Web应用的上下文参数; 3. 作为JNDI条目; 4. 作为环境变量; 5. 作为JVM的系统属性; 6. 在集成测试类上,使用`@ActiveProfiles`注解设置。 > 在web.xml文件中设置默认的profile ```xml <!-- 为上下文设默认的profile --> <context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param> <servlet> <servlet-name>taotao-manager</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <!-- 为Servlet设置默认的profile --> <init-param> <param-name>spring.profiles.deault</param-name> <param-value>dev</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> ``` 部署人员设置`spring.profiles.active`后,`spring.profiles.default`设置将不会生效。(系统会优先选择`spring.profiles.active`) **注意** `spring.profiles.active`和`spring.profiles.default` 都是复数形式。这意味着你可以设置多个profiles,不过相同作用的profile设置多个并没有多少意义。你可以通过逗号分隔,来开启多个不相关的profile > 测试profile spring提供了`@ActiveProfiles`注解,我们可以通过它来指定运行测试时要激活那个profile. ```java @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) @ActiveProfiles("dev") public class CDPlayerTest { ``` ### 条件化的 bean 如果我们希望有一个或多个bean 只在应用的类路径包含特定库时才创建,或者我们希望某个bean符合某些条件时才会被创建。那我们应该怎么做那? 在spring4之前,很难实现这种级别的条件化配置,但在Spring4引入了一个新的`@Conditional`注解,它可以用到带有`@Bean`注解的方法之上。如果给定的条件计算结果为true,那么就会创建这个bean.否则这个bean就会被忽略. ```java @Bean @Conditional(TestConditon.class) //条件化创建bean public TestBean testBean(){...} ``` 可以看到,`@Conditional`中指定了一个class,他指明了条件--在上面的例子中就是`TestCinditon`,`@Conditional`将会通过`Conditon`接口进行条件对比: ```java public interface Condition{ boolean matches(ConditionContext ctxt,AnnotatedTypeMetadata metadata); } ``` 设置给`@Conditional`的类可以是任意实现了Condition接口的类型,可以看出,实现这个接口很简单,只需提供matches()方法实现即可,如果matches()方法返回ture,就会创建带有@Conditional注解的bean,false就不创建。 ### 处理自动装配的歧义性 spring引入自动装配为我们减少显示配置提供了很大的帮助。不过仅有一个bean匹配所需的结果时,自动装配才是有效的,如果不仅有一个bean能够匹配结果的话,这种歧义性会阻碍Spring自动装配。 > 如何解决歧义性? 1. 标示首选的bean 在我们生命bean的时候,通过将其中的bean设置为首选bean能够避免自动化装配时的歧义性。当遇见歧义性的时候,Spring将会使用首选的bean,而不是其他可选的bean. Spring种我们通过`@Primary`来表示最喜欢的方案。`@Primary`可以与`@Component`,`@Bean`组合用在Java配置的bean声明中。 ```java @Component @Primary public class IceCream implements Dessert{...} ``` 或者 ```java @Bean @Primary public Dessert iceCream(){ return new IceCream(); } ``` xml配置bean的话 ```xml <bean id="iceCream" class="com.yc.IceCream" primary="true" /> ``` **使用此方法的问题** 但是,如果你标示两个或更多的首先bean,那么它就无法正常工作了。 2. 限定自动装配的bean(一种更强大的解决Spring歧义性的方法) `@Primary`无法将可选范围限定到唯一一个无歧义的选项种,Spring限定符能够完全解决这种歧义性。 `@Qualifier`注解是使用限定符的主要方式。它可以与`@Autowired`和`Inject`协同使用。在注入的时候制定想要注入的是哪个bean ```java @Audowired @Qualifier("iceCream") public void setDessert(Dessert dessert){ this.dessert=dessert; } ``` 这是使用`@Qualifier`最简单的例子。为`@Qualifier`注入所设置的参数就是想要注入的bean的ID,所有使用`@Component`注解声明的类都会创建为bean,并且bean的ID为首字母变为小写的类名。实际上Spring为所有的bean都会给定一个默认的限定符,这个限定符与bean的ID相同。 基于默认的bean ID作为限定符是非常简单的,但是当我们重构IceCream类,将其重命名为Gelato的话,如果这样的话,bean的ID和默认的限定符会变为gelato,这就无法匹配setDessert方法中的限定符,自动装配会失败。 ### 创建自定义的限定符 我们可以通过自定义限定符,而不是依赖将beanID作为限定符。在这里我们所需要的是在bean声明上添加`@Qualifier`注解 ```java @Component @Qualifier("cold") public class IceCream implements Dessert{...} ``` 再注入的地方,我们只需要引用cold限定符就可以了 ```java @Audowired @Qualifier("cold") public void setDessert(Dessert dessert){ this.dessert=dessert; } ``` ## bean的作用域 ## 运行时值注入 前面我们的例子中有值的注入,但是它的实现是将值硬编码在配置类中,但有时候,我们希望避免硬编码值,而是想让这些值在运行时在确定,为了实现这些功能,Spring提供了两种在运行时求值的方式: - 属性占位符 - Spring表达式语言 ### 注入外部的值 在Spring中,处理外部值的最简单方式就是,声明属性源并通过Spring的Enviroment来检索属性。 ```java @Configuration @PropertySource("classpath:com/yc/app.properties") public class CDPlayerConfig { @Autowired Environment env; public void disc(){ System.out.println("titie"+env.getProperty("title")+"---"+"name"+env.getProperty("name")); } ``` ```app.properties title=你的名字 name=小明 ``` 在本例中`@PropertySource`引用了类路径中一个app.properties文件,这个属性文件会加载到Spring的Environment中,。 > 深入学习 Spring的Environment 如上图所示,前两种getProperty()方法会返回String类型的值。第一种是直接获取值,第二种重载方法是如果属性值不存在的时候,会使用一个默认值。 剩下两种getProperty()方法与之前的两种类似,但他们不会将所有值都视为String类型。 ```java int connectionCount=env.getProperty("db.connection.count",Integer.class,30); ``` Environment还提供了几个与属性相关的方法,如果你在使用getProperty()方法的时候没有指定默认值,并且这个属性没有第一的话,获取的值是null,如果你希望属性必须要定义,那么可以使用` String title=env.getRequiredProperty("title");`,如果属性没有定义,那么他将会抛出异常。 如果检测属性是否存的话,你可以使用Environment的`containsProperty()`方法 除了和属性相关的功能以外,Environment还提供了一些检查哪些profile处于激活状态。 ### 解析属性占位符 Spring 一直支持将属性定义到外部的属性文件中,并使用占位符值将其插入到Spring bean中。在Spring装配中,占位符形式是`${...}` ```xml <bean id="sgtPepers" class="soundsystem.SgtPeppers" c:_title="${title}"></bean> ``` 如果我们依赖组件扫描和自动装配,我们可以使用注解`@Value` ```java public void disc(@Value("${title}") String title){ System.out.println("titie"+title); } ``` 为了使用占位符,我们必须配置一个bean. - java配置 ```java @Bean public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(){ return new PropertySourcesPlaceholderConfigurer(); } ``` - xml配置 ```xml <context:property-placeholder/> ``` 解析外部属性能够将值的处理推迟到运行。 ## 使用Spring表达式语言进行装配 Spring表达式语言能够以一种强大和简洁的方式装配到bean. SpEl拥有的特性: - 使用bean的ID来引用bean; - 调用方法和访问对象的属性 - 对值进行算术,关系和逻辑运算 - 正则表达式匹配 - 集合操作