SpringBoot-容器功能

  |   0 评论   |   0 浏览

SpringBoot-容器功能

1.Spring 注入组件的注解

1.@Component、@Controller、 @Service、@Repository

说明: 这些在 Spring 中的传统注解仍然有效,通过这些注解可以给容器注入组件

2.案例演示

1.创建src\main\java\com\llp\bean\A.java

@Repository
public class A {
}

2.修改MainApp.java主启动类

@SpringBootApplication(scanBasePackages = {"com"})
public class MainApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
    	//演示spring中传统的注解依然可以使用@Controller @Service @Repository
        A bean = ioc.getBean(A.class);
        //com.llp.bean.A@6c17c0f8
        System.out.println(bean);
    }
}

A类被@Repository修饰,可以看到A被注入到了Spring的ioc容器中,而默认的以类名首字母小写的方式作为bean的id

image-20220728225711741

image-20220728225525910

2.@Configuration

1.应用实例

● @Configuration 应用实例需求

演示在 SpringBoot, 如何通过@Configuration 创建配置类来注入组件

● 回顾传统方式如何通过配置文件注入组件

1.创建src\main\java\com\llp\bean\Monster.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Monster {
    private Integer id;
    private String name;
    private Integer age;
    private String skill;
}

2.创建src\main\resources\beans.xml

<?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="monster03" class="com.llp.bean.Monster">
      <property name="id" value="100"/>
      <property name="name" value="孙悟空"/>
      <property name="age" value="1000"/>
      <property name="skill" value="刷光棍"/>
   </bean>
</beans>

3.修改主启动类

@SpringBootApplication(scanBasePackages = {"com"})
public class MainApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        Monster monster03 = ac.getBean("monster03", Monster.class);
        //Monster(id=100, name=孙悟空, age=1000, skill=刷光棍)
        System.out.println(monster03);
    }
}

SpringBoot是支持传统的方式去获取bean对象的

image-20220728230729495

● 使用 SpringBoot 的@Configuration 添加/注入组件

1.创建src\main\java\com\llp\config\BeanConfig.java

@Configuration
public class BeanConfig {

    /**
     * 1. @Bean : 给容器添加组件, 就是Monster bean
     * 2. monster01() : 默认 你的方法名monster01 作为Bean的名字/id
     * 3. Monster : 注入类型, 注入bean的类型是Monster
     * 4. new Monster(200,"库里林",200,"铁头功") 注入到容器中具体的Bean
     * 5. @Bean(name = "monster_nmw") : 在配置、注入Bean指定名字
     * 6. 默认是单例注入
     * 7. 通过 @Scope("prototype")  可以每次返回新的对象,就多例.
     */
    //@Bean(name = "monster_nmw")
    @Bean
    //@Scope("prototype")
    public Monster monster01(){
        return new Monster(200,"库里林",200,"铁头功");
    }
}

2.修改主启动类,进行测试

@SpringBootApplication(scanBasePackages = {"com"})
public class MainApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
        Monster monster01 = ioc.getBean("monster01", Monster.class);
        //Monster(id=200, name=库里林, age=200, skill=铁头功)
        System.out.println(monster01);
    }
}

image-20220728231440370

debug查看ioc容器

image-20220728231732844

beanDefinitionMap, 只是存放了 bean 定义信息, 真正存放 Bean 实例的在 singleonObjectis 的 Map 中, 对于非单例,是每次动态反射生成的实例

image-20220728231815481

2.@Configuration注意事项和细节

  1. 配置类本身也是组件, 因此也可以获取, 测试 修改 MainApp.java
  2. SpringBoot2 新增特性: proxyBeanMethods 指定 Full 模式 和 Lite 模式

(1)修改src\main\java\com\llp\config\BeanConfig.java 设置proxyBeanMethods = true(默认为true)

/**
 * 1. proxyBeanMethods:代理bean的方法
 * (1) Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的, 是代理方式】
 * (2) Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的, 是非代理方式】
 * (3) 特别说明: proxyBeanMethods 是在 调用@Bean方法 才生效,因此,需要先获取BeanConfig 组件,再调用方法
 * 而不是直接通过 SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean, proxyBeanMethods 值并没有生效
 * (4) 如何选择: 组件依赖必须使用Full模式默认。如果不需要组件依赖使用 Lite模式
 * (5) Lite模式 也称为轻量级模式,因为不检测依赖关系,运行速度快
 */
@Configuration(proxyBeanMethods = true)
public class BeanConfig {

