如何写好一个Spring组件的实现步骤

本文详细的介绍了Spring组件的实现步骤,分享给大家,具体如下:

背景

Spring 框架提供了许多接口,可以使用这些接口来定制化 bean ,而非简单的 getter/setter 或者构造器注入。细翻 Spring Cloud Netflix、Spring Cloud Alibaba 等这些构建在 Spring Framework 的成熟框架源码,你会发现大量的扩展 bean 例如

Eureka 健康检查

package org.springframework.cloud.netflix.eureka;

public class EurekaHealthCheckHandler implements InitializingBean {}

Seata Feign 配置

package com.alibaba.cloud.seata.feign;

public class SeataContextBeanPostProcessor implements BeanPostProcessor {}

代码示例

DemoBean

@Slf4j
public class DemoBean implements InitializingBean {

 public DemoBean() {
  log.info("--> instantiate ");
 }

 @PostConstruct
 public void postConstruct() {
  log.info("--> @PostConstruct ");
 }

 @Override
 public void afterPropertiesSet() throws Exception {
  log.info("--> InitializingBean.afterPropertiesSet ");
 }

 public void initMethod() {
  log.info("--> custom initMehotd");
 }
}

DemoBeanPostProcessor

@Configuration
public class DemoBeanPostProcessor implements BeanPostProcessor {
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  if ("demoBean".equals(beanName)){
   log.info("--> BeanPostProcessor.postProcessBeforeInitialization ");
  }
  return bean;
 }

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  if ("demoBean".equals(beanName)){
   log.info("--> BeanPostProcessor.postProcessAfterInitialization ");
  }
  return bean;
 }
}

DemoConfig

@Configuration
public class DemoConfig {

 @Bean(initMethod = "initMethod")
 public DemoBean demoBean() {
  return new DemoBean();
 }
}

运行输出日志

整个 bean 的创建过程日志输出如下和文首图片横线以上 bean 创建周期一致

DemoBean : --> instantiate
DemoBeanPostProcessor: --> BeanPostProcessor.postProcessBeforeInitialization
DemoBean : --> @PostConstruct
DemoBean : --> InitializingBean.afterPropertiesSet
DemoBean : --> custom initMehotd
DemoBeanPostProcessor: --> BeanPostProcessor.postProcessAfterInitialization

执行过程核心源码

AbstractAutowireCapableBeanFactory.initializeBean

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

 // 执行BeanPostProcessor.postProcessBeforeInitialization
 Object wrappedBean = wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  ...
  // 执行用户自定义初始化and JSR 250 定义的方法
 invokeInitMethods(beanName, wrappedBean, mbd);
  ...
 // 执行执行BeanPostProcessor.postProcessAfterInitialization
 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

 return wrappedBean;
}

下文就详细说明一下每个 bean 的扩展周期的最佳使用场景BeanPostProcessor

BeanPostProcessor 是一个可以自定义实现回调方法接口,来实现自己的实例化逻辑、依赖解决逻辑等,如果想要在 Spring 完成对象实例化、配置、初始化之后实现自己的业务逻辑,可以通过扩展实现一个或多个 BeanPostProcessor 处理。

多用于适配器模式,可以在实现同一接口 bean 创建前后进行包装转换

// seata 上下文转换,将其他类型 wrap 成 SeataFeignContext
public class SeataContextBeanPostProcessor implements BeanPostProcessor {

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName){
  if (bean instanceof FeignContext && !(bean instanceof SeataFeignContext)) {
   return new SeataFeignContext(getSeataFeignObjectWrapper(),
     (FeignContext) bean);
  }
  return bean;
 }
}

自定义 注解查找扩展

net.dreamlu.mica.redisson.stream.RStreamListenerDetector 查找自定义 @RStreamListener 实现 基于 Redisson 的 pub/sub

public class RStreamListenerDetector implements BeanPostProcessor {

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  Class<?> userClass = ClassUtils.getUserClass(bean);
  ReflectionUtils.doWithMethods(userClass, method -> {
   RStreamListener listener = AnnotationUtils.findAnnotation(method, RStreamListener.class);
   .... do something
  }, ReflectionUtils.USER_DECLARED_METHODS);
  return bean;
 }
}

PostConstruct

JavaEE5 引入了@PostConstruct 作用于 Servlet 生命周期的注解,实现 Bean 初始化之前的自定义操作。

  • 只能有一个非静态方法使用此注解
  • 被注解的方法不能有返回值和方法参数
  • 被注解的方法不得抛出异常

这里需要注意的 这个注解不是 Spring 定义的,而是属于 JavaEE JSR-250 规范定义的注解,当你在使用 Java11 的时候要手动引入相关 jar(因为 Java11 移除了)

<dependency>
 <groupId>javax.annotation</groupId>
 <artifactId>javax.annotation-api</artifactId>
</dependency>

使用场景: 在之前的版本,我们可以在启动后通过 @PostConstruct 注解的方法执行初始化数据。但由于 Java 高版本已经移除相关 API ,我们不推荐使用此 注解,可以通过 Spring 相关 Event 回调事件处理

