Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解

本文通过一个简易安全认证示例的开发实践,理解过滤器和拦截器的工作原理。

很多文章都将过滤器(Filter)、拦截器(Interceptor)和监听器(Listener)这三者和Spring关联起来讲解,并认为过滤器(Filter)、拦截器(Interceptor)和监听器(Listener)是Spring提供的应用广泛的组件功能。

但是严格来说,过滤器和监听器属于Servlet范畴的API,和Spring没什么关系。

因为过滤器继承自javax.servlet.Filter接口,监听器继承自javax.servlet.ServletContextListener接口,只有拦截器继承的是org.springframework.web.servlet.HandlerInterceptor接口。

上面的流程图参考自网上资料,一图胜千言。看完本文以后,将对过滤器和拦截器的调用过程会有更深刻理解。

一、安全认证设计思路

有时候内外网调用API,对安全性的要求不一样,很多情况下外网调用API的种种限制在内网根本没有必要,但是网关部署的时候,可能因为成本和复杂度等问题,内外网要调用的API会部署在一起。

实现REST接口的安全性,可以通过成熟框架如Spring Security或者 shiro 搞定。

但是因为安全框架往往实现复杂(我数了下Spring Security,洋洋洒洒大概有11个核心模块,shiro的源码代码量也比较惊人)同时可能要引入复杂配置(能不能让人痛快一点),不利于中小团队的灵活快速开发、部署及问题排查。

很多团队自己造轮子实现安全认证,本文这个简易认证示例参考自我所在的前厂开发团队,可以认为是个基于token的安全认证服务。

大致设计思路如下:

1、自定义http请求头,每次调用API都在请求头里传人一个token值

2、token放在缓存(如redis)中,根据业务和API的不同设置不同策略的过期时间

3、token可以设置白名单和黑名单,可以限制API调用频率,便于开发和测试,便于紧急处理异状,甚至临时关闭API

4、外网调用必须传人token,token可以和用户有关系,比如每次打开页面或者登录生成token写入请求头,页面验证cookie和token有效性等

在Spring Security框架里有两个概念,即 认证授权 ,认证指可以访问系统的用户,而授权则是用户可以访问的资源。

实现上述简易安全认证需求,你可能需要独立出一个token服务,保证生成token全局唯一,可能包含的模块有自定义流水生成器、CRM、加解密、日志、API统计、缓存等,但是和用户(CRM)其实是弱绑定关系。某些和用户有关系的公共服务,比如我们经常用到的发送短信SMS和邮件服务,也可以通过token机制解决安全调用问题。

综上,本文的简易安全认证其实和Spring Security框架提供的认证和授权有点不一样,当然,这种“安全”处理方式对专业人士没什么新意,但是可以对外挡掉很大一部分小白用户。

二、自定义Filter

和Spring MVC类似,Spring Boot提供了很多servlet过滤器(Filter)可使用,并且它自动添加了一些常用过滤器,比如CharacterEncodingFilter(用于处理编码问题)、HiddenHttpMethodFilter(隐藏HTTP函数)、HttpPutFormContentFilter(form表单处理)、RequestContextFilter(请求上下文)等。通常我们还会自定义Filter实现一些通用功能,比如记录日志、判断是否登录、权限验证等。

1、自定义请求头

很简单,在request header添加自定义请求头authtoken:

@RequestMapping(value = "/getinfobyid", method = RequestMethod.POST)
 @ApiOperation("根据商品Id查询商品信息")
 @ApiImplicitParams({
   @ApiImplicitParam(paramType = "header", name = "authtoken", required = true, value = "authtoken", dataType =
     "String"),
 })
 public GetGoodsByGoodsIdResponse getGoodsByGoodsId(@RequestHeader String authtoken, @RequestBody GetGoodsByGoodsIdRequest request) {
  return _goodsApiService.getGoodsByGoodsId(request);
 }
getGoodsByGoodsId

加了@RequestHeader修饰的authtoken字段就可以在swagger这样的框架下显示出来。

调用后,可以根据http工具看到请求头,本文示例是authtoken(和某些框架的token区分开):

备注:很多httpclient工具都支持动态传人请求头,比如RestTemplate。

2、实现Filter

