SpringBoot2学习总结三:了解自动配置原理——容器功能

SpringBoot2学习总结三:了解自动配置原理——容器功能,第1张

SpringBoot2学习总结三:了解自动配置原理——容器功能

⭐️前面的话⭐️
本文章总结自尚硅谷SpringBoot2,视频链接:尚硅谷雷神SpringBoot2零基础入门springboot全套完整版(spring boot2)
上一篇:SpringBoot2学习总结三:了解自动配置原理——SpringBoot特点

⭐️容器功能⭐️ 1. 组件添加

首先准备好两个组件:

Pet组件

public class Pet {
    private String name;

    public Pet() {
    }

    public Pet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + ''' +
                '}';
    }
}

User组件

public class User {
    private String name;
    private Integer age;
    private Pet pet;
    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", pet=" + pet +
                '}';
    }
}

以前我们在Spring中使用xml文件进行配置

而在SpingBoot中我们使用注解开发
1. @Configuration

import com.BraidHu.boot.bean.Pet;
import com.BraidHu.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
    @Bean
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

注释:@Configuration:告诉SpringBoot这是一个配置类 == 配置文件
@Bean:给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。

获取组件的方法:

@SpringBootApplication(scanbasePackages="com.BraidHu")
public class MainApplication {
    public static void main(String[] args) {
        //spring应用启动
        //1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        //2.查看容器里的组件
        String[] names = run.getBeanDefinitionNames();
        for(String name:names){
            System.out.println(name);
        }
    }
}
        

注:

  • 可以自定义组件名:
    例如
 @Bean("tom")
public Pet tomcatPet(){
        return new Pet("tomcat");
    }

获取组件名后可看到:

组件名已变成自定义的名字。

  • 定义的组件是单实例的
    注释:单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例
    如:
        //从容器中获取组件
        Pet tom01=run.getBean("tom", Pet.class);
        //run.getBean()中有两个参数
        // 第一个参数:要获取的组件名
        // 第二个参数:组件类型
        Pet tom02=run.getBean("tom", Pet.class);
        System.out.println("组件:"+(tom01==tom02));//true

从而得到结论:配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的

  • 配置类也是容器的组件
//4.com.hj.boot.config.MyConfig$$EnhancerBySpringCGLIB$f1e1ca@1654a892
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

  • proxyBeanMethods:代理bean的方法
  1. Full模式(proxyBeanMethods = true):【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
    例:
import com.BraidHu.boot.bean.User;
import com.BraidHu.boot.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(scanbasePackages="com.BraidHu")
public class MainApplication {
    public static void main(String[] args) {
        //spring应用启动
        //1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        MyConfig bean = run.getBean(MyConfig.class);
        User u1=bean.user01();
        User u2=bean.user01();
        System.out.println(u1==u2);
    }
}
import com.BraidHu.boot.bean.Pet;
import com.BraidHu.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = true) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
}

运行主程序后得到运行结果为:true.
得出:
Ful模式l或者说@Configuration(proxyBeanMethods = true):外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象

  1. Lite模式:proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
    如:
import com.BraidHu.boot.bean.User;
import com.BraidHu.boot.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(scanbasePackages="com.BraidHu")
public class MainApplication {
    public static void main(String[] args) {
        //spring应用启动
        //1.返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        MyConfig bean = run.getBean(MyConfig.class);
        User u1=bean.user01();
        User u2=bean.user01();
        System.out.println(u1==u2);
    }
}
import com.BraidHu.boot.bean.Pet;
import com.BraidHu.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
}

运行主程序后得到运行结果为:true.
得出:
Lite模式l或者说@Configuration(proxyBeanMethods = false):外部无论对配置类中的这个组件注册方法调用多少次返回的组件都是新创建的

总结

Full模式:全配置模式
外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象。
Lite模式:轻量级配置
Lite模式l或者说@Configuration(proxyBeanMethods = false):外部无论对配置类中的这个组件注册方法调用多少次返回的组件都是新创建的。
组件依赖必须使用Full模式默认。其他默认是否Lite模式

2. @Bean、@Component、@Controller、@Service、@Repository

根据它们的源码可以看到,Controller、Service、Repository其本质就是Component。
它存在的本质只是给开发者看的,对Spring而言它们就都是Component。
@Controller 控制层类,@Service 业务层类,@Repository 持久层(数据库)类,@Component 无法归类到前3种时就称为组件。有需要的同学自行去复习一下Spring注解。

3. @ComponentScan、@import

@ComponentScan:扫描包
@import:自动从类中的无参构造函数创建一个实例注册到 IOC 容器中
【注意】@import所创建的实例在 IOC 容器中默认的id名为类的全限定名,如 User 类就是:com.BraidHu.boot.bean.User
例如:

//4、@import({User.class, DBHelper.class})
//   给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
}
//5.获取组件
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        System.out.println("==============");
        for(String s: beanNamesForType){
            System.out.println(s);
        }
        DBHelper bean1 = run.getBean(DBHelper.class);
        System.out.println(bean1);

可以看到:
输出为:

想要了解更多的请看:@import 高级用法

4. @Conditional

 注:@Conditional中派生了很多的子注解,它们可以添加在@Bean注解的方法上也可以放在配置类上,在方法上满足所需条件时则执行方法中内容并注册到 IOC 容器中如果不满足条件则不注册,在配置类中满足需求时则执行配置类中所有的@Bean方法并注册到 IOC 容器中如果不满足条件则不注册,以@ConditionalOnBean(name=“tom”)为例,当 IOC 容器中拥有id为tom的组件时才会满足条件,否则不满足条件

条件装配:满足Conditional指定的条件,则进行组件注入

@Conditional的派生注解

⭐️实例1:添加@ConditionalOnBea注解在方法组件上⭐️
先将tom组件注释掉,MyConfig类中只剩user01组件。

@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
    //@Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}
public class MainApplication {
    public static void main(String[] args) {
        //spring应用启动
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        boolean tom = run.containsBean("tom");
        System.out.println("容器中有没有tom组件:"+tom);
        boolean user01 = run.containsBean("user01");
        System.out.println("容器中有没有user01组件:"+user01);
    }
}

此时的运行结果为:

现在添加@ConditionalOnBea注解(即@ConditionalOnBean(name=“tom”))

@import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @ConditionalOnBean(name="tom")
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
    //@Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

此时的运行结果为:

总结:

ConditionalOnBean(name=“tom”):表示容器中有组件tom才注入。

⭐️实例2:添加@ConditionalOnBea注解在类上⭐️
情况一:将配置类中的组件tom注释取消并改名为tom2

@import({User.class, DBHelper.class})
@ConditionalOnBean(name="tom")//添加@ConditionalOnBea注解在类上
@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
    @Bean("tom2")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

此时的运行结果为:

情况二:将配置类中的组件tom注释取消

@import({User.class, DBHelper.class})
@ConditionalOnBean(name="tom")//添加@ConditionalOnBea注解在类上
@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
    @Bean("tom")//注意这里的变化
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

再来看此时的运行结果依旧为:

为啥呢?
:因为@ConditionalOnBean(name = “tom”)放在MyConfig类上面,同时tom又在当前类注册的话,不就形成了一个悖论吗。不管tom是在user01前面还是后面,都不会生效。只有@ConditionalOnBean(name = “tom”)放在user01的方法上,同时tom的注册在user01之前,user01才会被注册。也就是注册先后顺序问题。

@import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig{
    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
    @ConditionalOnBean(name="tom")
    @Bean//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        return new User("ChrisHu",18);
    }
}

运行结果

大总结:
1. 放在配置类上表示,当容器中满足条件时,配置类中的组件才生效;
2. 放在配置方法上的时候,表示的意思是当满足条件的时候配置方法才生效;

2. 原生配置文件引入

定义:指以.xml结尾的配置文件,通过@importResource导入后SpringBoot进行解析,完成对应的组件注册位置:在主配置类的上方

例如:配置文件为

======================beans.xml=========================



        
        
    

    
        
    

在MyConfig配置类上加上注解@importResource(“classpath:beans.xml”):导入spring的配置文件来进行生效

@importResource("classpath:beans.xml")//导入spring的配置文件来进行生效
public class MyConfig{

⭐️主程序⭐️

boolean haha = run.containsBean("haha");
boolean hehe = run.containsBean("hehe");
System.out.println("haha:"+haha);//true
System.out.println("hehe:"+hehe);//true
3. 配置绑定

场景例子:我们习惯将经常爱变化的东西写在.properties配置文件中,比如与数据库相关的信息(连接池、URL等)配置到配置文件中,为了方便我们会将配置文件中的内容解析到JavaBean中。这个过程使用java原生代码较为麻烦。

注:如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5681428.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存