@PostConstruct 注解的方法在项目启动的时候执行这个方法,也可以理解为在 spring 容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的。

InitializingBean

InitializingBean 接口方法会在 容器初始化(getter/setter/构造器)完成 bean 的属性注入后执行。

应用场景: 动态修改容器注入的 Bean 参数

正常用户配置参数注入到 bean

security:
 oauth2:
  ignore-urls:
  - '/ws/**'

@ConfigurationProperties(prefix = "security.oauth2")
public class PermitAllUrlProperties {
 @Getter
 @Setter
 private List<String> ignoreUrls = new ArrayList<>();
}

我们发现此时用户配置并不完整,还有一些通用不需要用户维护,可通过实现 InitializingBean 接口回调扩展

@ConfigurationProperties(prefix = "security.oauth2.ignore")
public class PermitAllUrlProperties implements InitializingBean {

 @Getter
 @Setter
 private List<String> urls = new ArrayList<>();

 @Override
 public void afterPropertiesSet() {
  urls.add("/common/*");
 }
}

initMethod

上文 @PostConstruct 已经不推荐大家使用,可以使用 Bean(initMethod = 'initMehotd') 替代,相关的限制如上。

@Bean(initMethod = "initMethod")
public DemoBean demoBean() {
 return new DemoBean();
}

public void initMethod() {
 log.info("--> custom initMehotd");
}

总结

参考

https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#beans-factory-nature
mica : https://github.com/lets-mica/mica

pig: https://github.com/lltx/pig

