【问题标题】:component-scan get in the way of bean initialization?组件扫描妨碍 bean 初始化?
【发布时间】:2015-10-21 12:34:55
【问题描述】:

我在尝试复制一个简单的 Spring OAuth 项目 sparklr2 时遇到了这个问题。源代码在这里 https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2/sparklr

源代码运行完美,当我用tomcat调试它时,它会初始化WebMvcConfigurerAdapter中的所有@Bean,包括控制器。但注意到 @ComponentScan() 没有被使用。

然后我创建自己的 MVC 项目,复制几乎 100% 的代码,但我使用的是 WebApplicationInitializer 而不是 AbstractDispatcherServletInitializer。我使用 WebApllicationInitializer 是因为我只学过这种方式来编写 MVC。

然后我运行项目,@Bean 初始化。然后我用浏览器检查 /login,得到 404。这可能是由于 spring 不知道我有控制器,然后我将 @ComponentScan 添加到我的配置类中,现在 /login 出现了。 但奇怪的是,所有与 Controller 相关的 @Bean 都没有初始化。因此,当我对这些控制器调用任何方法时,由于它们的属性未初始化,因此不会给我任何对象或 null 异常。

所以,我的意思是,该示例是如何工作的,我的意思是控制器和 jsp 在不使用 @ComponentScan 的情况下正确处理和响应? 换个角度看,为什么@ComponentScan 会阻止@Bean 在我的项目中初始化?

我的 WebApplicationInitializer

@Configuration
@EnableWebMvc
@ComponentScan("umedia.test.oauth.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ContentNegotiatingViewResolver contentViewResolver()
            throws Exception {
        ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean();
        contentNegotiationManager.addMediaType("json",
                MediaType.APPLICATION_JSON);

        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");

        MappingJackson2JsonView defaultView = new MappingJackson2JsonView();
        defaultView.setExtractValueFromSingleKeyModel(true);

        ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver();
        contentViewResolver
                .setContentNegotiationManager(contentNegotiationManager
                        .getObject());
        contentViewResolver.setViewResolvers(Arrays
                .<ViewResolver> asList(viewResolver));
        contentViewResolver.setDefaultViews(Arrays.<View> asList(defaultView));
        return contentViewResolver;
    }



    @Bean
    public PhotoServiceImpl photoServices() {
        List<PhotoInfo> photos = new ArrayList<PhotoInfo>();
        photos.add(createPhoto("1", "marissa"));
        photos.add(createPhoto("2", "paul"));
        photos.add(createPhoto("3", "marissa"));
        photos.add(createPhoto("4", "paul"));
        photos.add(createPhoto("5", "marissa"));
        photos.add(createPhoto("6", "paul"));

        PhotoServiceImpl photoServices = new PhotoServiceImpl();
        photoServices.setPhotos(photos);
        return photoServices;
    }

    // N.B. the @Qualifier here should not be necessary (gh-298) but lots of
    // users report needing it.
    @Bean
    public AdminController adminController(
            TokenStore tokenStore,
            @Qualifier("consumerTokenServices") ConsumerTokenServices tokenServices,
            SparklrUserApprovalHandler userApprovalHandler) {
        AdminController adminController = new AdminController();
        adminController.setTokenStore(tokenStore);
        adminController.setTokenServices(tokenServices);
        adminController.setUserApprovalHandler(userApprovalHandler);
        return adminController;
    }

    // this url, do I need to change it?
    private PhotoInfo createPhoto(String id, String userId) {
        PhotoInfo photo = new PhotoInfo();
        photo.setId(id);
        photo.setName("photo" + id + ".jpg");
        photo.setUserId(userId);
        photo.setResourceURL("/org/springframework/security/oauth/examples/sparklr/impl/resources/"
                + photo.getName());
        return photo;
    }

    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public PhotoServiceUserController photoServiceUserController(
            PhotoService photoService) {
        PhotoServiceUserController photoServiceUserController = new PhotoServiceUserController();
        return photoServiceUserController;
    }

    @Bean
    public PhotoController photoController(PhotoService photoService) {
        PhotoController photoController = new PhotoController();
        photoController.setPhotoService(photoService);
        return photoController;
    }

    @Bean
    public AccessConfirmationController accessConfirmationController(
            ClientDetailsService clientDetailsService,
            ApprovalStore approvalStore) {
        AccessConfirmationController accessConfirmationController = new AccessConfirmationController();
        accessConfirmationController
                .setClientDetailsService(clientDetailsService);
        accessConfirmationController.setApprovalStore(approvalStore);
        return accessConfirmationController;
    }

/*  @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }*/

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations(
                "/resources/");
    }

}

【问题讨论】:

    标签: spring spring-mvc component-scan


    【解决方案1】:

    所以,您有 @ComponentScan 与您的控制器上的 @Controller 交互 + 仍然使用这些创建 @Bean

    作为第一步,尝试删除@Beans 并尝试在控制器的构造函数上使用@Autowired 注入依赖项。然后@ComponentScan 应该可以识别@Controller,注入依赖项并毫无问题地使用@RequestMapping

    【讨论】:

    • 怪胎,谢谢。你可能是对的,我是 java config mvc 项目的新手。在 /@Controller 之前永远不要使用 /@Bean。我只是使用它们,因为该示例代码可以做到这一点。在尝试了许多组合之后,我认为组件扫描可以正确地使用 /@Controller,但 /@Bean 不是。我没有删除 /@Bean,我猜它只是告诉 spring 这是一个 bean。然后我将 /@Autowire 添加到这些控制器类中的一些相关变量中,它似乎可以工作。虽然不完全理解它们是如何工作的。
    • /@Bean 本身没有问题。通常它用于诸如服务之类的事情。但是 /@Controller 需要 /@RequestMapping 知道在哪个路径上工作,如果您像示例中那样使用 @Bean 创建控制器,则不会提供该路径。似乎您正在混合两种解决方案并混淆 spring ; ) 如果你把你的代码放到 github 上会更好,这样会更容易帮助你。
    • 在这里github.com/maxiwu/springexercise 我认为spring 被我的混乱配置搞糊涂了。但不知道如何正确地做到这一点。我的目标是,使用带有 WebMvcConfigurerAdapter 和 WebApplicationInitializer 的 spring MVC 设置。在向那些 MVC 控制器发出任何请求之前支持这些初始化。清理不必要的配置和注释。
    • 很抱歉,在不理解的情况下复制周围的所有内容并不是学习任何东西的解决方案。我试图为您修复它,但是错误太多了。请遵循一些 Spring Boot 教程 - 它们的结构非常好:)
    • 我也认为复制东西不是一个好方法。发生了很多事情,注释,安全性,oauth,spring boot。我刚刚阅读了一些 Spring Security 材料,将继续阅读有关 Spring Boot 的内容。感谢您为我调查此问题,非常感谢。
    猜你喜欢
    • 2012-02-19
    • 2014-11-17
    • 2021-09-11
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多