• Spring系列12: `@Value` `@Resource` `@PostConstruct` `@PreDestroy` 详解


    本文内容

    1. @Resource实现依赖注入
    2. @Value详细使用
    3. @PostConstruct @PreDestroy的使用

    @Resource实现依赖注入

    前面章节介绍了使用@Autowired注入依赖的详细用法,感兴趣的可以翻看前面的文章。Spring 还支持通过在字段或 bean 的Setter方法上使用 JSR-250 @Resource 注解进行注入。

    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface Resource {
    	// 指定名称
        String name() default "";
    }
    

    基本使用

    依赖组件定义

    @Component
    public class RepositoryA implements RepositoryBase {
    }
    
    @Component
    public class RepositoryB implements RepositoryBase {
    }
    

    使用@Resource注入依赖

    @Component
    public class Service1 {
        // 字段
        @Resource
        private RepositoryA repositoryA;
    
        private RepositoryB repositoryB;
    
        // Setter方法
        @Resource
        public void setRepositoryB(RepositoryB repositoryB) {
            this.repositoryB = repositoryB;
        }
    	// ...
      
    }
    

    运行测试

    @org.junit.Test
    public void test() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig.class);
        Service1 service1 = context.getBean(Service1.class);
        System.out.println(service1);
        context.close();
    }
    // 结果
    Service1{repositoryA=com.crab.spring.ioc.demo09.RepositoryA@1622f1b, repositoryB=com.crab.spring.ioc.demo09.RepositoryB@72a7c7e0}
    

    @Resource@Autowired的区别

    1. 使用范围不同:@Resource使用范围是类字段和Setter方法,@Autowired范围更广:类字段、Setter方法、构造方法、方法参数。
    2. 当依赖缺失时,@Resource注入会报错,@Autowired(required=false)可以避免报错。
    3. @Resource有属性可以指定依赖的bean名称,@Autowired@Qulifier也可以达到该效果。

    @Value详细使用

    @Value 通常用于注入外部化属性。在字段或方法构造函数参数级别使用的注释,指示注解元素的默认值表达式。

    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Value {
    
    	// 实际值表达式如 #{systemProperties.myProp} 
    	// 或属性占位符如${my.app.myProp}
    	String value();
    }
    

    案例1

    使用@Value注入配置属性值

    @Component
    public class MovieRecommender {
       private final  String catalog;
    
       public MovieRecommender(@Value("${catalog.name}") String catalog) {
          this.catalog = catalog;
       }
    
       // ...
    }
    

    外部配置文件demo10/application.properties

    catalog.name=MovieCatalog
    

    容器中通过@PropertySource配置文件资源

    @Configuration
    @ComponentScan(basePackages = "com.crab.spring.ioc.demo10")
    @PropertySource("classpath:demo10/application.properties")
    public class AppConfig {
    
    }
    

    运行测试

    @org.junit.Test
    public void test() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig.class);
        MovieRecommender recommender = context.getBean(MovieRecommender.class);
        System.out.println(recommender);
        context.close();
    }
    // 结果
    MovieRecommender{catalog='MovieCatalog'}
    

    从结果看,外部配置文件中的属性值成功注入。

    案例2:无法解析属性值

    Spring 提供了一个默认的宽松嵌入式值解析器。它将尝试解析属性值,如果无法解析,属性名称(例如 ${catalog.name})将作为值注入。

    将配置文件内容修改如下

    xxx.catalog.name=MovieCatalog	
    

    运行同样上一个案例的测试,可以发现无法解析到属性值则将属性名称注入了。

    MovieRecommender{catalog='${catalog.name}'}	
    

    如果需要严格控制不存在的值,可以声明一个 PropertySourcesPlaceholderConfigurer并注入到Spring中

    @Configuration
    @ComponentScan(basePackages = "com.crab.spring.ioc.demo10")
    @PropertySource("classpath:demo10/application.properties")
    public class AppConfig {
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    }
    

    运行同样的测试程序,此时严格模式下会抛出异常

    java.lang.IllegalArgumentException: Could not resolve placeholder 'catalog.name' in value "${catalog.name}"
    

    扩展: Spring Boot 默认配置一个 PropertySourcesPlaceholderConfigurer bean,它将从 application.properties 和 application.yml 文件中获取属性。

    案例3: 提供默认值

    可以提供默认值在无法解析属性值作为属性值注入。

    @Component
    public class MovieRecommender {
       private final  String catalog;
    
       public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
          this.catalog = catalog;
       }
     }
    

    此时配置文件如下

    xxx.catalog.name=MovieCatalog
    

    运行同样的测试程序,注入的是默认值

    MovieRecommender{catalog='defaultCatalog'}
    

    案例4:支持SpEL表达式

    SpEL表达式后续专门讲,此处不展开。

    @PostConstruct @PreDestroy的使用

    Spring支持生命周期回调接口注解,@PostConstruct @PreDestroy是JSR 250提供的。

    @PostConstruct 注解的方法在容器初始化化bean的阶段回调。

    @PostConstruct 注解的方法在容器销毁bean的阶段回调。

    直接来看案例

    @Component
    public class FoodRecommender {
    
       @PostConstruct
       public void onInit() {
          System.out.println("FoodRecommender onInit");
       }
       @PreDestroy
       public void onDestroy() {
          System.out.println("FoodRecommender onDestroy");
       }
    
    }
    

    注解的配置相当于下面的xml配置文件

    <bean class="com.crab.spring.ioc.demo10.FoodRecommender" id="foodRecommender"
          init-method="onInit"
          destroy-method="onInit"></bean>
    

    运行下测试程序并观察结果

    @org.junit.Test
    public void test1() {
        System.out.println("开始初始化容器");
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig.class);
        FoodRecommender bean = context.getBean(FoodRecommender.class);
        System.out.println("使用容器中");
        System.out.println("销毁容器");
        context.close();
    }
    // 结果
    开始初始化容器
    FoodRecommender onInit
    使用容器中
    销毁容器
    FoodRecommender onDestroy
    

    总结

    本文介绍@Resource实现依赖注入,@Value详细使用和@PostConstruct @PreDestroy的使用。

    本篇源码地址: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo09

    知识分享,转载请注明出处。学无先后,达者为先!

  • 相关阅读:
    c++输入输出文件操作stream
    lintcode 3605 · 二维网格偏移 【数组相关,模拟即可】
    大家都能看得懂的源码之ahooks useInfiniteScroll
    Transit path
    我的十年编程路 2016年篇
    UI组件Kendo UI for jQuery R3 2022亮点——拥有全新的按钮组件
    MySql 执行count(1)、count(*) 与 count(列名) 区别
    Vue 官方文档2.x教程学习笔记 1 基础 1.1 安装
    [附源码]SSM计算机毕业设计闲置物品交易管理系统JAVA
    网络-笔记
  • 原文地址:https://www.cnblogs.com/kongbubihai/p/15898245.html