找的好辛苦呀

原文地址: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.

转载于:https://www.cnblogs.com/davidwang456/p/5570127.html

Spring Annotation Processing: How It Works--转相关推荐

  1. ANNOTATION PROCESSING 101 by Hannes Dorfmann — 10 Jan 2015

    原文地址:http://hannesdorfmann.com/annotation-processing/annotationprocessing101 In this blog entry I wo ...

  2. Error:java: Annotation processing is not supported for module cycles. Please ensure that all modules

    Error:java: Annotation processing is not supported for module cycles. Please ensure that all modules ...

  3. Spring Annotation(@Autowire、@Qualifier)

    Spring Annotation(@Autowire.@Qualifier) 1 <?xml version="1.0" encoding="UTF-8" ...

  4. DWR的学习文档(Hello World,类型转换,Spring,Annotation)

    來源:http://www.iteye.com/topic/40313 DWR是作为远程调用的ajax框架,将服务端的java类,方法和浏览器的javascript的类,方法对应起来.现在官方最新的版 ...

  5. 记录 Annotation processing is not supported for module cycles.

    报错:Error:java: Annotation processing is not supported for module cycles. Please ensure that all modu ...

  6. 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 ...

  7. Spring Annotation知识梳理

    2019独角兽企业重金招聘Python工程师标准>>> Spring annotation:(目的:减少applicationContext.xml文件配置) 使用注解时需要添加扫描 ...

  8. 【idea】Error:java: Annotation processing is not supported for module cycles. Please ensure that all..

    出现这个问题就是出现了模块循环依赖的问题,需要好好看下各模块的作用,判断下目前代码的架构是否合理,并解除互相依赖的模块的依赖 判断哪两个模块循环依赖的方法如图 如果出现循环依赖,模块会是红色的 参考文 ...

  9. Error:java: Annotation processing is not supported for module cycles.项目启动报错 异常解决

    项目在启动时报如下Error错误的原因是:Maven项目中各模块module之间产生了循环依赖,因此在启动运行时就会产生循环依赖的问题,此时需要查看报错信息中显示对应模块的pom.xml,排查删除其中 ...

最新文章

  1. php t double arrow,关于php:php – 语法错误,意外T_DOUBLE_ARROW
  2. 别了比特币,BCH将领导加密货币!
  3. string 大小写转换
  4. 【新书推荐】Silverlight 4教程书籍推荐
  5. Git:常用的命令(不断迭代完善)
  6. mpg文件怎么转换成mp4
  7. Buddy/Sponsor培训•信任的构建
  8. mysql 错误代码1130_mysql出现错误码1130怎么办
  9. android侧滑菜单 动画,Android 打造完美的侧滑菜单/侧滑View控件
  10. PyQt5 与 PySide2 所有事件大集合,一段代码包括键盘、鼠标和窗口所有事件
  11. 下午的一杯清茶,慢了下来
  12. OpenCat 简评
  13. 织梦制作二级全国分站教程,多城市分站插件代码调用
  14. Linux常用命令——最详细!!!!
  15. PCDMIS 零件坐标系创建技巧 4
  16. 遭到强烈反对后,Apple推迟了扫描设备以查找虐待儿童图像的计划
  17. (4道全A)拼多多2021届提前批-服务端研发工程师笔试
  18. 如何给WordPress博客网站换个漂亮的字体
  19. MAPGIS二次开发常见问题汇总
  20. Online Object Tracking : A Benchmark

热门文章

  1. 如何使html中的din居中,HTML+CSS--position大法好
  2. php制作表格生成器,php表格生成图片.doc
  3. php修改文件访问目录为 .htaccess_借github上韩国师傅的一个源码实例再次理解.htaccess的功效...
  4. 华为架构服务器销售,FusionServer Pro E9000融合架构刀片服务器
  5. java语言修饰符$_Java语言中的修饰符
  6. oracle没有groupby,oraclegroupby之后还可以groupby么
  7. 将派生类指针赋值给基类的指针
  8. C 语言链表其他实现
  9. VALID SAME
  10. pyspark.sql.DataFrame与pandas.DataFrame之间的相互转换