Filter接口共有三个方法,即init,doFilter和destory,看到名称就大概知道它们主要用途了,通常我们只要在doFilter这个方法内,对Http请求进行处理:

package com.power.demo.controller.filter;
import com.power.demo.common.AppConst;
import com.power.demo.common.BizResult;
import com.power.demo.service.contract.AuthTokenService;
import com.power.demo.util.PowerLogger;
import com.power.demo.util.SerializeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class AuthTokenFilter implements Filter {
 @Autowired
 private AuthTokenService authTokenService;
 @Override
 public void init(FilterConfig var1) throws ServletException {
 }
 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
   throws IOException, ServletException {
  HttpServletRequest req = (HttpServletRequest) request;
  String token = req.getHeader(AppConst.AUTH_TOKEN);
  BizResult<String> bizResult = authTokenService.powerCheck(token);
  System.out.println(SerializeUtil.Serialize(bizResult));
  if (bizResult.getIsOK() == true) {
   PowerLogger.info("auth token filter passed");
   chain.doFilter(request, response);
  } else {
   throw new ServletException(bizResult.getMessage());
  }
 }
 @Override
 public void destroy() {
 }
}
AuthTokenFilter

注意,Filter这样的东西,我认为从实际分层角度,多数处理的还是表现层偏多,不建议直接在Filter中直接使用数据访问层Dao,虽然这样的代码一两年前我在很多老古董项目中看到过很多次,而且<<Spring实战>>的书里也有这样写的先例。

3、认证服务

这里就是主要业务逻辑了,示例代码只是简单写下思路,不要轻易就用于生产环境:

package com.power.demo.service.impl;
import com.power.demo.cache.PowerCacheBuilder;
import com.power.demo.common.BizResult;
import com.power.demo.service.contract.AuthTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class AuthTokenServiceImpl implements AuthTokenService {
  @Autowired
  private PowerCacheBuilder cacheBuilder;
  /*
   * 验证请求头token是否合法
   * */
  @Override
  public BizResult<String> powerCheck(String token) {
    BizResult<String> bizResult = new BizResult<>(true, "验证通过");
    System.out.println("token的值为:" + token);
    if (StringUtils.isEmpty(token) == true) {
      bizResult.setFail("authtoken为空");
      return bizResult;
    }
    //处理黑名单
    bizResult = checkForbidList(token);
    if (bizResult.getIsOK() == false) {
      return bizResult;
    }
    //处理白名单
    bizResult = checkAllowList(token);
    if (bizResult.getIsOK() == false) {
      return bizResult;
    }
    String key = String.format("Power.AuthTokenService.%s", token);
    //cacheBuilder.set(key, token);
    //cacheBuilder.set(key, token.toUpperCase());
    //从缓存中取
    String existToken = cacheBuilder.get(key);
    if (StringUtils.isEmpty(existToken) == true) {
      bizResult.setFail(String.format("不存在此authtoken:%s", token));
      return bizResult;
    }
    //比较token是否相同
    Boolean isEqual = token.equals(existToken);
    if (isEqual == false) {
      bizResult.setFail(String.format("不合法的authtoken:%s", token));
      return bizResult;
    }
    //do something
    return bizResult;
  }
}
AuthTokenServiceImpl

用到的缓存服务可以参考这里,这个也是我在前厂的经验总结。

4、注册Filter

常见的有两种写法:

(1)、使用@WebFilter注解来标识Filter

@Order(1)
@WebFilter(urlPatterns = {"/api/v1/goods/*", "/api/v1/userinfo/*"})
public class AuthTokenFilter implements Filter {

使用@WebFilter注解,还可以配合使用@Order注解,@Order注解表示执行过滤顺序,值越小,越先执行,这个Order大小在我们编程过程中就像处理HTTP请求的生命周期一样大有用处。当然,如果没有指定Order,则过滤器的调用顺序跟添加的过滤器顺序相反,过滤器的实现是责任链模式。

最后,在启动类上添加@ServletComponentScan 注解即可正常使用自定义过滤器了。

(2)、使用FilterRegistrationBean对Filter进行自定义注册

本文以第二种实现自定义Filter注册:

package com.power.demo.controller.filter;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.List;
@Configuration
@Component
public class RestFilterConfig {
  @Autowired
  private AuthTokenFilter filter;
  @Bean
  public FilterRegistrationBean filterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(filter);
    //设置(模糊)匹配的url
    List<String> urlPatterns = Lists.newArrayList();
    urlPatterns.add("/api/v1/goods/*");
    urlPatterns.add("/api/v1/userinfo/*");
    registrationBean.setUrlPatterns(urlPatterns);
    registrationBean.setOrder(1);
    registrationBean.setEnabled(true);
    return registrationBean;
  }
}
RestFilterConfig

