Spring boot中参数注入,@Value失效以及解决方案

问题

项目中我们都要要尽量避免将参数直接写进程序里,这样一旦需要需要修改配置,我们可以只需要在配置文件里做修改,而不必在程序里找,这样可以避免很多错误,个人项目可能不会注意这一点,但是需要上线发布的项目,Configure配置文件就显得非常重要!现在很多公司其实都有这方面的应用,甚至有专门的中间件可以专门管理配置文件,即时生效,不必去上线修改参数,这不是我们今天说的重点。先看内容:

application-dev.properties
#URL
confirmURL=http://127.0.0.1:8081/confirm

这里写图片描述
然后在Controller类里面通过@Value将参数注入进来,最后的确成功了。因此基于此经验,我便在其他使用的类里面也采用这样的方式注入参数,但是发现去失效了,报错为NULL,说明参数并没有我们料想的被注入进来。

原因

这是为什么呢?为什么在Controller类就成功了?在其他类里面我尝试过@Service,@Component,@Configure,但是我没有成功,经过查询,原来,在使用这些参数生成Bean类的时候,我们注入的参数还没有生效,因此获取不到,而不是由于参数注入的问题,而在某些场景,spring可能做了优化,是的参数优先注入,再生成Bean。那么有没有好的方法可以解决这个问题呢?

方案

首先,我们的参数的直接注入是肯定不行了,那么我们就采用初始化类的方式,将配置信息集中初始化。

public class PropertyUtil {

  private static final Logger logger = LoggerFactory.getLogger(PropertyUtil.class);
  private static Properties props;

  static {
    loadProps();
  }

  synchronized static private void loadProps() {
    logger.info("start to load properties.......");
    props = new Properties();
    InputStream in = null;
    try {

      in = PropertyUtil.class.getClassLoader().
      getResourceAsStream("application.properties");
      props.load(in);
      logger.info(name);
    } catch (FileNotFoundException e) {
      logger.error("properties not found!");
    } catch (IOException e) {
      logger.error("IOException");
    } finally {
      try {
        if (null != in) {
          in.close();
        }
      } catch (IOException e) {
        logger.error("properties close Exception!");
      }
    }
    // logger.info(props);
    logger.info("load properties over...........");
  }

  public static String getProperty(String key) {
    if (null == props) {
      loadProps();
    }
    return props.getProperty(key);
  }
}

通过Util类我们一次行加载参数,在需要获取的地方,直接通过

private static String 
   unsubscribeUrl = PropertyUtil.getProperty("unsubscribeUrl");

这样就可以获取我们所需要的参数了,直接定义为静态变量,一次读取,后面都可以直接使用。

引申

在Spring boot中参数配置有三个 application.properties, application-dev.properties, application-prod.properties

application.properties
spring.profiles.active=dev

通过在这里修改dev 或者 prod 我可以配置两套配置,一套用于产品,一套是开发,那么我如果来根据我配置的信息来读取不同的配置呢,这又让我头疼了,看着代码,我想到,既然他可以读取配置文件了,那么我为何不先获取一下 application.properties里面的信息,然后看dev,或者prod来加载不同的配置呢,因此,我加了一段判断代码:

 in = PropertyUtil.class.getClassLoader().getResourceAsStream("application.properties");
      props.load(in);
      String name = getProperty("spring.profiles.active");
      if (name.equals("dev")) {
        in = PropertyUtil.class.getClassLoader().getResourceAsStream("application-dev.properties");
      } else if (name.equals("prod")) {
        in = PropertyUtil.class.getClassLoader().getResourceAsStream("application-prod.properties");
      }
      props.load(in);
      logger.info(name);

通过测试,name的确是我 在application.properties配置的信息,因此,这具很好的解决了两套配置,在线下测试跟发布,我只需要修改一个配置文件就可以完成转换,节省时间的同时,也避免了错误发生,很多上线的问题都是因为配置的问题,因此这个问题一定要小心,当然还有很多方案,后面接触到我会继续总结,最近在代码的重构,发现自己的代码有很多问题。

