spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 学习如何通过自己写代码的方式,向spring容器中注册bean

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos
  • 本章是《spring4.1.8扩展实战》系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean;

关于注册bean到容器

  • 我们开发的类,如果想注册到spring容器,让spring来完成实例化,常用方式如下:
  1. xml中通过bean节点来配置;
  2. 使用@Service、@Controller、@Conponent等注解;
  • 其实,除了以上方式,spring还支持我们通过代码来将指定的类注册到spring容器中,也就是今天我们要实践的主要内容,接下来就从spring源码开始,先学习源码再动手实战;

本章概要

  • 本章由以下几部分组成:
  1. 了解BeanDefinitionRegistryPostProcessor接口;
  2. 分析spring容器如何使用BeanDefinitionRegistryPostProcessor接口;
  3. 实战,开发BeanDefinitionRegistryPostProcessor接口的实现类,验证通过代码注册bean的功能;

了解BeanDefinitionRegistryPostProcessor接口

  • 实现注册bean功能的关键是BeanDefinitionRegistryPostProcessor接口,来看看这接口的继承关系,如下图:

image.png

  • BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor接口,关于BeanFactoryPostProcessor我们在上一章《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》已做了详细的分析和实战,知道BeanFactoryPostProcessor的实现类在其postProcessBeanFactory方法被调用时,可以对bean的定义进行控制,因此BeanDefinitionRegistryPostProcessor的实现类一共要实现以下两个方法:
  • void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException:

该方法的实现中,主要用来对bean定义做一些改变,这些在上一章《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》有详细说明;

  • void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException:

该方法用来注册更多的bean到spring容器中,详细观察入参BeanDefinitionRegistry接口,看看这个参数能带给我们什么能力:
image.png

  • 从上图可以看到,为了能让我们通过代码将bean注册到spring环境,BeanDefinitionRegistry提供了丰富的方法来操作bean定义,判断、注册、反注册等方法都准备好了,我们在编写postProcessBeanDefinitionRegistry方法的内容时,就能直接使用入参registry的这些方法来完成判断和注册、反注册等操作;

分析spring容器如何使用BeanDefinitionRegistryPostProcessor接口

  • 来看看BeanDefinitionRegistryPostProcessor接口的实现类,是在哪里被spring容器使用的:
  • 如下图所示,红框中的invokeBeanFactoryPostProcessors方法用来找出所有beanFactory后置处理器,并且调用这些处理器来改变bean的定义:

image.png

  • 打开invokeBeanFactoryPostProcessors方法,如下所示,实际操作是委托PostProcessorRegistrationDelegate去完成的:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    }
  • 继续看PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法,该方法内容太丰富,我们只看重点,第一个重点如下图红框所示,当前的beanFactory是否实现了接口BeanDefinitionRegistry

image.png

  • 为了搞清楚这个问题,我们应该看看当前beanFactory的继承和实现,以springboot中的应用为例,当前beanFactory的类型是DefaultListableBeanFactory,来看看它的类图:

image.png

  • 从上图红框可见,beanFactory实现了BeanDefinitionRegistry接口,因此我们的关注点是if条件满足后的执行逻辑;
  • 继续看PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法,以下片段就是操作BeanDefinitionRegistryPostProcessor的核心逻辑:
boolean reiterate = true;
while (reiterate) {
    reiterate = false;
    //查出所有实现了BeanDefinitionRegistryPostProcessor接口的bean名称
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
        //前面的逻辑中,已经对实现了PriorityOrdered和Ordered的bean都处理过了,因此通过processedBeans过滤,processedBeans中没有的才会在此处理
        if (!processedBeans.contains(ppName)) {
            //根据名称和类型获取bean
            BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
            //把已经调用过postProcessBeanDefinitionRegistry方法的bean全部放在registryPostProcessors中
            registryPostProcessors.add(pp);
            //把已经调用过postProcessBeanDefinitionRegistry方法的bean的名称全部放在processedBeans中
            processedBeans.add(ppName);
            //执行此bean的postProcessBeanDefinitionRegistry方法
            pp.postProcessBeanDefinitionRegistry(registry);
            //改变退出while的条件
            reiterate = true;
        }
    }
}