    /**
     * 1. @Bean : 给容器添加组件, 就是Monster bean
     * 2. monster01() : 默认 你的方法名monster01 作为Bean的名字/id
     * 3. Monster : 注入类型, 注入bean的类型是Monster
     * 4. new Monster(200,"库里林",200,"铁头功") 注入到容器中具体的Be
     * 5. @Bean(name = "monster_nmw") : 在配置、注入Bean指定名字
     * 6. 默认是单例注入
     * 7. 通过 @Scope("prototype")  可以每次返回新的对象,就多例.
     */
    //@Bean(name = "monster_nmw")
    @Bean
    //@Scope("prototype")
    public Monster monster01(){
        return new Monster(200,"库里林",200,"铁头功");
    }
}

修改主程序进行测试

@SpringBootApplication(scanBasePackages = {"com"})
public class MainApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
        BeanConfig bean = ioc.getBean(BeanConfig.class);
        Monster monster01 = bean.monster01();
        Monster monster02 = bean.monster01();
        //monster01 ---  1533783975
        System.out.println("monster01 ---  " + monster01.hashCode());
        //monster02 ---  1533783975
        System.out.println("monster02 ---  " + monster02.hashCode());
    }
}

Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的, 是代理方式】

image-20220728232421518

(2)修改src\main\java\com\llp\config\BeanConfig.java 设置proxyBeanMethods = fasle

/**
 * 1. proxyBeanMethods:代理bean的方法
 * (1) Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的, 是代理方式】
 * (2) Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的, 是非代理方式】
 * (3) 特别说明: proxyBeanMethods 是在 调用@Bean方法 才生效,因此,需要先获取BeanConfig 组件,再调用方法
 * 而不是直接通过 SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean, proxyBeanMethods 值并没有生效
 * (4) 如何选择: 组件依赖必须使用Full模式默认。如果不需要组件依赖使用 Lite模式
 * (5) Lite模式 也称为轻量级模式,因为不检测依赖关系,运行速度快
 */
@Configuration(proxyBeanMethods = false)
public class BeanConfig {

    /**
     * 1. @Bean : 给容器添加组件, 就是Monster bean
     * 2. monster01() : 默认 你的方法名monster01 作为Bean的名字/id
     * 3. Monster : 注入类型, 注入bean的类型是Monster
     * 4. new Monster(200,"库里林",200,"铁头功") 注入到容器中具体的Be
     * 5. @Bean(name = "monster_nmw") : 在配置、注入Bean指定名字
     * 6. 默认是单例注入
     * 7. 通过 @Scope("prototype")  可以每次返回新的对象,就多例.
     */
    //@Bean(name = "monster_nmw")
    @Bean
    //@Scope("prototype")
    public Monster monster01(){
        return new Monster(300,"库里林",200,"铁头功");
    }
}

这里要注意一个坑,在前面我们使用lombok的@Data注解会重新hashCode方法如下图,这样会导致一个问题,只要我们对象属性的值一样那么计算出来的hashCode值就是一样的,因此要验证(lite模式)轻量级模式就需要去修改Monster.java不要使用lombok @Data注解的方式

image-20220728235333224

image-20220728235235547

  1. 配置类可以有多个, 就和 Spring 可以有多个 ioc 配置文件是一个道理.

(1)创建src\main\java\com\llp\config\BeanConfig2.java

@Configuration
public class BeanConfig2 {

    @Bean
    public Monster monster02() {
        return new Monster(800,"蚂蚁精",80,"吃小昆虫");
    }
}

(2)修改主启动类测试

@SpringBootApplication(scanBasePackages = {"com"})
public class MainApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args);
        Monster monster02 = ioc.getBean("monster02", Monster.class);
        //Monster(id=800, name=蚂蚁精, age=80, skill=吃小昆虫)
        System.out.println(monster02);
    }
}

3.@Import

1.应用实例

演示在 SpringBoot, 如何通过 @Import 来注入组件

1.创建src\main\java\com\llp\bean\Cat.java和src\main\java\com\llp\bean\Dog.java

public class Cat {
}
public class Dog {
}

2.创建src\main\java\com\llp\config\BeanConfig3.java,采用@Import({Dog.class, Cat.class})的方式注入bean到ioc容器中

/**
 * 1. @Import 代码 可以看到,可以指定 class的数组, 可以注入指定类型的Bean
 * public @interface Import {
 *
 * 	 	Class<?>[] value()}
 *
 * 2. 通过@Import 方式注入了组件, 默认组件名字/id就是对应类型的全类名
 */
@Import({Dog.class, Cat.class})
@Configuration
public class BeanConfig3 {
}

3.运行主程序,查看ioc容器单例池

image-20220729223955258

4.@Conditional

1.@Conditional 介绍

  1. 条件装配:满足 Conditional 指定的条件,则进行组件注入
  2. @Conditional 是一个根注解,下面有很多扩展注解

image-20220729224434961

3.@Conditional扩展注解应用场景

image-20220729224738586