请大家特别注意urlPatterns,属性urlPatterns指定要过滤的URL模式。对于Filter的作用区域,这个参数居功至伟。

注册好Filter,当Spring Boot启动时监测到有javax.servlet.Filter的bean时就会自动加入过滤器调用链ApplicationFilterChain。

调用一个API试试效果:

通常情况下,我们在Spring Boot下都会自定义一个全局统一的异常管理增强 GlobalExceptionHandler (和上面这个显示会略有不同)。

根据我的实践,过滤器里抛出异常,不会被全局唯一的异常管理增强捕获到并进行处理,这个和拦截器Inteceptor以及下一篇文章介绍的自定义AOP拦截不同。

到这里,一个通过自定义Filter实现的简易安全认证服务就搞定了。

三、自定义拦截器

1、实现拦截器

继承接口HandlerInterceptor,实现拦截器,接口方法有下面三个:

preHandle是请求执行前执行

postHandle是请求结束执行

afterCompletion是视图渲染完成后执行

package com.power.demo.controller.interceptor;
import com.power.demo.common.AppConst;
import com.power.demo.common.BizResult;
import com.power.demo.service.contract.AuthTokenService;
import com.power.demo.util.PowerLogger;
import com.power.demo.util.SerializeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
 * 认证token拦截器
 * */
@Component
public class AuthTokenInterceptor implements HandlerInterceptor {
  @Autowired
  private AuthTokenService authTokenService;
  /*
   * 请求执行前执行
   * */
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    boolean handleResult = false;
    String token = request.getHeader(AppConst.AUTH_TOKEN);
    BizResult<String> bizResult = authTokenService.powerCheck(token);
    System.out.println(SerializeUtil.Serialize(bizResult));
    handleResult = bizResult.getIsOK();
    PowerLogger.info("auth token interceptor拦截结果:" + handleResult);
    if (bizResult.getIsOK() == true) {
      PowerLogger.info("auth token interceptor passed");
    } else {
      throw new Exception(bizResult.getMessage());
    }
    return handleResult;
  }
  /*
   * 请求结束执行
   * */
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  }
  /*
   * 视图渲染完成后执行
   * */
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  }
}
AuthTokenInterceptor

示例中,我们选择在请求执行前进行token安全认证。

认证服务就是过滤器里介绍的AuthTokenService,业务逻辑层实现复用。

2、注册拦截器

定义一个InterceptorConfig类,继承自WebMvcConfigurationSupport,WebMvcConfigurerAdapter已经过时。

将AuthTokenInterceptor作为bean注入,其他设置拦截器拦截的URL和过滤器非常相似:

package com.power.demo.controller.interceptor;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Configuration
@Component
public class InterceptorConfig extends WebMvcConfigurationSupport { //WebMvcConfigurerAdapter已经过时
  private static final String FAVICON_URL = "/favicon.ico";
  /**
   * 发现如果继承了WebMvcConfigurationSupport,则在yml中配置的相关内容会失效。
   *
   * @param registry
   */
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/").addResourceLocations("/**");
    registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
  }
  /**
   * 配置servlet处理
   */
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    //设置(模糊)匹配的url
    List<String> urlPatterns = Lists.newArrayList();
    urlPatterns.add("/api/v1/goods/*");
    urlPatterns.add("/api/v1/userinfo/*");
    registry.addInterceptor(authTokenInterceptor()).addPathPatterns(urlPatterns).excludePathPatterns(FAVICON_URL);
    super.addInterceptors(registry);
  }
  //将拦截器作为bean写入配置中
  @Bean
  public AuthTokenInterceptor authTokenInterceptor() {
    return new AuthTokenInterceptor();
  }
}
InterceptorConfig

