SpringBoot注入数据的方式

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> SpringBoot注入数据的方式

点击上方“Java知音”,选择“置顶公众号”

技术文章第一时间送达!

作者:胖先森 juejin.im/post/5c1f119ee51d4568f03b8857

关于注入数据说明

SpringBoot注入数据的方式

1.不通过配置文件注入数据

通过@Value将外部的值动态注入到Bean中,使用的情况有:

  • 注入普通字符串
  • 注入操作系统属性
  • 注入表达式结果
  • 注入其他Bean属性:注入Student对象的属性name
  • 注入文件资源
  • 注入URL资源
  • 辅助代码

    
    package com.hannpang.model;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component(value = "st")//对student进行实例化操作
    public class Student {
        @Value("悟空")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    测试@Value的代码

    
    package com.hannpang.model;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.io.Resource;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SimpleObject {
    
        @Value("注入普通字符串")
        private String normal;
    
        //关于属性的KEY可以查看System类说明
        @Value("#{systemProperties['java.version']}")//--使用了SpEL表达式
        private String systemPropertiesName; // 注入操作系统属性
    
        @Value("#{T(java.lang.Math).random()*80}")//获取随机数
        private double randomNumber; //注入表达式结果
    
        @Value("#{1+2}")
        private double sum; //注入表达式结果 1+2的求和
    
        @Value("classpath:os.yaml")
        private Resource resourceFile; // 注入文件资源
    
        @Value("http://www.baidu.com")
        private Resource testUrl; // 注入URL资源
    
        @Value("#{st.name}")
        private String studentName;
    
        //省略getter和setter方法
    
        @Override
        public String toString() {
            return "SimpleObject{" +
                    "normal='" + normal + ''' +
                    ", systemPropertiesName='" + systemPropertiesName + ''' +
                    ", randomNumber=" + randomNumber +
                    ", sum=" + sum +
                    ", resourceFile=" + resourceFile +
                    ", testUrl=" + testUrl +
                    ", studentName='" + studentName + ''' +
                    '}';
        }
    }
    

    Spring的测试代码

    
    package com.hannpang;
    
    import com.hannpang.model.SimpleObject;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Demo04BootApplicationTests {
    
        @Autowired
        private SimpleObject so;
    
        @Test
        public void contextLoads()  {
            System.out.println(so);
        }
    }
    

    运行结果为:SimpleObject{normal=’注入普通字符串’, systemPropertiesName=’1.8.0_172’, randomNumber=56.631954541947266, sum=3.0, resourceFile=class path resource [os.yaml], testUrl=URL [http://www.baidu.com], studentName=’悟空’}

    2.通过配置文件注入数据

    通过@Value将外部配置文件的值动态注入到Bean中。配置文件主要有两类:

    application.properties、application.yaml application.properties在spring boot启动时默认加载此文件

    自定义属性文件。自定义属性文件通过@PropertySource加载。@PropertySource可以同时加载多个文件,也可以加载单个文件。如果相同第一个属性文件和第二属性文件存在相同key,则最后一个属性文件里的key起作用。加载文件的路径也可以配置变量,如下文的 ${anotherfile.configinject},此值定义在第一个属性文件config.properties

    在application.properties中加入如下测试代码

    
    app.name=一步教育
    

    在resources下面新建第一个属性文件config.properties内容如下

    
    book.name=西游记
    anotherfile.configinject=system
    

    在resources下面新建第二个属性文件config_system.properties内容如下

    我的目的是想system的值使用第一个属性文件中定义的值

    
    book.name.author=吴承恩
    

    下面通过 @Value(“${app.name}”)语法将属性文件的值注入bean属性值,详细代码见:

    
    package com.hannpang.test;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    
    @Component
    @PropertySource(value = {"classpath:config.properties","classpath:config_${anotherfile.configinject}.properties"})
    public class LoadPropsTest {
    
        @Value("${app.name}")
        private String appName; // 这里的值来自application.properties,spring boot启动时默认加载此文件
    
        @Value("${book.name}")
        private String bookName; // 注入第一个配置外部文件属性
    
        @Value("${book.name.author}")
        private String author; // 注入第二个配置外部文件属性
    
        @Autowired
        private Environment env;  // 注入环境变量对象,存储注入的属性值
    
        //省略getter和setter方法
    
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
    
        @Override
        public String toString(){
            StringBuilder sb = new StringBuilder();
            sb.append("bookName=").append(bookName).append("rn")
                    .append("author=").append(author).append("rn")
                    .append("appName=").append(appName).append("rn")
                    .append("env=").append(env).append("rn")
                    // 从eniroment中获取属性值
                    .append("env=").append(env.getProperty("book.name.author")).append("rn");
            return sb.toString();
        }
    
    }
    

    测试代码

    
    package com.hannpang;
    
    import com.hannpang.model.SimpleObject;
    import com.hannpang.test.LoadPropsTest;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Demo04BootApplicationTests {
    
        @Autowired
        private LoadPropsTest lpt;
    
        @Test
        public void loadPropertiesTest()  {
    
            System.out.println(lpt);
        }
    }
    

    运行结果为: bookName=西游记 author=吴承恩 appName=一步教育 env=StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, MapPropertySource {name='Inlined Test Properties'}, MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}, ResourcePropertySource {name='class path resource [config_system.properties]'}, ResourcePropertySource {name='class path resource [config.properties]'}]} env=吴承恩

    3. #{…}和${…}的区别演示

    A.${…}的用法

    {}里面的内容必须符合SpEL表达式,通过 @Value(“${app.name}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如 @Value("${app.name:胖先森}")

    部分代码

    
    // 如果属性文件没有app.name,则会报错
    //  @Value("${app.name}")
    //  private String name;
    
    // 使用app.name设置值,如果不存在则使用默认值
    @Value("${app.name:胖先森}")
    private String name;
    

    B.#{…}的用法

    部分代码直接演示

    
    // SpEL:调用字符串Hello World的concat方法
    @Value("#{'Hello World'.concat('!')}")
    private String helloWorld;
    
    // SpEL: 调用字符串的getBytes方法,然后调用length属性
    @Value("#{'Hello World'.bytes.length}")
    private String helloWorldbytes;
    

    C.#{…}和${…}混合使用

    ${...} #{...}可以混合使用,如下文代码执行顺序:通过 ${server.name}从属性文件中获取值并进行替换,然后就变成了 执行SpEL表达式 {‘server1,server2,server3’.split(‘,’)}

    
    // SpEL: 传入一个字符串,根据","切分后插入列表中, #{}和${}配置使用(注意单引号,注意不能反过来${}在外面,#{}在里面)
    @Value("#{'${server.name}'.split(',')}")
    private ListString servers;
    

    在上文中在 #{}外面, ${}在里面可以执行成功,那么反过来是否可以呢 ${}在外面, #{}在里面,如代码

    
    // SpEL: 注意不能反过来${}在外面,#{}在里面,这个会执行失败
    @Value("${'HelloWorld'.concat('_')}}")
    private ListString servers2;
    

    答案是不能。

    因为spring执行${}是时机要早于#{}。

    在本例中,Spring会尝试从属性中查找 #{‘HelloWorld’.concat(‘_’)},那么肯定找到,由上文已知如果找不到,然后报错。所以 ${}在外面, #{}在里面是非法操作

    D.用法总结

  • `#{…}` 用于执行SpEl表达式,并将内容赋值给属性
  • `${…}` 主要用于加载外部属性文件中的值
  • `#{…}` 和`${…}` 可以混合使用,但是必须`#{}`外面,`${}`在里面
  • 4.@Value获取值和@ConfigurationProperties获取值比较

    SpringBoot注入数据的方式

    配置文件yml还是properties他们都能获取到值;

  • 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
  • 如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
  • 关于数据校验的部分代码

    
    @Component
    @ConfigurationProperties(prefix = "person")
    @Validated
    public class Person {
       //lastName必须是邮箱格式
        @Email
        private String lastName;
    

    5. @ImportResource引入配置文件

    不推荐的使用方式

    Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;

    想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上

    
    @ImportResource(locations = {"classpath:beans.xml"})
    

    导入Spring的配置文件让其生效

    编写配置文件信息

    
    ?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"
        bean id="helloService" class="com.hanpang.springboot.service.HelloService"/bean
    /beans
    

    大概了解就好,我们基本上不使用这种方式

    6.@Configuration注解

    SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式

  • 配置类@Configuration作用于类上,相当于一个xml配置文件
  • 使用@Bean给容器中添加组件,作用于方法上
  • 
    /**
     * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
     *
     * 在配置文件中用beanbean/标签添加组件
     * bean id="helloService" class="com.hanpang.springboot.service.HelloService"/bean
     */
    @Configuration
    public class MyAppConfig {
    
        //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
        @Bean
        public HelloService helloService02(){
            System.out.println("配置类@Bean给容器中添加组件了...");
            return new HelloService();
        }
    }
    

    使用Bean注入太麻烦,我们更加喜欢使用扫描的方式

    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    import com.wx.dao.IUserDao;
    import com.wx.dao.UserDaoImpl;
    
    //通过该注解来表明该类是一个Spring的配置,相当于一个传统的ApplicationContext.xml
    @Configuration
    //相当于配置文件里面的context:component-scan/标签,扫描这些包下面的类的注解
    @ComponentScan(basePackages="com.hanpang.dao,com.hanpang.service")
    public class SpringConfig {
        // 通过该注解来表明是一个Bean对象,相当于xml中的bean
        //bean的id值默认是方法名userDao
        /*
        @Bean
        public HelloService helloService02(){
            System.out.println("配置类@Bean给容器中添加组件了...");
            return new HelloService();
        }
        */
    }
    

    附录

    随机数

    
    ${random.value}、${random.int}、${random.long}
    ${random.int(10)}、${random.int[1024,65536]}
    

    END

    Java面试题专栏

    SpringBoot注入数据的方式

    我知道你 “在看SpringBoot注入数据的方式

    原文始发于微信公众号(Java知音):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    转载声明:转载请注明出处,本技术博客是本人原创文章

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> SpringBoot注入数据的方式


     上一篇
    SpringBoot实现通用的接口参数校验 SpringBoot实现通用的接口参数校验
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:cipher juejin.im/post/5af3c25b5188253064651c76 本文介绍基于Spring Boot和JDK8编写一个A
    下一篇 
    Dubbo整合SpringBoot Dubbo整合SpringBoot
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:雪山上的蒲公英 cnblogs.com/zjfjava/p/9696086.html 目前的dubbo已支持和springboot集成,还是之前的例