到此这篇关于如何写好一个Spring组件的实现步骤的文章就介绍到这了,更多相关Spring 组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring高阶用法之自定义业务对象组件化

    若干年前在使用SpringMVC的时候,发现springMVC可以把HttpSession,HttpRequest组件化注入: @Autowired HttpSession session; @Autowired HttpRequest httpRequest; 于是花了30分钟追踪了相关的源代码彻底摸清其原理,并且决定将用户(User/Principle)也组件化(尽管当时工作处于极其忙碌的状态,也忍不住去研究). 方法如下: 1. 定义IPrincipal(IUser)接口 interfac

  • 分享Spring的下载组件

    本文为大家分享了Spring4的下载组件,供大家参考,具体内容如下 package com.hnust.common.controller; import org.apache.commons.io.FileUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import

  • Spring组件开发模式支持SPEL表达式

    本文是一个 Spring 扩展支持 SPEL 的简单模式,方便第三方通过 Spring 提供额外功能. 简化版方式 这种方式可以在任何能获取ApplicationContext 的地方使用.还可以提取一个方法处理动态 SPEL 表达式. import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.beans.

  • Spring组件自动扫描详解及实例代码

    Spring组件自动扫描详解及实例代码 问题描述 一个系统往往有成千上万的组件,如果需要手动将所有组件都纳入spring容器中管理,是一个浩大的工程. 解决方案 Spring 提供组件扫描(component scanning)功能.它能从classpath里自动扫描.侦测和实例化具有特定注解的组件.基本的注解是@Component,它标识一个受Spring管理的组件.其他特定的注解有@Repository.@Service和@Controller,它们分别标识了持久层.服务处和表现层的组件.

  • 浅谈Spring装配Bean之组件扫描和自动装配

    Spring从两个角度来实现自动化装配: 组件扫描:Spring会自动发现应用上下文中所创建的bean. 自动装配:Spring自动满足bean之间的依赖. 案例:音响系统的组件.首先为CD创建CompactDisc接口及实现类,Spring会发现它并将其创建为一个bean.然后,会创建一个CDPlayer类,让Spring发现它,并将CompactDisc bean注入进来. 创建CompactDisc接口: package soundsystem; public interface Comp

  • 使用spring框架中的组件发送邮件功能说明

    Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途不仅仅限于服务器端的开发.从简单性.可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益. 首先进入自己的QQ邮箱,在设置中修改账户信息 然后来至底部 点击开启,再用手机发送对应信息到指定号码,然后点击我已发送 获取授权码 注意提示: 到这里,相信你已经开通了SMTP服务,这样就可以在java code发送邮件了 接下来的是S

  • 如何写好一个Spring组件的实现步骤

    本文详细的介绍了Spring组件的实现步骤,分享给大家,具体如下: 背景 Spring 框架提供了许多接口,可以使用这些接口来定制化 bean ,而非简单的 getter/setter 或者构造器注入.细翻 Spring Cloud Netflix.Spring Cloud Alibaba 等这些构建在 Spring Framework 的成熟框架源码,你会发现大量的扩展 bean 例如 Eureka 健康检查 package org.springframework.cloud.netflix.

  • 如何写好一个vue组件,老夫的一年经验全在这了(推荐)

    一个适用性良好的组件,一种是可配置项很多,另一种就是容易覆写,从而扩展功能 Vue 组件的 API 来自三部分--prop.事件和插槽: prop 允许外部环境传递数据给组件 event 允许从组件内触发外部环境的副作用 slot 允许外部环境将额外的内容组合在组件中 prop 组件具有自身状态,当没有相关 porps 传入时,使用自身状态完成渲染和交互逻辑:当该组件被调用时,如果有相关 props 传入,那么将会交出控制权,由父组件控制其行为 仅一个值传入组件 如果该组件设计上支持双向绑定,可

  • 手把手教你搭建第一个Spring Batch项目的步骤

    一.概述 Spring Batch是一个轻量级,全面的批处理框架. 一个典型的批处理过程可能是: 从数据库,文件或队列中读取大量记录. 以某种方式处理数据. 以修改之后的形式写回数据 Spring Batch 应用架构图: 一个Batch(批处理)过程由一个Job(作业)组成.这个实体封装了整个批处理过程. 一个Job(作业)可以由一个或多个Step(步骤)组成.在大多数情况下,一个步骤将读取数据(通过ItemReader),处理数据(使用ItemProcessor),然后写入数据(通过Item

  • 如何给element添加一个抽屉组件的方法步骤

    近来因为业务需要,对比iview和element库,发现element确实要比实习期间使用的iview强大点,尤其文档更为友好,但是iview的组件功能更多一点,比如分割线和抽屉组件 今天特意手写一个抽屉组件,方便自己使用element库,写好的组件我已经放在我的githup了, 点这里 一.实践 1.分析 一个抽屉组件的z-index必定是在当前页面之上的,在抽屉主体之外的区域还会有一层半透明的遮罩层,知道这些就很容易了 // drawer.vue <template> <div cl

  • 从零开始在NPM上发布一个Vue组件的方法步骤

    TL;DR 本文细致讲解了在NPM上发布一个Vue组件的全过程,包括创建项目.编写组件.打包和发布四个环节. 创建项目 这里我们直接利用@vue/cli来生成项目.如果没有安装@vue/cli的话,可以使用以下方法进行安装: # 如果喜欢npm npm i -g @vue/cli # 如果喜欢yarn yarn global add @vue/cli 此外,如果安装了npx(高版本的nodejs发行版会自带这一工具)的话,还可以很方便地通过npx vue这一方式实现免安装使用. 接下来就可以创建

  • vue的一个分页组件的示例代码

    分页组件在项目中经常要用到之前一直都是在网上找些jq的控件来用(逃..),最近几个项目用上vue了项目又刚好需要一个分页的功能.具体如下: 文件page.vue为一个pc端的分页组件,基础的分页功能都有,基本的思路是,页面是用数据来展示的,那就直接操作相关数据来改变视图 Getting started import Page from './page.vue' 从目录引入该文件,在父组件注册使用 复制代码 代码如下: <page :total='total' :current-index="

  • 利用Angular7开发一个Radio组件的全过程

    一.准备工作 Angular7(以下简称ng7),已经跟之前版本大有不同.新建工程后,可方便创建library(简称lib),lib是什么呢?就是一个npm包的源码包.npm作为强大的包管理器,已经成为很多FEer分享智慧成果的法器.本文主要介绍本人写的一个radio组件. 二.开发组件radio过程 1.使用ng cli,新建工程,创建lib // 安装ng cli npm install -g @angular/cli // 新建工程 ng new ng-project // 进入ng-pr

  • 如何使用Vue3.2+Vite2.7从0快速打造一个UI组件库

    目录 1. 前言 2. 使用Vite搭建官网 2.1 创建项目 2.1.1. 全局安装vite(这里我装的时候是2.7.2) 2.1.2. 构建一个vue模板(项目名可以改成自己的名字) 2.1.3. 装好之后按照提示逐步执行命令 2.2 基本完成官网的搭建 2.2.1. 下载vue-router 2.2.2. 创建home首页与doc文档页 以及顶部导航栏 2.2.3. 配置路由 3. 封装一个Button组件 4. 封装Markdown组件介绍文档 4.1. 下载 4.2. main.ts中

  • Vue+ElementUI 封装简易PaginationSelect组件的详细步骤

    在实际开发工作中,经常会碰到当select下拉数据过需要做分页的情况这里简单介绍封装的一个Pagination-Select组件几个步骤封装的比较简易,可以根据自己的项目进行改动 /components/Pagination-Select/index.vue <template> <div id="PaginationSelect"> <el-select v-model="value" :placeholder="selec

  • 如何手写一个Spring Boot Starter

    何为 Starter ? 想必大家都使用过 SpringBoot,在 SpringBoot 项目中,使用最多的无非就是各种各样的 Starter 了.那何为 Starter 呢?你可以理解为一个可拔插式的插件(组件).或者理解为场景启动器. 通过 Starter,能够简化以前繁杂的配置,无需过多的配置和依赖,它会帮你合并依赖,并且将其统一集成到一个 Starter 中,我们只需在 Maven 或 Gradle 中引入 Starter 依赖即可.SpringBoot 会自动扫描需要加载的信息并启动

随机推荐