启动应用后,调用接口就可以看到拦截器拦截的效果了。全局统一的异常管理 GlobalExceptionHandler 捕获异常后处理如下:

和过滤器显示的主要错误提示信息几乎一样,但是堆栈信息更加丰富。

四、过滤器和拦截器区别

主要区别如下:

1、拦截器主要是基于java的反射机制的,而过滤器是基于函数回调

2、拦截器不依赖于servlet容器,过滤器依赖于servlet容器

3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用

4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问

5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

参考过的一些文章,有的说“拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑”,经过实际验证,这是不对的。

注意:过滤器的触发时机是容器后,servlet之前,所以过滤器的 doFilter (ServletRequest request, ServletResponse response, FilterChain chain)的入参是ServletRequest,而不是HttpServletRequest,因为过滤器是在HttpServlet之前。下面这个图,可以让你对Filter和Interceptor的执行时机有更加直观的认识:

只有经过DispatcherServlet 的请求,才会走拦截器链,自定义的Servlet请求是不会被拦截的,比如我们自定义的Servlet地址http://localhost:9090/testServlet是不会被拦截器拦截的。但不管是属于哪个Servlet,只要符合过滤器的过滤规则,过滤器都会执行。

根据上述分析,理解原理,实际操作就简单了,哪怕是ASP.NET过滤器亦然。

问题:实现更加灵活的安全认证

在Java Web下通过自定义过滤器Filter或者拦截器Interceptor通过urlPatterns,可以实现对特定匹配的API进行安全认证,比如匹配所有API、匹配某个或某几个API等,但是有时候这种匹配模式对开发人员相对不够友好。

我们可以参考Spring Security那样,通过注解+SpEL实现强大功能。

又比如在ASP.NET中,我们经常用到Authorized特性,这个特性可以加在类上,也可以作用于方法上,可以更加动态灵活地控制安全认证。

我们没有选择Spring Security,那就自己实现类似Authorized的灵活的安全认证,主要实现技术就是我们所熟知的AOP。

通过AOP方式实现更灵活的拦截的基础知识本文就先不提了,更多的关于AOP的话题将在下篇文章分享。

总结

