Spring Annotation(五)

本文介绍如何在Spring框架中使用JSR-250规范定义的注解,包括@Resource、@PostConstruct及@PreDestroy等。通过示例详细解释了这些注解的功能和使用方法。

Spring Annotation(五)

 

使用 JSR-250 的注释

Spring 不但支持自己定义的 @Autowired 的注释,还支持几个由 JSR-250 规范定义的注释,它们分别是 @Resource@PostConstruct 以及 @PreDestroy

@Resource

@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面 @Resource 默认按 byName 自动注入罢了。@Resource 有两个属性是比较重要的,分别是 name 和 type,Spring 将 @Resource 注释的 name 属性解析为 Bean 的名字,而 type 属性则解析为 Bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。

Resource 注释类位于 Spring 发布包的 lib/j2ee/common-annotations.jar 类包中,因此在使用之前必须将其加入到项目的类库中。来看一个使用 @Resource 的例子:


清单 16. 使用 @Resource 注释的 Boss.java

                
package com.baobaotao;

import javax.annotation.Resource;

public class Boss {
    // 自动注入类型为 Car 的 Bean
    @Resource
    private Car car;

    // 自动注入 bean 名称为 office 的 Bean
    @Resource(name = "office")
    private Office office;
}

 

一般情况下,我们无需使用类似于 @Resource(type=Car.class) 的注释方式,因为 Bean 的类型信息可以通过 Java 反射从代码中获取。

要让 JSR-250 的注释生效,除了在 Bean 类中标注这些注释外,还需要在 Spring 容器中注册一个负责处理这些注释的 BeanPostProcessor

<bean 
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

 

CommonAnnotationBeanPostProcessor 实现了 BeanPostProcessor 接口,它负责扫描使用了 JSR-250 注释的 Bean,并对它们进行相应的操作。

@PostConstruct 和 @PreDestroy

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。关于 Spring 的生命周期,笔者在《精通 Spring 2.x—企业应用开发精解》第 3 章进行了详细的描述,有兴趣的读者可以查阅。

JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。


清单 17. 使用 @PostConstruct 和 @PreDestroy 注释的 Boss.java

                
package com.baobaotao;

import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class Boss {
    @Resource
    private Car car;

    @Resource(name = "office")
    private Office office;

    @PostConstruct
    public void postConstruct1(){
        System.out.println("postConstruct1");
    }

    @PreDestroy
    public void preDestroy1(){
        System.out.println("preDestroy1"); 
    }
    …
}

 

您只需要在方法前标注 @PostConstruct@PreDestroy,这些方法就会在 Bean 初始化后或销毁之前被 Spring 容器执行了。

我们知道,不管是通过实现 InitializingBean/DisposableBean 接口,还是通过 <bean> 元素的 init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct@PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct@PreDestroy 注释的方法都会在初始化 / 销毁时被执行。

通过以下的测试代码,您将可以看到 Bean 的初始化 / 销毁方法是如何被执行的:


清单 18. 测试类代码

                
package com.baobaotao;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ClassPathXmlApplicationContext ctx = 
            new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
        ctx.destroy();// 关闭 Spring 容器,以触发 Bean 销毁方法的执行
    }
}

 

这时,您将看到标注了 @PostConstructpostConstruct1() 方法将在 Spring 容器启动时,创建 Boss Bean 的时候被触发执行,而标注了 @PreDestroy 注释的 preDestroy1() 方法将在 Spring 容器关闭前销毁 Boss Bean 的时候被触发执行。

### 如何在Spring框架中获取AnnotationMetadata 在Spring框架中,`AnnotationMetadata`接口用于描述类级别的元数据。这允许开发人员访问目标类型的注解信息而不必实例化该类型。对于基于Java配置或扫描组件的应用程序来说非常有用。 为了获得某个特定bean定义的`AnnotationMetadata`对象,可以利用`StandardReflectionMetadataReaderFactory`来读取并处理这些信息[^1]: ```java import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AnnotationTypeFilter; public class MetadataExample { private final MetadataReaderFactory metadataReaderFactory; public void readAnnotations(String className) throws IOException { MetadataReader reader = metadataReaderFactory.getMetadataReader(className); AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata(); // 判断是否存在指定注解 boolean hasRestcontroller = annotationMetadata.hasAnnotation(RestController.class.getName()); System.out.println("Has RestController: " + hasRestcontroller); // 获取所有注解名称 Set<String> annotations = annotationMetadata.getAnnotationTypes(); for (String anno : annotations) { System.out.println(anno); } } } ``` 此外,在某些情况下可能需要通过实现自定义逻辑来间接取得此类信息。例如当使用AOP代理时,可以通过拦截器或者后处理器的方式捕获到被增强的目标对象及其关联的元数据[^4]。 如果是在编写测试案例或是工具性质的功能模块,则可以直接借助于反射API配合Spring提供的辅助方法来进行操作。比如下面这段代码展示了如何直接从已知class对象提取其上的注解详情: ```java import org.springframework.core.annotation.AnnotatedElementUtils; public static Map<String, Object> getClassLevelAnnotations(Class<?> clazz){ MultiValueMap<String, Object> attributes = AnnotatedElementUtils.getAllAnnotationAttributes(clazz, true, false); return CollectionUtils.isEmpty(attributes)? Collections.emptyMap():attributes.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get(0))); } ``` 以上两种方式分别适用于不同场景下的需求,前者更侧重于编译期静态分析,后者则适合运行时期动态查询。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值