Spring:WebMvcConfigurerAdapter bean注册了两次

时间:2013-12-19 04:57:07

标签: spring-mvc spring-boot spring-bean

这是Spring + Jackson + joda time: how to specify the serialization/deserialization format?的后续问题。

当我编写代码的最终版本时,我第一次编写如下代码:(仅显示相关部分)

@Configuration
public class WebMvcConfiguration
{
    @Bean
    public WebMvcConfigurerAdapter apiWebMvcConfiguration()
    {
        return new ApiWebMvcConfiguration();
    }

    public class ApiWebMvcConfiguration extends WebMvcConfigurerAdapter
    {
        public ApiWebMvcConfiguration()
        {
            log.debug("### ApiWebMvcConfiguration");
        }

        @Bean
        public UserInterceptor userInterceptor()
        {
            return new UserInterceptor(false);
        }

        @Override
        public void addInterceptors(InterceptorRegistry registry)
        {
            log.debug("### addInterceptors");
            registry.addInterceptor(userInterceptor())
                .addPathPatterns("/api/user/**");
        }
    }

    private static final Log log =
        LogFactory.getLog(WebMvcConfiguration.class);
}

没有@EnableWebMvc,因为使用了Spring Boot的默认@EnableWebMvc类。
请注意,userInterceptor bean位于WebMvcConfigurerAdapter类中,该类也是一个bean。

当我运行应用程序时,发生以下错误:
(我的班级的班级名称被我自己替换为“......”)

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name '...WebMvcConfiguration$ApiWebMvcConfiguration': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [...WebMvcConfiguration$ApiWebMvcConfiguration]: No default constructor found; nested exception is java.lang.NoSuchMethodException: ...WebMvcConfiguration$ApiWebMvcConfiguration.<init>()
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1076)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1021)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:124)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:609)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
    at ...Application.main(Application.java:17)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [...WebMvcConfiguration$ApiWebMvcConfiguration]: No default constructor found; nested exception is java.lang.NoSuchMethodException: ...WebMvcConfiguration$ApiWebMvcConfiguration.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1069)
    ... 14 more
Caused by: java.lang.NoSuchMethodException: ...WebMvcConfiguration$ApiWebMvcConfiguration.<init>()
    at java.lang.Class.getConstructor0(Class.java:2810)
    at java.lang.Class.getDeclaredConstructor(Class.java:2053)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
    ... 15 more

然后我将ApiWebMvcConfiguration类更改为静态内部类。

应用程序“正常”启动,但ApiWebMvcConfiguration类已实例化两次。也就是说,“### ApiWebMvcConfiguration”被打印两次。因此,“### addInterceptors”被打印了两次。

然后,当UserIntercepter的代码运行时,由于null @Autowired JdbcTemplate而失败。也就是说,@ Autowired并不适用于该对象。 (JdbcTemplate在其他对象中成功@Autowired)

所以,我将代码更改为最终版本,如Spring + Jackson + joda time: how to specify the serialization/deserialization format?所示,也就是说,UserIntercepter bean已经退出ApiWebMvcConfiguration,问题就消失了。

这是正确的行为吗? @Bean不应该嵌套吗?

2 个答案:

答案 0 :(得分:0)

Spring尝试实例化ApiWebMvcConfiguration本身。这不起作用,因为非静态内部类不能像普通类一样实例化。他们需要引用外部类的实例。因此出现错误消息“找不到默认构造函数”。

当您将内部类更改为静态类时,实例化会起作用,但正如您所注意到的,它仍然会发生两次。

问题是@Bean的{​​{1}}注释。你告诉Spring它可以从这个方法中获取bean。但是为了获得一个,Spring需要一个userInterceptor()的实例。所以它创造了一个。但是另一个是由ApiWebMvcConfiguration方法创建的。

答案 1 :(得分:0)

@Configuration类中的嵌套类始终被解释为@Bean。所以你通过添加自己的显式@Bean定义来注册它两次。