以上所述是小编给大家介绍的Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • spring boot 配置Filter过滤器的方法

    Filter 过滤器是web开发中很重要的一个组件,下面以一个session登陆的例子介绍下spring boot中如何使用Filter 首先要准备一个实现了Filter的接口的类 SessionFilter: import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRespo

  • Spring Boot项目实战之拦截器与过滤器

    一.拦截器与过滤器 在讲Spring boot之前,我们先了解一下过滤器和拦截器.这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的.在分析两者的区别之前,我们先理解一下AOP的概念,AOP不是一种具体的技术,而是一种编程思想.在面向对象编程的过程中,我们很容易通过继承.多态来解决纵向扩展. 但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的.所以AOP--面向切面编程其实是面向对象编程思想的一个补充.而我们今天讲的过滤器

  • spring boot过滤器FilterRegistrationBean实现方式

    有2种方式可以实现过滤器 1:通过FilterRegistrationBean实例注册 2:通过@WebFilter注解生效 这里选择第一种,因为第二种不能设置过滤器之间的优先级 为了演示优先级,这里创建2个测试过滤器类:Test1Filter.Test2Filter 通过实现javax.servlet.Filter接口,覆盖其doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)方法,决定拦截或放行 publi

  • Spring Boot的filter(过滤器)简单使用实例详解

    过滤器(Filter)的注册方法和 Servlet 一样,有两种方式:代码注册或者注解注册 1.代码注册方式 通过代码方式注入过滤器 @Bean public FilterRegistrationBean indexFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(new IndexFilter()); registration.addUrlPatterns("/&quo

  • SpringBoot定义过滤器、监听器、拦截器的方法

    一.自定义过滤器 创建一个过滤器,实现javax.servlet.Filter接口,并重写其中的init.doFilter.destory方法. package com.example.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.Se

  • 详谈springboot过滤器和拦截器的实现及区别

    前言 springmvc中有两种很普遍的AOP实现: 1.过滤器(Filter) 2.拦截器(Interceptor) 本篇面对的是一些刚接触springboot的人群 所以主要讲解filter和interceptor的简单实现和它们之间到底有什么区别 (一些复杂的功能我会之后发出文章,请记得关注) Filter的简单实现 字面意思:过滤器就是过滤的作用,在web开发中过滤一些我们指定的url 那么它能帮我们过滤什么呢? 那功能可就多了: 比如过拦截掉我们不需要的接口请求 修改请求(reques

  • spring boot实现过滤器和拦截器demo

    整理文档,搜刮出一个spring boot实现过滤器和拦截器demo ,稍微整理精简一下做下分享. 拦截器定义: @WebServlet public class ActionInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Ex

  • Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解

    本文通过一个简易安全认证示例的开发实践,理解过滤器和拦截器的工作原理. 很多文章都将过滤器(Filter).拦截器(Interceptor)和监听器(Listener)这三者和Spring关联起来讲解,并认为过滤器(Filter).拦截器(Interceptor)和监听器(Listener)是Spring提供的应用广泛的组件功能. 但是严格来说,过滤器和监听器属于Servlet范畴的API,和Spring没什么关系. 因为过滤器继承自javax.servlet.Filter接口,监听器继承自ja

  • Spring Boot项目中定制拦截器的方法详解

    这篇文章主要介绍了Spring Boot项目中定制拦截器的方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供HandlerInterceptor(拦截器)工具.根据文档,HandlerInterceptor的功能跟过滤器类似,但拦截器提供更精细的控制能力:在request被响应之前.request被响应之后.视

  • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    标题和Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start.before_init.after_start的lifecycleEvent事件给相应的监听器(如

  • Spring Boot加密配置文件特殊内容的示例代码详解

    有时安全不得不考虑,看看新闻泄漏风波事件就知道了我们在用Spring boot进行开发时,经常要配置很多外置参数ftp.数据库连接信息.支付信息等敏感隐私信息,如下 ​ 这不太好,特别是互联网应用,应该用加密的方式比较安全,有点类似一些应用如电商.公安.安检平台.滚动式大屏中奖信息等显示身份证号和手机号都是前几位4109128*********和158*******.那就把图中的明文改造下1. 引入加密包,可选,要是自己实现加解密算法,就不需要引入第三方加解密库 <dependency> &l

  • Spring Boot Actuator监控的简单使用方法示例代码详解

    Spring Boot Actuator帮助我们实现了许多中间件比如mysql.es.redis.mq等中间件的健康指示器. 通过 Spring Boot 的自动配置,这些指示器会自动生效.当这些组件有问题的时候,HealthIndicator 会返回 DOWN 或 OUT_OF_SERVICE 状态,health 端点 HTTP 响应状态码也会变为 503,我们可以以此来配置程序健康状态监控报警. 使用步骤也非常简单,这里演示的是线程池的监控.模拟线程池满了状态下将HealthInicator

  • Spring Boot 的创建和运行示例代码详解

    目录 1.什么是Spring Boot 2.Spring Boot 优点 3. Spring Boot 项目创建 3.1 使用 Idea 社区版创建 4.项目目录介绍和运行 4.1 运行项目 点击启动类的 main ⽅法就可以运⾏ Spring Boot 项⽬了 4.2 验证项目是否成功 5. 注意事项:包路径错误 5.1 正确路径 6. Spring Boot 热部署(热加载) 6.1 添加框架⽀持  在 pom.xml 中添加如下框架引⽤: 6.2 开启项目自动编译 6.3 开启运⾏中热部署

  • Spring Aware接口示例代码详解

    若 Spring 检测到 bean 实现了 Aware 接口,则会为其注入相应的依赖.所以通过让bean 实现 Aware 接口,则能在 bean 中获得相应的 Spring 容器资源. Spring 中提供的 Aware 接口有: BeanNameAware:注入当前 bean 对应 beanName BeanClassLoaderAware:注入加载当前 bean 的 ClassLoader BeanFactoryAware:注入 当前BeanFactory容器 的引用 BeanNameAw

  • Spring的BeanFactoryPostProcessor接口示例代码详解

    接口简介 BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它. BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级,主要用来在

随机推荐