底层注解@Configuration、@Bean、@ComponentScan 、@Conditional、@Import、@ConfigurationProperties(prefix=“xxx“)

底层注解

@Configuration

把一个类变为配置类,回想下,之前spring中的 xxx.xml Bean配置文件,@Configuration代替了 之前的xml配置文件,那之前xml中的 <bean id="xxx" class="com.lxc.xxx.xxx" /> 在类中如何表示? 在一个方法上用注解 @Bean 来表示 <Bean> 标签,默认方法名表示之前Bean标签上的id,也就是组件的名字默认是方法名称,class值(返回类型)就是方法的返回值,也就是组件的类型

配置类

此时,两个组件(我的理解就是两个对象而已)Person和Cat会被加载到IOC的容器里边,在启动入口类中,通过 app.getBean() 的方式可以获取到任何一个组件!!!

@Configuration(proxyBeanMethods = true)
public class ConfigBean {
    /**
     * @Bean 相当于 之前
     * <Bean id="person" class="com.lxc.xxx.person">
     * </Bean>
     * @return User
     */
    @Bean // 方法名就是之前xml中 id的值,也可以自定义 @Bean(name="alisName")
    public Person person() {
        return new Person(); // 返回值 相当于class 返回类型
    }
    @Bean
    public Cat cat () {
        return new Cat();
    }
}

Cat

public class Cat {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

Person

public class Person {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

 测试

@SpringBootApplication
public class BootAndVueProjectApplication {
    private static final Logger LOG = LoggerFactory.getLogger(BootAndVueProjectApplication.class);
    public static void main(String[] args) {
        // 返回IOC容器
        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        Person person = app.getBean("person", Person.class);
        Cat cat = app.getBean("cat", Cat.class);
        System.out.println(person);
        System.out.println(cat);
    }

}

几个提示点

(1)通过@Configuration配置一个类,里边的所有注册的组件默认是单例模式(调用多少次都会从缓存中获取除了第一次调用)。

@SpringBootApplication
public class BootAndVueProjectApplication {
    private static final Logger LOG = LoggerFactory.getLogger(BootAndVueProjectApplication.class);
    public static void main(String[] args) {SpringApplication(BootAndVueProjectApplication.class);
        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        Person person = app.getBean("person", Person.class);
        Person person1 = app.getBean("person", Person.class);
        System.out.println(person == person1);
    }
}


 

(2)配置类本身也是一个组件

@SpringBootApplication
public class BootAndVueProjectApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        ConfigBean configBean = app.getBean(ConfigBean.class);
        System.out.println(configBean);
}

(3)springboot 2.0  @Configuration多了一个 这样的属性 proxyBeanMethod :是否代理Bean的方法,默认为true,什么意思?

很简单,在外部调用配置类中两个组件的注册方法(person和Cat)时,如果 proxyBeanMethod 属性取默认值true,那么不管调用方法多少次,都会取容器中的单实例对象。

@SpringBootApplication
public class BootAndVueProjectApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        ConfigBean configBean = app.getBean(ConfigBean.class);
        System.out.println(configBean.person() == configBean.person());
    }
}

如果 proxyBeanMethod 属性为false,每次调用,都会拿到一个新对象

@Configuration(proxyBeanMethods = false)
public class ConfigBean {// ··· ···}
@SpringBootApplication
public class BootAndVueProjectApplication {
    public static void main(String[] args) {

        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        ConfigBean configBean = app.getBean(ConfigBean.class);
        System.out.println(configBean.person() == configBean.person());
    }
}


 

(3)有上边 proxyBeanMethod 属性为true,引出一个组件依赖的问题。

假设Person类中有Cat cat属性,可以理解为这个人 A 想领养了一只猫 B,下边来测试下,在外部通过person来创建这个猫 B,和调用 cat方法来创建猫A,是不是同一只猫呢?

@Configuration(proxyBeanMethods = true)
public class ConfigBean {
    @Bean
    public Person person() {
        Person person = new Person();
        person.setCat(cat());
        return person;
    }
    @Bean
    public Cat cat () {
        return new Cat();
    }
}
public class Person {
    private Cat cat;
    public Cat getCat() {
        return cat;
    }
    public void setCat(Cat cat) {
        this.cat = cat;
    }
}
public class Cat {
}

测试:

很明显,是同一只猫,这个人领养的这只猫就是B

@SpringBootApplication
public class BootAndVueProjectApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(BootAndVueProjectApplication.class, args);
        Person person = app.getBean("person", Person.class);
        Cat cat = app.getBean("cat", Cat.class);
        System.out.println(person.getCat() == cat);
    }
}

