珠海好的网站制作平台,系统官网网站模板下载,世界工厂网登录,网站编程培训学校有哪些Spring Framework对将从属性文件中找到的属性值注入到bean或Configuration类中提供了很好的支持。 但是#xff0c;如果将单个属性值注入这些类中#xff0c;则会遇到一些问题。 这篇博客文章指出了这些问题#xff0c;并描述了我们如何解决它们。 让我们开始吧。 如果使… Spring Framework对将从属性文件中找到的属性值注入到bean或Configuration类中提供了很好的支持。 但是如果将单个属性值注入这些类中则会遇到一些问题。 这篇博客文章指出了这些问题并描述了我们如何解决它们。 让我们开始吧。 如果使用Spring Boot则应使用其Typesafe配置属性。 您可以从以下网页中获取有关此信息的更多信息 Spring Boot参考手册的23.7节Typesafe配置属性 EnableConfigurationProperties批注的Javadoc ConfigurationProperties批注的Javadoc 在Spring Boot中使用ConfigurationProperties 很简单但并非没有问题 如果将单个属性值注入到我们的bean类中我们将面临以下问题 1.注入多个属性值很麻烦 如果我们通过使用Value批注注入单个属性值或者通过使用Environment对象来获取属性值则注入多个属性值会很麻烦。 假设我们必须向UrlBuilder对象注入一些属性值。 该对象需要三个属性值 服务器的主机 app.server.host 服务器监听的端口 app.server.port 使用的协议 app.server.protocol 当UrlBuilder对象构建用于访问Web应用程序的不同功能的URL地址时将使用这些属性值。 如果我们通过使用构造函数注入和Value注释注入这些属性值则UrlBuilder类的源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;Component
public class UrlBuilder {private final String host;private final String port;private final String protocol;Autowiredpublic UrlBuilder(Value(${app.server.protocol}) String protocol,Value(${app.server.host}) String serverHost,Value(${app.server.port}) int serverPort) {this.protocol protocol.toLowercase();this.serverHost serverHost;this.serverPort serverPort;}
} 补充阅读 Value注释的Javadoc 如果我们通过使用构造函数注入和Environment类注入这些属性值则UrlBuilder类的源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;Component
public class UrlBuilder {private final String host;private final String port;private final String protocol;Autowiredpublic UrlBuilder(Environment env) {this.protocol env.getRequiredProperty(app.server.protocol).toLowercase();this.serverHost env.getRequiredProperty(app.server.host);this.serverPort env.getRequiredProperty(app.server.port, Integer.class);}
} 补充阅读 环境接口的Javadoc 我承认这看起来还不错。 但是当所需属性值的数量增加和/或我们的类也具有其他依赖项时将所有这些属性都注入是很麻烦的。 2.我们必须指定多个属性名称或记住使用常量 如果我们将单个属性值直接注入需要它们的Bean中并且有多个BeanA和B需要相同的属性值那么我们想到的第一件事就是在两个Bean类中指定属性名称 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(Value(${app.server.protocol}) String protocol) {this.protocol protocol.toLowercase();}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(Value(${app.server.protocol}) String protocol) {this.protocol protocol.toLowercase();}
} 这是一个问题因为 因为我们是人类所以我们会打错字 。 这不是一个大问题因为在启动应用程序时我们会注意到它。 但是它使我们放慢了速度。 这使维护更加困难 。 如果更改属性的名称则必须对使用该属性的每个类进行此更改。 我们可以通过将属性名称移到常量类来解决此问题。 如果这样做我们的源代码如下所示 public final PropertyNames {private PropertyNames() {}public static final String PROTOCOL ${app.server.protocol};
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(Value(PropertyNames.PROTOCOL) String protocol) {this.protocol protocol.toLowercase();}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(Value(PropertyNames.PROTOCOL) String protocol) {this.protocol protocol.toLowercase();}
} 这可以解决维护问题但前提是所有开发人员都记得要使用它。 我们当然可以通过使用代码审阅来强制执行此操作但这是审阅者必须记住要检查的另一件事。 3.添加验证逻辑成为问题 假设我们有两个类 A和B 它们需要app.server.protocol属性的值。 如果我们将此属性值直接注入到A和B Bean中并且想要确保该属性的值为http或https则必须 将验证逻辑添加到两个bean类中。 将验证逻辑添加到实用程序类中并在需要验证是否给出了正确的协议时使用它。 如果我们将验证逻辑添加到两个bean类则这些类的源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(Value(${app.server.protocol}) String protocol) {checkThatProtocolIsValid(protocol);this.protocol protocol.toLowercase();}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase(http) !protocol.equalsIgnoreCase(https)) {throw new IllegalArgumentException(String.format(Protocol: %s is not allowed. Allowed protocols are: http and https.,protocol));}}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(Value(${app.server.protocol}) String protocol) {checkThatProtocolIsValid(protocol);this.protocol protocol.toLowercase();}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase(http) !protocol.equalsIgnoreCase(https)) {throw new IllegalArgumentException(String.format(Protocol: %s is not allowed. Allowed protocols are: http and https.,protocol));}}
} 这是一个维护问题因为A和B类包含复制粘贴代码。 通过将验证逻辑移至实用程序类并在创建新的A和B对象时使用它可以稍微改善这种情况。 完成此操作后我们的源代码如下所示 public final class ProtocolValidator {private ProtocolValidator() {}public static void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase(http) !protocol.equalsIgnoreCase(https)) {throw new IllegalArgumentException(String.format(Protocol: %s is not allowed. Allowed protocols are: http and https.,protocol));}}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(Value(${app.server.protocol}) String protocol) {ProtocolValidator.checkThatProtocolIsValid(protocol);this.protocol protocol.toLowercase();}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(Value(${app.server.protocol}) String protocol) {ProtocolValidator.checkThatProtocolIsValid(protocol);this.protocol protocol.toLowercase();}
} 问题在于我们仍然必须记住要调用此实用程序方法。 我们当然可以通过使用代码审阅来强制执行此操作但是再次重申审阅者必须记住要检查的另一件事。 4.我们不能编写好的文档 我们不能编写描述应用程序配置的好的文档因为我们必须将此文档添加到实际的属性文件中使用Wiki或编写* gasp * Word文档。 这些选项中的每个选项都会引起问题因为我们不能在编写需要从属性文件中找到属性值的代码的同时使用它们。 如果需要阅读文档则必须打开“外部文档”这会导致上下文切换非常昂贵 。 让我们继续前进找出如何解决这些问题。 将属性值注入配置Bean 通过将属性值注入配置Bean中我们可以解决前面提到的问题。 首先为示例应用程序创建一个简单的属性文件。 创建属性文件 我们要做的第一件事是创建一个属性文件。 我们的示例应用程序的属性文件称为application.properties 其外观如下 app.nameConfiguration Properties example
app.production.mode.enabledfalseapp.server.port8080
app.server.protocolhttp
app.server.hostlocalhost 让我们继续并配置示例应用程序的应用程序上下文。 配置应用程序上下文 我们的示例应用程序的应用程序上下文配置类有两个目标 启用Spring MVC并导入其默认配置。 确保读取了从application.properties文件中找到的属性值并且可以将其注入Spring Bean中。 通过执行以下步骤我们可以实现其第二个第二个目标 配置Spring容器以扫描所有包含bean类的软件包。 确保从application.properties文件中找到的属性值已读取并添加到Spring Environment中 。 确保从Value批注中找到的$ {...}占位符替换为从当前Spring Environment及其PropertySources中找到的属性值。 WebAppContext类的源代码如下所示 import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;Configuration
ComponentScan({net.petrikainulainen.spring.trenches.config,net.petrikainulainen.spring.trenches.web
})
EnableWebMvc
PropertySource(classpath:application.properties)
public class WebAppContext {/*** Ensures that placeholders are replaced with property values*/BeanPropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}
} 补充阅读 ComponentScan批注的Javadoc PropertySource批注的Javadoc Spring框架参考手册的5.13.4 PropertySource部分 PropertySourcesPlaceholderConfigurer类的Javadoc 下一步是创建配置Bean类并将从属性文件中找到的属性值注入到它们中。 让我们找出如何做到这一点。 创建配置Bean类 让我们创建以下描述的两个配置bean类 WebProperties类包含用于配置使用的协议服务器的主机以及服务器侦听的端口的属性值。 ApplicationProperties类包含用于配置应用程序名称并标识是否启用生产模式的属性值。 它还具有对WebProperties对象的引用。 首先 我们必须创建WebProperties类。 我们可以按照以下步骤进行操作 创建WebProperties类并使用Component注释对其进行注释。 将最终协议 serverHost和serverPort字段添加到创建的类中。 通过使用构造函数注入将属性值注入到这些字段中并确保协议字段的值必须为http或https忽略大小写。 添加用于获取实际属性值的吸气剂。 WebProperties类的源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;Component
public final class WebProperties {private final String protocol;private final String serverHost;private final int serverPort;Autowiredpublic WebProperties(Value(${app.server.protocol}) String protocol,Value(${app.server.host}) String serverHost,Value(${app.server.port}) int serverPort) {checkThatProtocolIsValid(protocol);this.protocol protocol.toLowercase();this.serverHost serverHost;this.serverPort serverPort;}private void checkThatProtocolIsValid(String protocol) {if (!protocol.equalsIgnoreCase(http) !protocol.equalsIgnoreCase(https)) {throw new IllegalArgumentException(String.format(Protocol: %s is not allowed. Allowed protocols are: http and https.,protocol));}}public String getProtocol() {return protocol;}public String getServerHost() {return serverHost;}public int getServerPort() {return serverPort;}
} 其次 我们必须实现ApplicationProperties类。 我们可以按照以下步骤进行操作 创建ApplicationProperties类并使用Component注释对其进行注释。 将最终名称 productionModeEnabled和webProperties字段添加到创建的类。 通过使用构造函数注入将属性值和WebProperties bean注入ApplicationProperties bean。 添加用于获取字段值的吸气剂。 ApplicationProperties类的源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;Component
public final class ApplicationProperties {private final String name;private final boolean productionModeEnabled;private final WebProperties webProperties;Autowiredpublic ApplicationProperties(Value(${app.name}) String name,Value(${app.production.mode.enabled:false}) boolean productionModeEnabled,WebProperties webProperties) {this.name name;this.productionModeEnabled productionModeEnabled;this.webProperties webProperties;}public String getName() {return name;}public boolean isProductionModeEnabled() {return productionModeEnabled;}public WebProperties getWebProperties() {return webProperties;}
} 让我们继续研究该解决方案的好处。 这对我们有何帮助 现在我们创建了包含从application.properties文件中找到的属性值的Bean类。 该解决方案可能看起来像是过度设计但与传统的简单方法相比它具有以下优点 1.我们只能注入一个Bean而不是多个属性值 如果我们将属性值注入到配置Bean中然后通过使用构造函数注入将该配置Bean注入UrlBuilder类则其源代码如下所示 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class UrlBuilder {private final WebProperties properties;Autowiredpublic UrlBuilder(WebProperties properties) {this.properties properties;}
} 如我们所见这使我们的代码更整洁 尤其是在使用构造函数注入的情况下 。 2.我们只需指定一次属性名称 如果将属性值注入到配置Bean中则只能在一个地方指定属性名称。 这意味着 我们的代码遵循关注点分离原则。 可从配置Bean中找到属性名称而其他需要此信息的Bean不知道其来源。 他们只是使用它。 我们的代码遵循“ 不要重复自己”的原则。 因为属性名称仅在一个位置在配置bean中指定所以我们的代码更易于维护。 另外IMO我们的代码看起来也更加简洁 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(WebProperties properties) {this.protocol properties.getProtocol();}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(WebProperties properties) {this.protocol properties.getProtocol();}
} 3.我们只需要编写一次验证逻辑 如果将属性值注入到配置Bean中则可以将验证逻辑添加到配置Bean中而其他Bean不必知道它。 这种方法具有三个好处 我们的代码遵循关注点分离原则因为可以从配置bean它所属的地方中找到验证逻辑。 其他的豆子不必知道。 我们的代码遵循“不要重复自己”的原则因为验证逻辑是从一个地方找到的。 创建新的Bean对象时我们不必记住调用验证逻辑因为在创建配置Bean时我们可以强制执行验证规则。 另外我们的源代码看起来也更加干净 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class A {private final String protocol;Autowiredpublic A(WebProperties properties) {this.protocol properties.getProtocol();}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;Component
public class B {private final String protocol;Autowiredpublic B(WebProperties properties) {this.protocol properties.getProtocol();}
} 4.我们可以从IDE中访问文档 我们可以通过向配置bean中添加Javadoc注释来记录应用程序的配置。 完成此操作后当我们编写需要这些属性值的代码时可以从IDE中访问此文档。 我们不需要打开其他文件或阅读维基页面。 我们可以简单地继续编写代码避免上下文切换的开销 。 让我们继续并总结从这篇博客文章中学到的知识。 摘要 这篇博客文章告诉我们将属性值注入配置Bean 帮助我们遵循关注点分离原则。 有关配置属性和属性值验证的内容封装在我们的配置bean中。 这意味着使用这些配置bean的bean不知道属性值来自何处或如何验证它们。 帮助我们遵循“不要重复自己”的原则因为1我们只需指定一次属性名称然后2可以将验证逻辑添加到配置bean。 使我们的文档更易于访问。 使我们的代码更易于编写阅读和维护。 但是这无助于我们弄清应用程序的运行时配置。 如果我们需要此信息则必须读取从服务器中找到的属性文件。 这很麻烦。 我们将在我的下一篇博客文章中解决此问题。 PS您可以从Github获得此博客文章的示例应用程序 。 翻译自: https://www.javacodegeeks.com/2015/04/spring-from-the-trenches-injecting-property-values-into-configuration-beans.html