当前位置:首页 > java技术文章 > Spring如何处理注解的深入理解

Spring处理注解的方法

  • 发布时间:
  • 作者:码农之家原创
  • 点击:143

这篇文章主要知识点是关于Spring、处理注解、的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

深入理解Spring Cloud与微服务构建
  • 类型:微服务大小:173.49 MB格式:PDF作者:方志朋
立即下载

Spring如何处理注解的深入理解

前言

传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:

1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。

2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。
为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

如果你看到了注解,那么一定有什么代码在什么地方处理了它.

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的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

推荐内容

idea2020注册激活码(激活到2100年)

实例分析Java实现的zip压缩及解压缩工具类

python3 pandas 如何读取MySQL数据和插入

ThinkPHP3.2.3框架如何实现分页功能

深入理解JS函数stack size计算方法

展开 +

收起 -

Spring 相关电子书
学习笔记
网友NO.180804

Spring @Bean vs @Service注解区别

今天跟同事讨论了一下在Spring Boot中,是使用@Configuration和@Bean的组合来创建Bean还是直接使用 @Service等注解放在类上的方式。笔者倾向于使用第一种,即@Configuration和@Bean的组合。 先来看一个例子,目标是创建SearchService的一个Bean。 直接使用@Service的方式: // SearchService.javapackage li.koly.search;import java.util.List;public interface SearchService { ListObject search(String q);}// ElasticSearchServiceImpl.javapackage li.koly.search;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.List;@ServiceComponentpublic class ElasticSearchServiceImpl implements SearchService { @Override public ListObject search(String q) { return Arrays.asList("hello", q); }}// Application.javapackage li.koly.search;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;im……

网友NO.833604

Spring IOC基于注解启动示例详析

Spring 基于注解启动 主要有两个Class实现注解启动 AnnotationConfigApplicationContext AnnotationConfigWebApplicationContext 我们以AnnotationConfigApplicationContext 为研究对象 AnnotationConfigApplicationContext.png 引入Spring 最小依赖 dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency 编写器启动代码 public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(BeanConfig.class); applicationContext.refresh(); Date date = applicationContext.getBean("date",Date.class); System.out.println(date); } AnnotationConfigApplicationContext 构造函数 public AnnotationConfigApplicationContext() { //负责注册Class ,读取器 this.reader = new AnnotatedBeanDefinitionReader(this); //负责扫描指定类路径下的Class,注册bean this.scanner = new ClassPathBeanDefinitionScanner……

网友NO.607535

关于Spring注解@Async引发其他注解失效的解决

概述 在前面一篇文章中,介绍,在一个Bean中注入自己,如果有@Async和@Transaction,如果使用@Autowire注入自身,会报循环依赖,如果使用BeanFactoryAware注入自己,会使得@Transaction失效。 例如: @Servicepublic class MyService implements BeanFactoryAware{ private MyService self; //事务注解无效 @Transactional public void notWork() { ... } @Async public Future async(){ ... } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { self= beanFactory.getBean(MyService.class); }} 当时只是简单提了一下,这篇文章就是来介绍为什么会失效。 一般情况 造成上面的情况需要满足以下条件: 有@Async和其他类似@Transaction注解 自己类在BeanFactoryAware中,通过BeanFactory获取自己 造成的结果:除@Async外的注解生效,其他的都不生效,如下图 而正常代理的应该是下图: 原因 首先想到的是@Async注解的处理方式可能和其他……

网友NO.566655

springmvc注解配置实现解析

springmvc大大减少了对xml的配置,减少了配置量,以及可以在一个controller类中进行多个请求配置 一、springmvc配置 context:component-scan 开启包扫描,对指定的包进行注解扫描 mvc:annotation-driven开启注解功能 二、controller配置 在类上加上@Controller 在指定的方法上@RequestMapping("/t请求名") 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。 ……

<
1
>

电子书 编程教程 文档 软件 源码 视频

Copyright 2018-2020 xz577.com 码农之家

本站所有电子书资源不再提供下载地址,只分享来路

免责声明:网站所有作品均由会员网上搜集共同更新,仅供读者预览及学习交流使用,下载后请24小时内删除

版权投诉 / 书籍推广 / 赞助:QQ:520161757