//registryPostProcessors中保存了所有执行过postProcessBeanDefinitionRegistry方法的bean,
//现在再来执行这些bean的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
//regularPostProcessors中保存的是所有入参中带来的BeanFactoryPostProcessor实现类,并且这里面已经剔除了BeanDefinitionRegistryPostProcessor的实现类,现在要让这些bean执行postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  • 如上述代码所示,所有实现了BeanDefinitionRegistryPostProcessor接口的bean,其postProcessBeanDefinitionRegistry方法都会调用,然后再调用其postProcessBeanFactory方法,这样一来,我们如果自定义了BeanDefinitionRegistryPostProcessor接口的实现类,那么我们开发的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法都会被执行一次;
  • 到这里,我们的源码学习部分就完成了,接下来看开始实战吧;

实战,开发BeanDefinitionRegistryPostProcessor接口的实现类

  • 本次实战的内容是创建一个springboot工程,在里面自定义一个BeanDefinitionRegistryPostProcessor接口的实现类,如果您不想敲代码,也可以去github下载源码,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本章源码在文件夹customizebeandefinitionregistrypostprocessor下,如下图红框所示:

image.png

  • 接下来开始实战吧:
  • 基于maven创建一个springboot的web工程,名为customizebeandefinitionregistrypostprocessor,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bolingcavalry</groupId>
    <artifactId>customizebeandefinitionregistrypostprocessor</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>customizebeandefinitionregistrypostprocessor</name>
    <description>Demo project for customize BeanDefinitionRegistryPostProcessor</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.15.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • 定义一个服务接口CalculateService:
package com.bolingcavalry.customizebeandefinitionregistrypostprocessor.service;

public interface CalculateService {
    /**
     * 整数加法
     * @param a
     * @param b
     * @return
     */
    int add(int a, int b);

    /**
     * 返回当前实现类的描述信息
     * @return
     */
    String getServiceDesc();
}
  • 创建CalculateService接口的实现类CalculateServiceImpl,注意,不要将其声明为spring的bean
package com.bolingcavalry.customizebeandefinitionregistrypostprocessor.service.impl;

import com.bolingcavalry.customizebeandefinitionregistrypostprocessor.service.CalculateService;

public class CalculateServiceImpl implements CalculateService {

    private String desc = "desc from class";

    public void setDesc(String desc) {
        this.desc = desc;
    }


    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public String getServiceDesc() {
        return desc;
    }
}
  • 创建工具类Utils,里面是些工具方法,例如在日志中打印出当前线程的执行堆栈:
package com.bolingcavalry.customizebeandefinitionregistrypostprocessor.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Description : 提供一些常用的工具方法
 * @Author : zq2599@gmail.com
 * @Date : 2018-08-14 05:51
 */
public class Utils {

    private static final Logger logger = LoggerFactory.getLogger(Utils.class);

    /**
     * 打印当前线程堆栈信息
     * @param prefix
     */
    public static void printTrack(String prefix){
        StackTraceElement[] st = Thread.currentThread().getStackTrace();

        if(null==st){
            logger.info("invalid stack");
            return;
        }

        StringBuffer sbf =new StringBuffer();

        for(StackTraceElement e:st){
            if(sbf.length()>0){
                sbf.append(" <- ");
                sbf.append(System.getProperty("line.separator"));
            }

            sbf.append(java.text.MessageFormat.format("{0}.{1}() {2}"
                    ,e.getClassName()
                    ,e.getMethodName()
                    ,e.getLineNumber()));
        }

        logger.info(prefix
                + "\n************************************************************\n"
                + sbf.toString()
                + "\n************************************************************");
    }
}
  • 创建Controller类,用于验证我们通过代码注册的bean,注意要给成员变量calculateService添加注解@Autowired(required = false),否则有的IDEAL上会有红叉提示
package com.bolingcavalry.customizebeandefinitionregistrypostprocessor.controller;

import com.bolingcavalry.customizebeandefinitionregistrypostprocessor.service.CalculateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired(required = false)
    CalculateService calculateService;

    @GetMapping("/add/{a}/{b}")
    public String add(@PathVariable("a") int a, @PathVariable("b") int b){
        return "add result : " + calculateService.add(a, b) + ", from [" + calculateService.getServiceDesc() + "]";
    }
}
  • 创建BeanDefinitionRegistryPostProcessor的实现类CustomizeBeanDefinitionRegistryPostProcessor:
@Component
public class CustomizeBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
    //打印当前堆栈信息    
        Utils.printTrack("execute postProcessBeanDefinitionRegistry");
    
    //创建一个bean的定义类的对象,bean类型是CalculateServiceImpl
        RootBeanDefinition helloBean = new RootBeanDefinition(CalculateServiceImpl.class);

        //bean的定义注册到spring环境
        beanDefinitionRegistry.registerBeanDefinition("calculateService", helloBean);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    //打印当前堆栈信息        
        Utils.printTrack("execute postProcessBeanFactory");
    }
}
  • 上面CustomizeBeanDefinitionRegistryPostProcessor就是我们扩展出来的BeanDefinitionRegistryPostProcessor实现类,用来将CalculateServiceImpl注册到spring容器,名称为calculateService;
  • 启动类CustomizebeandefinitionregistrypostprocessorApplication:
package com.bolingcavalry.customizebeandefinitionregistrypostprocessor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CustomizebeandefinitionregistrypostprocessorApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomizebeandefinitionregistrypostprocessorApplication.class, args);
    }
}
  • 启动应用,在启动日志中可以看到CustomizeBeanDefinitionRegistryPostProcessor的方法被调用时的堆栈情况:
2018-08-30 18:55:40.323  INFO 14880 --- [           main] c.b.c.util.Utils                         : execute postProcessBeanDefinitionRegistry
************************************************************
java.lang.Thread.getStackTrace() 1,556 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.util.Utils.printTrack() 20 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.registrypostprocessor.CustomizeBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry() 21 <- 
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors() 272 <- 
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 122 <- 
org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors() 687 <- 
org.springframework.context.support.AbstractApplicationContext.refresh() 525 <- 
org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh() 122 <- 
org.springframework.boot.SpringApplication.refresh() 693 <- 
org.springframework.boot.SpringApplication.refreshContext() 360 <- 
org.springframework.boot.SpringApplication.run() 303 <- 
org.springframework.boot.SpringApplication.run() 1,118 <- 
org.springframework.boot.SpringApplication.run() 1,107 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.CustomizebeandefinitionregistrypostprocessorApplication.main() 10
************************************************************
2018-08-30 18:55:40.542  INFO 14880 --- [           main] c.b.c.util.Utils                         : execute postProcessBeanFactory
************************************************************
java.lang.Thread.getStackTrace() 1,556 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.util.Utils.printTrack() 20 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.registrypostprocessor.CustomizeBeanDefinitionRegistryPostProcessor.postProcessBeanFactory() 31 <- 
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 283 <- 
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 127 <- 
org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors() 687 <- 
org.springframework.context.support.AbstractApplicationContext.refresh() 525 <- 
org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh() 122 <- 
org.springframework.boot.SpringApplication.refresh() 693 <- 
org.springframework.boot.SpringApplication.refreshContext() 360 <- 
org.springframework.boot.SpringApplication.run() 303 <- 
org.springframework.boot.SpringApplication.run() 1,118 <- 
org.springframework.boot.SpringApplication.run() 1,107 <- 
com.bolingcavalry.customizebeandefinitionregistrypostprocessor.CustomizebeandefinitionregistrypostprocessorApplication.main() 10
************************************************************
  • 在浏览器输入:http://localhost:8080/add/1/2,如下图可以看到网页正常响应,controller可以正常使用calculateService实例:

image.png

  • 去掉CustomizeBeanDefinitionRegistryPostProcessor的注释@Component,重启应用,再去访问http://localhost:8080/add/1/2,可以看到网页提示错误如下图:

image.png

  • 看后台日志,是因为calculateService这个bean为空,导致HelloController在响应请求的时候出现了空指针异常,这也再次证明了CustomizeBeanDefinitionRegistryPostProcessor的注册操作是有效的:
2018-08-30 19:00:01.186 ERROR 2760 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at com.bolingcavalry.customizebeandefinitionregistrypostprocessor.controller.HelloController.add(HelloController.java:18) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.18.RELEASE.jar:4.3.18.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]
  • 至此,自定义bean注册的实战就结束了,其实除了注册bean,我们还能利用BeanDefinitionRegistry这个入参做些其他事情,例如查找bean,反注册bean等,帮助我们实现一些bean操作的业务需求;

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关文章
|
14天前
|
JSON Java API
解码Spring Boot与JSON的完美融合:提升你的Web开发效率,实战技巧大公开!
【8月更文挑战第29天】Spring Boot作为Java开发的轻量级框架,通过`jackson`库提供了强大的JSON处理功能,简化了Web服务和数据交互的实现。本文通过代码示例介绍如何在Spring Boot中进行JSON序列化和反序列化操作,并展示了处理复杂JSON数据及创建RESTful API的方法,帮助开发者提高效率和应用性能。
45 0
|
14天前
|
SQL Java 数据库连接
Spring Boot联手MyBatis,打造开发利器:从入门到精通,实战教程带你飞越编程高峰!
【8月更文挑战第29天】Spring Boot与MyBatis分别是Java快速开发和持久层框架的优秀代表。本文通过整合Spring Boot与MyBatis,展示了如何在项目中添加相关依赖、配置数据源及MyBatis,并通过实战示例介绍了实体类、Mapper接口及Controller的创建过程。通过本文,你将学会如何利用这两款工具提高开发效率,实现数据的增删查改等复杂操作,为实际项目开发提供有力支持。
53 0
|
14天前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
41 0
|
14天前
|
Java 开发者 Spring
Spring Boot实战宝典:揭秘定时任务的幕后英雄,让业务处理如流水般顺畅,轻松驾驭时间管理艺术!
【8月更文挑战第29天】在现代应用开发中,定时任务如数据备份、报告生成等至关重要。Spring Boot作为流行的Java框架,凭借其强大的集成能力和简洁的配置方式,为开发者提供了高效的定时任务解决方案。本文详细介绍了如何在Spring Boot项目中启用定时任务支持、编写定时任务方法,并通过实战案例展示了其在业务场景中的应用,同时提供了注意事项以确保任务的正确执行。
27 0
|
14天前
|
Cloud Native 持续交付 Docker
云原生之旅:Docker容器化实战指南
【8月更文挑战第29天】本文将引领你进入云原生技术的世界,以Docker容器化为切入点,深入浅出地介绍如何利用Docker进行应用的打包、部署及管理。我们将通过实际代码示例,一步步展示Docker镜像的构建过程,以及如何运行和管理这些容器。无论你是初学者还是有一定经验的开发者,都能从中获得宝贵的知识和实操经验。
|
9天前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
|
9天前
|
应用服务中间件 nginx Docker
Docker同一台宿主机容器通信-通过容器名称互联
本文详细介绍了如何通过容器名称实现同一宿主机上容器间的互联,并提供了实战案例。首先,文章解释了容器间通过自定义名称访问的原理,随后演示了创建并连接Tomcat与Nginx容器的具体步骤。此外,还讨论了配置中可能出现的问题及解决方案,包括避免硬编码IP地址和使用自定义容器别名来增强系统的灵活性与可维护性。通过这些实践,展示了如何高效地配置容器间通信,确保服务稳定可靠。
16 1
Docker同一台宿主机容器通信-通过容器名称互联
|
6天前
|
Cloud Native 持续交付 Docker
云原生技术实践:Docker容器化部署教程
【9月更文挑战第4天】本文将引导你了解如何利用Docker这一云原生技术的核心工具,实现应用的容器化部署。文章不仅提供了详细的步骤和代码示例,还深入探讨了云原生技术背后的哲学,帮助你理解为何容器化在现代软件开发中变得如此重要,并指导你如何在实际操作中运用这些知识。
|
9天前
|
存储 Unix 虚拟化
Docker容器简介
Docker是一种轻量级的虚拟化技术,它通过容器化应用,提高了硬件资源利用率,简化了应用的部署、运输和运行,且与虚拟机相比,具有更快的交付速度和更低的资源消耗。
24 2
|
15天前
|
存储 安全 Ubuntu
Docker 镜像与 Docker 容器的区别
【8月更文挑战第27天】
56 5