当 proxyBeanMethod 属性为false时,如果组件间存在依赖关系(一个类中属性引用了别的类),每次外部在调用时,都会产生一个新的对象。

实际上平时开发,如果配置类中的只是单纯的注册组件的话,通常会把proxyBeanMethod 属性设置为false,如果 proxyBeanMethod = true ,在springboot启动时,每次都会去检查注册组件的方法 在容器中是否存在,启动很慢;如果设置为false,在启动时,会跳过检查容器,启动速度非常快。

向容器注册组件,除了上边@Bean注解,还有以下注解:

@Component、@Controller、@Service、@Repository

@Import

@import注解与上边几个一样,都是把组件注册到IOC容器里边,注解属性默认是数组,里边元素必须为Class类型。

@Import({Person.class, Cat.class})
@Configuration(proxyBeanMethods = true)
public class TestConfig {
}
public class Cat {
}
public class Person {
}

@ComponentScan 包扫描

@ComponentScan(basePackages="包全路径")

@Conditional 条件装配

条件装配:满足 @Conditional 指定的条件时,则进行组件注入,基于@Conditional 注解衍生出非常多的条件判断注解,springboo源码中有很多基于这个注解的文件类,存在一个按需加载的概念,就是用@Conditional 注解和衍生注解来做的。
下边以 @ConditionalOnBean 为例 记录下。

// 当容器中存在名字为 user 的组件时,下边所有注册的主键才能生效,否则不生效!
@ConditionalOnBean(name = {"user"})
@Import({Person.class, Cat.class})
@Configuration(proxyBeanMethods = true)
public class TestConfig {
    @Bean
    public Person person () {
        return new Person();
    }
    @Bean
    public Cat cat () {
        return new Cat();
    }
}

@ImportResource("classpass:beans.xml") 导入spring的xml文件

一些老项目,都会使用xml的方式向容器中注册组件 , 我们可以使用@ImportResource("classpass:beans.xml") 注解的方式去加载xml配置文件来注册组件。

配置绑定(获取配置文件中的内容)

有两种方式:

方式一:

如何使用java读取到properties文件中的内容,并把它封装到JavaBean 中,以供随时使用。在springboot中可以使用以下注解非常方便的拿到properties配置文件中的键值对:

@ConfigurationProperties(prefix="xxx")
prefix:在配置文件中的属性前缀

Person.java

// 为了方便,不在使用 @Configuration+@Bean方式注册组件,可以直接在类上添加@Component注解
// ,spring会把这个类直接注册到IOC容器里
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private String address;
    
    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }
}

 测试

@SpringBootApplication
public class SprintBoot01Application {
    public static void main(String[] args) {
          ConfigurableApplicationContext ctx = SpringApplication.run(SprintBoot01Application.class, args);
          Person person = ctx.getBean("person", Person.class);
        System.out.println(person.getName());
        System.out.println(person.getAddress());
    }
}

 测试时,发现报了一个错误,仔细琢磨下边这个报错:
在名为person的组件中 绑定属性失败,下边是配置文件中的键值对,最后一行Reason划重点,原因:address这个属性没有setter方法,原来如此,猜测:springboot底层为每个属性赋值时,使用的是每个属性的settter方法为其赋值,加上属性的settter方法,结果输出了。

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private String address;
    public void setAddress(String address) {
        this.address = address;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public String getAddress() {
        return address;
    }
}

方式二:

在配置文件中 添加 @EnableConfigurationProperties 注解,开启配置文件与 类的绑定。

// (1)开启配置绑定:把配置文件中的属性与Person类绑定,使用这个注解
@EnableConfigurationProperties(Person.class)
@Configuration(proxyBeanMethods = true)
public class TestConfig {
    @Bean
    public Person person () {
        return new Person();
    }
}

Person.java 

@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private String address;

    public void setAddress(String address) {
        this.address = address;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值