2.应用实例

  1. 要求: 演示在 SpringBoot, 如何通过 @ConditionalOnBean 来注入组件
  2. 只有在容器中有 name = cat007组件时,才注入 dog01, 代码如下
@Bean(name = "cat007")
public Cat cat01(){
    return new Cat();
}

/**
 * 1. @ConditionalOnBean(name = "cat007") 表示
 * 2. 当容器中有一个Bean , 名字是cat007 (类型不做约束), 就注入dog01这个Dog bean
 * 3. 如果没有 名字是cat007 的Bean 就不注入dog01这个Dog bean
 * 4. @ConditionalOnMissingBean(name = "cat007") 表示在容器中,
 * 没有 名字/id 为 cat007 才注入dog01这个Bean
 * 5. @ConditionalOnBean(name = "cat007") 也可以放在配置类
 * 表示对该配置类的所有要注入的组件,都进行条件约束.
 */
@Bean
@ConditionalOnBean(name = "cat007")
public Dog dog01(){
    return new Dog();
}

image-20220729231628468

image-20220729231751011

5.@ImportResource

1.作用

原生配置文件引入, 也就是可以直接导入 Spring 传统的 beans.xml ,可以认为是 SpringBoot 对 Spring 容器文件的兼容.

不使用@ImportResource引入,从ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args); ioc中是获取不到bean的

image-20220729232414148

image-20220729232353816

2.@ImportResource 应用实例

  1. 需求: 将 beans.xml 导入到 BeanConfig.java 配置类, 并测试是否可以获得 beans.xml 注入/配置的组件

beans.xml

<?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="monster03" class="com.llp.bean.Monster">
      <property name="id" value="100"/>
      <property name="name" value="孙悟空"/>
      <property name="age" value="1000"/>
      <property name="skill" value="刷光棍"/>
   </bean>
</beans>

beans02.xml

<?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">
    <!--配置了Monster bean-->
    <bean id="monster04" class="com.llp.bean.Monster">
        <property name="name" value="狐狸精"></property>
        <property name="age" value="9000"></property>
        <property name="skill" value="美人计"></property>
        <property name="id" value="2000"></property>
    </bean>
</beans>
  1. 创建新的 BeanConfig4.java来测试, 使用@ImportResource 导入 beans.xml、beans02.xml
/**
 * @Retention(RetentionPolicy.RUNTIME)
 * @Target({ElementType.TYPE})
 * @Documented
 * public @interface ImportResource {
 *     @AliasFor("locations")
 *     String[] value() default {};
 *
 *     @AliasFor("value")
 *     String[] locations() default {};
 *
 *     Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
 * }
 */
@Configuration
@ImportResource(locations = {"classpath:beans.xml","classpath:beans02.xml"})
public class BeanConfig4 {

}

3.运行主启动类,进行测试

image-20220729232938593

6.配置绑定

1.作用

一句话:使用 Java 读取到 SpringBoot 核心配置文件 application.properties 的内容, 并且把它封装到 JavaBean 中

2.应用实例

  1. 需求: 将 application.properties 指定的 k-v 和 JavaBean 绑定

在application.properties文件中加入

furn01.id=100
furn01.name=悟空
furn01.price=0.02

创建src\main\java\com\llp\bean\Furn.java

这里javaBean需要提供get和set方法,在容器启动时会利用反射调用set方法进行赋值,在获取bean进行序列化时会调用get方法

@Data
@Component
@ConfigurationProperties(prefix = "furn01")
public class Furn {
    private Integer id;
    private String name;
    private Double price;
}

修改HelloController.java

@RestController
public class HelloController {

    @Autowired
    private Furn furn;

    @RequestMapping("/hello")
    public String hello(){
        return "hello SpringBoot";
    }

    @RequestMapping("/furn")
    public Furn furn(){
        System.out.println(furn);
        return furn;
    }
}

启动容器访问测试

image-20220730083748167

3.注意事项和细节

  1. 如果 application.properties 有中文, 需要转成 unicode 编码写入, 否则出现乱码;也可以设置idea工具的编码格式

  1. 使用 @ConfigurationProperties(prefix = "furn01") 会提示如下信息, 但是不会影响使用

image-20220730084406208

  1. 解决 @ConfigurationProperties(prefix = "furn01") 提示信息, 在 pom.xml 增加依赖, 即可

​ 4.配置绑定还可以在配置类中添加 @EnableConfigurationProperties({Furn.class})配置如下:

@Data
//@Component
@ConfigurationProperties(prefix = "furn01")
public class Furn {
    private Integer id;
    private String name;
    private Double price;
}
@Configuration
@EnableConfigurationProperties({Furn.class})
public class BeanConfig5 {

}

重启测试,发现也是可以的

image-20220730084807645


标题:SpringBoot-容器功能
作者:llp
地址:https://llinp.cn/articles/2022/08/09/1660051378670.html