Spring Annotation Processing: How It Works--转

找的好辛苦呀

原文地址:https://dzone.com/articles/spring-annotation-processing-how-it-works

If you see an annotation, there must be some code somewhere to process it.

One of the things I emphasize when I teach Java classes is the fact that annotations are inert. In other words, they are just markers, potentially with some properties, but with no behavior of their own. So whenever you see an annotation on a piece of Java code, it means that there must be some other Java code somewhere that looks for that annotation and contains the real intelligence to do something useful with it.

Unfortunately, the issue with this line of reasoning is that it can be pretty difficult to identify exactly which piece of code is processing the annotation, particularly if it is inside a library. And code that processes annotations can be confusing, as it uses reflection and has to be written in a very generic way. So I thought it would be worthwhile to look at an example that‘s done well to see how it works.

I‘m going to walk through the InitDestroyAnnotationBeanPostProcessor from the Spring framework to show how it works. I‘ve chosen this one because it‘s relatively simple as these things go, it does something that‘s relatively easy to explain, and I happened to need it recently for some work I was doing.

Spring Bean Post Processing

First I would like to start with a little explanation of the purpose of Spring. One of the things the Spring framework does is "dependency injection". This changes the way we typically tie together modules within a piece of code. For example, let‘s say that we‘ve written some application logic that needs a connection to the database. Rather than coding into the application logic the specific class that provides that connection, we can just express it as a dependency, either in the constructor or a setter method:

class MyApplication {
    private DataConnection data;
    ...
    public void setData(DataConnection data) {
        this.data = data;
    }
    ...
}

Of course, we can do this dependency injection ourselves, and we might want to if we‘re writing a simple library and want to avoid adding a dependency to Spring. But if we‘re wiring together a complicated application, Spring can be very handy.

Since there‘s no magic, if we‘re going to let Spring inject these dependencies for us, there‘s going to be a tradeoff. Spring is going to have to "know" about the dependencies and about the classes and objects in our application. The way Spring prefers to handle this is by allowing Spring to do the instantiation of the objects; then it can keep track of them in a big data structure called the application context.

Post Processing and Initialization

And here‘s where InitDestroyBeanPostProcessor comes in. If Spring is going to handle instantiation, there are going to be cases where some "extra work" needs to be done after that instantiation is done, but before the application can start its real processing. One piece of "extra work" that needs doing is calling objects to tell them when they‘ve been fully set up, so they can do any extra initialization they need. This is especially important if we use "setter" injection, as above, where dependencies are injected by calling setXxx() methods, because those dependencies won‘t be available at the time the object‘s constructor is called. So Spring needs to allow users to specify the name of some method that should be called after the object has been initialized.

Spring has always supported using XML to define the objects that Spring should instantiate, and in that case there was an‘init-methodattribute that could be used to specify the method. Obviously in that case it still needed reflection to actually look up and call the method. But since annotations became available in Java 5, Spring has also supported tagging methods with annotations to identify them as objects that Spring should instantiate, to identify dependencies that should be injected, and to identify initialization and destruction methods that should be called.

That last item is handled by the InitDestroyBeanPostProcessor or one of its subclasses. A post processor is a special kind of object, instantiated by Spring, that implements a post processor interface. Because it implements this interface, Spring will call a method on it with each object Spring has instantiated, allowing it to modify or even replace that object. This is part of Spring‘s approach to a modular architecture, allowing easier extension of capability.

How It Works

It so happens that JSR-250 identified some "common" annotations, including a @PostConstruct annotation that is designed to tag initialization methods, and a @PreDestroy annotation for destruction methods. However, InitDestroyBeanPostProcessor is designed to work with any set of annotations, so it provides methods to identify the annotations:

    public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
        this.initAnnotationType = initAnnotationType;
    }
...
    public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
        this.destroyAnnotationType = destroyAnnotationType;
    }

Note that these are ordinary setter methods, so this object can itself be set up using Spring. In my case, I was using Spring‘sStaticApplicationContext, as I‘ve described previously.

Once Spring has instantiated the various objects and has injected all of the dependencies, it calls the postProcessBeforeInitializationmethod on all the post processors, for every object. This gives the post processor a chance to modify or replace the object before it‘s initialized. Because dependencies have been injected, this is the place where InitDestroyAnnotationBeanPostProcessor calls the initialization method.

    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        metadata.invokeInitMethods(bean, beanName);
    }

