Spring是如何处理注解的
如果你看到了注解,那么一定有什么代码在什么地方处理了它.
Alan Hohn
我教Java课程时强调的一点是注解是惰性的。换句话说,它们只是标记,可能具有某些属性,但没有自己的行为。因此,每当你在一段Java代码上看到一个注解时,就意味着必须有一些其他的Java代码来寻找那个注解并包含真正的智能来做一些有用的东西。
不幸的是,这种推理的问题在于,确切地确定哪一段代码正在处理注解是非常困难的,特别是如果它在库中。处理注解的代码可能会令人困惑,因为它使用反射并且必须以非常抽象的方式编写。所以我认为值得看看一个做得很好的例子来看看它是如何运行的。
我们详细研究一下 Spring 框架中的 InitDestroyAnnotationBeanPostProcessor 类是如何工作的。选择这个,因为它相对简单,只做了一些相对容易解释的事情, 碰巧和我手头的工作相关。
Spring Bean 的后处理
首先,我想首先解释一下 Spring 的用途。Spring 框架所做的一件事就是“依赖注入”。这改变了我们以往用代码将模块串在一起的方式。例如,假设我们编写了一些需要连接数据库的应用程序逻辑, 但并想将提供该连接的特定硬类编码到应用程序逻辑中,我们可以在构造函数或setter方法中将其表示为依赖项:
class MyApplication {
private DataConnection data;
...
public void setData(DataConnection data) {
this.data = data;
}
...
}
当然,如果想的话, 我们可以自己编写一个简单的库完成这种依赖注入,从而避免添加对 Spring 的依赖项。但是如果我们在编写一个复杂的应用程序, 想将各模块连接在一起,那么Spring可以非常方便。
既然没有什么神秘的,如果我们要让 Spring 为我们注入这些依赖,那么就会有一个权衡。Spring 需要“知道”依赖关系以及应用程序中的类和对象。Spring 处理这个问题的方法多是由 Spring 框架对对象进行实例化; 从而可以在称为"应用程序上下文"的大数据结构中跟踪管理这此对象。
后处理和初始化
而且这里是 InitDestroyBeanPostProcessor 进入的地方 。如果 Spring 要处理实例化,那么在对象实例化完成之后,但是在应用程序开始真正的运行之前,需要进行一些“额外工作”。需要做的一件“额外工作”就是调用对象来告诉他们什么时候完全设置好,这样他们就可以进行任何需要的额外初始化。如果我们使用“setter”注入,如上所述,便通过调用setXxx() 方法注入依赖项,这一点尤其重要,因为在调用对象的构造函数时这些依赖项并不可用。所以 Spring 需要允许用户指定在初始化对象后才应该调用的某个方法的名称。
Spring 一直支持使用XML配置文件来定义由 Spring 来实例化的对象,在这种情况下,有一个 'init-method' 属性可以用来指定初始化的方法。显然,在这种情况下,它仍然需要反射来实际查找并调用该方法。自Java 5起, 增加了注解,所以Spring 也支持带注解的标记方法,将它们标识为Spring应该实例化的对象,识别需要注入的依赖项,以及识别应该调用的初始化和销毁方法。
最后一项 InitDestroyBeanPostProcessor 由其子类或其中一个子类处理。后处理器是一种特殊的对象,由Spring实例化,实现后处理器接口。因为它实现了这个接口,所以Spring会在每个Spring实例化的对象上调用一个方法,允许它修改甚至替换该对象。这是Spring采用模块化架构方法的一部分,可以更轻松地扩展功能。
这是怎么运作的?
事实上, JSR-250 确定了一些“常见”注解,包括 @PostConstruct, 用于标记初始化方法,@PreDestroy 注解, 用于注解销毁方法的。不同的是,InitDestroyBeanPostProcessor 被设计成可以处理任何注解集,因此它提供了识别注解的方法:
public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
this.initAnnotationType = initAnnotationType;
}
...
public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
this.destroyAnnotationType = destroyAnnotationType;
}
请注意,这些是普通的 setter 方法,因此这个对象本身可以使用 Spring 进行设置。就我而言,我使用Spring 的 StaticApplicationContext。
一旦 Spring 实例化了各种对象并注入了所有依赖项,它就会在所有后处理器上为每个对象调用 postProcessBeforeInitialization 方法 。这使后处理器有机会在初始化之前修改或替换对象。因为已经注入了依赖项,所以这是 InitDestroyAnnotationBeanPostProcessor 调用初始化方法的地方。
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
由于我们对代码如何处理注解感兴趣,我们感兴趣 findLifecycleMetadata() 方法,因为这是对类进行检查的地方。该方法检查缓存,该缓存用于避免执行超过必要的反射,因为它可能很昂贵。如果尚未检查该类,则调用 buildLifecycleMetadata() 方法。该方法的内容如下:
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);
}
}
...
}
});
这里 ReflectionUtils 是一个方便的类,简化了反射的使用。除此之外,它还将经过反射的众多已检查异常转换为未经检查的异常(?),从而使事情变得更容易。此特定方法仅迭代本地方法(即不是继承的方法),并为每个方法调用回调。
完成所有设置之后,检查注解的部分非常无聊; 它只是调用Java反射方法来检查注解,如果找到它,则将该方法存储为初始化方法。
总结
事实上,这里最终发生的事情很简单,这就是我在教反射时所要做的事情。调试使用注解来控制行为的代码可能具有挑战性,因为从外部来看它非常不透明,所以很难想象发生了什么(或者没有发生)和什么时候发生。但最终,正在发生的事情只是Java代码; 它可能不会立即显现出代码的位置,但它就在那里。
如果你依然在编程的世界里迷茫,不知道自己的未来规划,可以加入JAVA架构学习交流群:614478470 里面可以与大神一起交流并走出迷茫。进群免费领取学习资料,看看前辈们是如何在编程的世界里傲然前行!群里不停更新最新的教程和学习方法(进群送JAVA架构视频资料),有想学习JAVA的,或是转行,还有工作中想提升自己能力的,正在学习的小伙伴欢迎加入学习
点击:加入
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
怎么做一名好的JAVA架构师?
怎么做一名好的JAVA架构师? 好的架构师不是叫出来的,是做出来的,不是有架构师的title,他就是架构师啦。 那么什么样的架构师,才是好的架构师呢? 1.使用新的技术固然重要,但满足需求更重要 在项目中,但我们遇到一些问题的时候,我们时常会使用新的技术,新的方法论,诸如此类,这些解决问题的根本动机在于,我们其实是想丰富下我们架构师自己的简历,但实际上,这些新的技术可能并不是问题的最佳解决方案。 但是,我想,我们职业生涯中,最好的效果是,有一长串的客户,他们很愿意去推荐你,因为你在和他们做项目的过程中,做了正确的事情,得到了客户的认可和满意。 当然,作为一名架构师,保持最新技术和趋势的学习,是我们的天职。但是,我们也要牢记,使用对的技术是很重要的。是的,很难去拒绝使用那些新的,酷的技术,虽然他们并不是很适合你当前的项目。 使用对的技术和方案,团队会高兴,客户会满意,压力就会很小,这样就会给你更多的时间来钻研目前已有的成熟技术,或者是学习新的技术。 2.简化基本的复杂度,减少额外的复杂度 什么是基本的复杂度呢,基本的复杂度,就是说,在我们解决问题的时候本来就有的一些困难。 比方说,设计一...
- 下一篇
IPv6时代已来:双十一中的IPv6大规模应用实践
2018天猫双11以2135亿的成交额创下了新的记录,新纪录的背后大家可能没有意识到,你已经悄然成为首批互联网IPv6用户。今年天猫双11期间全面支持IPv6,这是IPv6在国内互联网领域首次大规模实践,覆盖了网购、视频直播、位置服务等众多大流量、高并发场景。 就和每个建筑都有自己的地址一样,在网络上,每个联网的设备会有自己的地址:IP,没有地址就无法联网。上世纪80年代初的时候人们设立了IPv4协议来分配地址,但当时无法预料现在有这么多设备要联网,到现在这一地址库已经接近枯竭,每个IP地址的价格也是水涨船高。 随着5G、IoT技术的不断成熟,带来了更大一波的新增入网设备。IPv6作为新的协议对网络地址大大地做了扩容,它多到可以让地球上的每一粒沙子分配到一个独立IP地址,不仅如此,IPv6还提升了转发效率以及通信安全性。 IPv6的
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- 设置Eclipse缩进为4个空格,增强代码规范
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装