@Component
public class ExchangeServiceUtil {

  private static String mailServer;
  private static String user;
  private static String password;
  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private ExchangeServiceUtil(@Value("${spring.EWS.mailServer}") String mailServer,
      @Value("${spring.EWS.user}") String user,
      @Value("${spring.EWS.password}") String password) {
    this.mailServer = mailServer;
    this.user = user;
    this.password = password;
  }

  private static ExchangeServiceUtil instance = new ExchangeServiceUtil(mailServer, user, password);

  public static ExchangeServiceUtil getExchangeServiceUtil() {
    return instance;
  }

  public ExchangeService getExchangeService() throws URISyntaxException {
    ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
    ExchangeCredentials credentials = new WebCredentials(user, password);
    service.setCredentials(credentials);
    service.setUrl(new URI(mailServer));
    //    service.autodiscoverUrl("<your_email_address>");
    return service;
  }
}

我通过构造函数的方式,也成功的把参数注入到了里面,这个方法也是偶然接触到的,在某些工具,比如数据池,这是微软的邮件发送配置,都是可以采用这样的方法,构造函数可以加载参数,然后在生成Bean,很好的避开了那个问题,这个问题我还会继续深究,看看有什么发现,如果你有高见,欢迎留言!

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在 Netty 客户端实现重连功能,可以按照以下步骤进行操作: 1. 添加 Netty 依赖 在你的 Spring Boot 项目的 pom.xml 文件添加以下依赖: ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.42.Final</version> </dependency> ``` 2. 创建 Netty 客户端 创建一个 Netty 客户端的示例代码如下: ```java @Component public class NettyClient { private EventLoopGroup group; private Bootstrap bootstrap; private Channel channel; private ChannelFuture channelFuture; @Value("${netty.server.host}") private String host; @Value("${netty.server.port}") private int port; @PostConstruct public void start() throws Exception { group = new NioEventLoopGroup(); bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringEncoder(), new StringDecoder(), new NettyClientHandler(NettyClient.this)); } }); channelFuture = bootstrap.connect(host, port).sync(); channel = channelFuture.channel(); } @PreDestroy public void stop() throws Exception { channel.closeFuture().sync(); group.shutdownGracefully(); } public void sendMessage(String message) { channel.writeAndFlush(message); } public boolean isChannelActive() { return channel.isActive(); } public ChannelFuture getChannelFuture() { return channelFuture; } } ``` 上述代码,我们创建了一个 Netty 客户端,包括了 Netty 的启动、停止和发送消息等功能,并添加了 isChannelActive 和 getChannelFuture 方法,用于判断通道是否活跃和获取通道的 ChannelFuture 对象。 3. 创建 Netty 客户端处理器 创建一个 Netty 客户端处理器的示例代码如下: ```java @Component public class NettyClientHandler extends SimpleChannelInboundHandler<String> { private NettyClient nettyClient; public NettyClientHandler(NettyClient nettyClient) { this.nettyClient = nettyClient; } @Override protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { // 处理接收到的消息 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 处理异常 ctx.close(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // 通道失效时触发重连 if (nettyClient.isChannelActive()) { return; } EventLoop eventLoop = ctx.channel().eventLoop(); eventLoop.schedule(nettyClient::start, 10, TimeUnit.SECONDS); super.channelInactive(ctx); } } ``` 上述代码,我们继承了 Netty 的 SimpleChannelInboundHandler 类,并重写了 messageReceived、exceptionCaught 和 channelInactive 方法,用于处理接收到的消息、异常和通道失效时触发重连。 4. 发送消息 我们可以在任何时候通过 Netty 客户端的 sendMessage 方法向服务端发送消息,示例代码如下: ```java @Autowired private NettyClient nettyClient; public void send() { nettyClient.sendMessage("hello world"); } ``` 通过上述步骤,我们就可以在 Spring Boot 实现 Netty 客户端重连功能了。当通道失效时,Netty 客户端会自动触发重连操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值