Since we‘re interested in how the code deals with annotations, we‘re interested in findLifecycleMetadata(), since that‘s where the class is inspected. That method checks a cache, which is used to avoid performing reflection more than necessary, since it can be expensive. If the class hasn‘t been inspected yet, the method buildLifecycleMetadata() is called. The meat of this method looks like:

ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
    @Override
    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
        if (initAnnotationType != null) {
            if (method.getAnnotation(initAnnotationType) != null) {
                LifecycleElement element = new LifecycleElement(method);
                currInitMethods.add(element);
            }
        }
        ...
    }
});

The ReflectionUtils is a handy class that simplifies using reflection. Amongst other things, it converts the numerous checked exceptions that go along with reflection into unchecked exceptions, making things easier. This particular method iterates over only local methods (i.e. not methods that are inherited) and calls the callback for each method.

After all of that setup, the part that checks for the annotation is pretty boring; it just calls a Java reflection method to check for the annotation and, if it‘s found, stores that method away as an initialization method.

Wrap Up

The fact that ultimately what‘s happening here is simple is really the point that I try to make when I teach reflection. It can be challenging to debug code that uses annotations to control behavior, because from the outside it‘s pretty opaque, so it‘s hard to envision what is happening (or not happening) and when. But at the end of the day, what‘s happening is really just Java code; it might not be immediately apparent where that code is, but it‘s there.

时间: 2024-10-08 01:33:24

Spring Annotation Processing: How It Works--转的相关文章

到谷歌下载最新的adt,找不到 Annotation processing

============问题描述============ 我从谷歌下载最新的ADT,右键项目的时候找不到 Annotation processing我记得以前的版本是有的啊. 这个是要做什么设置还是装什么插件才有的的? ============解决方案1============  这个东西干嘛用的? ============解决方案2============ 可能新版本已经不用着玩意了. ============解决方案3============ 和jdk版本有关系吗 ? ===========

spring Annotation 组件注入

spring 注解的分类 启动spring自己主动扫描功能 <context:component-scan/> [email protected]: 它用于将数据訪问层 (DAO 层 ) 的类标识为 Spring Bean.详细仅仅需将该注解标注在 DAO 类上就可以. 为什么 @Repository 仅仅能标注在 DAO 类上呢? 这是由于该注解的作用不仅仅是将类识别为 Bean,同一时候它还能将所标注的类中抛出的数据訪问异常封装为 Spring 的数据訪问异常类型. Spring 本身提供

【框架】Error:java: Annotation processing is not supported for module cycles.循环依赖

Error:java: Annotation processing is not supported for module cycles. Please ensure that all modules from cycle [hdd_smart_shiptracker_web,hdd_smart_shiptracker_service_imp] are excluded from annotation processing

Android点滴---ADT 23.0.2找不到Annotation Processing选项,解决方法

第一次 使用  AndroidAnnotations框架时, 遇到 java.lang.ClassNotFoundException, 发现是因为没有配置插入式注解使用的Jar包. 按照网上的教程配置时发现,在JavaCompiler下根本找不到 Annotation Processing 选项 然后 Google 后发现是缺少了 Eclipse的 JDT 插件(Java development tools), 安装上这个插件就OK了! 安装步骤: 1.  Help -->  Install N

Spring - Annotation Based Configuration

Starting from Spring 2.5 it became possible to configure the dependency injection using annotations. So instead of using XML to describe a bean wiring, you can move the bean configuration into the component class itself by using annotations on the re

Spring Annotation(@resource、@component)

1 package com.bxw.dao.impl; 2 3 import org.springframework.stereotype.Component; 4 5 import com.bxw.dao.UserDao; 6 import com.bxw.po.User; 7 8 @Component("userDao") 9 public class UserDaoImpl implements UserDao { 10 public void save(User u){ 11

spring annotation功能备注

  @Autowired @Autowired 注释可以在 setter 方法中被用于自动连接 bean.以type方式进行匹配. 一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接.   映射方式1    对变量使用@Autowired,在xml中注入其他bean,可以在注入对象中省略配置类的成员变量 映射方式2    对set方法使用@Autowired,在xml中注入其他bean,可以在注入对象中省略配

Spring Annotation注解进行aop的学习

使用Maven管理项目,pom文件为: 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&

hibernate spring annotation setup

First step setup for the pom.xml with hibernate dependency , hibernate dependency need to before the struts2,because the javassist dependency <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <