用@Configuration和@Controller注释一个类。在重构方面需要帮助

时间:2018-08-01 14:48:35

标签: java spring spring-boot thymeleaf

下面是我的类,在该类中,我必须同时使用@Configuration@Controller,因为在整个应用程序中应该只有Thymeleaf的一个实例,否则我会得到例外。我的其他类都用@RequestScope注释,因此我不能使用单例范围的Bean。所以我混合使用Configuration和Controller来获得结果,但是我觉得这是一个不好的做法。我希望能对重构代码和消除不良做法提供任何帮助。

更新

我正在使用spring-boot 1.5.14。我正在使用以下方法来处理模板,并将处理后的模板保留为字符串。

@Controller
@Configuration
@EnableWebMvc
@ApplicationScope
public class MyThymeleafConfig {

    @GetMapping("/view-template")
    @ResponseBody
    public void viewTemplates() {

        Context context = new Context();
        context.setVariable("mydata", "this is it");

        String html = templateEngine().process("templates/view-to-process.html", context);
        System.out.println(html);
    }


    /*

    configuration for thymeleaf and template processing

    */

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(thymeleafTemplateResolver());
        return templateEngine;
    }

    @Bean
    public SpringResourceTemplateResolver thymeleafTemplateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("classpath:");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheable(false);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    }
}

要提供静态资源,请执行以下配置:

@Configuration
@EnableWebMvc
public class StaticResourceConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/**")
                .addResourceLocations("/static/", "classpath:static/");
    }
}

更新

我还提到了我不能接受以下提到的答案的原因,因为我的其他类都有请求范围。

更新

我还有其他@RequestScope类,如下所示:

@RequestScope
@Controller
public class SecondController {

    @GetMapping("/viewPage")
    public String viewPage(Model model) {
        model.addAttribute("mydata", "sjfbsdf");
        model.addAttribute("somedata", "sjdfksfjhshgdfbskdfj");
        return "templates/view-to-process.html";
    }
}

6 个答案:

答案 0 :(得分:7)

假设您使用的是Spring Boot,因为它已包含在标签中,所以不需要任何配置即可使用Thymeleaf。

只需拥有this dependency,您就可以:

@GetMapping("/view-template")
public String viewTemplates(Model model) {
    model.addAttribute("mydata", "this is it")
    return "view-to-process";
}

它应该可以工作。

是的,是的,@Configuration@Controller在同一个类中是您永远都不需要的。

答案 1 :(得分:6)

如果您看到注释的源代码(春季5),您将拥有:

控制器

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    @AliasFor(annotation = Component.class)
    String value() default "";

}

配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

    /**
     * Explicitly specify the name of the Spring bean definition associated
     * with this Configuration class. If left unspecified (the common case),
     * a bean name will be automatically generated.
     * <p>The custom name applies only if the Configuration class is picked up via
     * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
     * If the Configuration class is registered as a traditional XML bean definition,
     * the name/id of the bean element will take precedence.
     * @return the suggested component name, if any (or empty String otherwise)
     * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
}

您会注意到它们是相同的(它们都包含更通用的@Component注释)。因此,通过看到这一事实来同时使用它们既没有意义。另一件事,更重要的是,spring正在尝试赋予这些应该描述用途的注释的标签含义。

Configuration用于在启动阶段连接到应用程序的必要部分,以使其正常运行。

Controller用于定义一个类,该类充当与外界的接口,即:其他参与者如何使用您的应用程序。

如您所见,将这两个一起使用几乎没有什么意义。

答案 2 :(得分:5)

看看Spring Boot文档typical layout

还有本文SOLID Programming Principles

并查看Spring Boot指南Spring Boot Thymeleaf(不需要@Bean配置)

您应该用两个词分开
 1. MyThymeleafConfig配置
 2. TemplateControllerviewTemplates()和另一个端点

答案 3 :(得分:5)

要重构它们,将@Bean方法分离到单独的@Configuration类是简单而直接的:

@Configuration
// @Controller  is redundant as we have @Configuration
// @EnableWebMvc is also redundant since you already annotate it in other class
// @ApplicationScope is also redundant since you do not need to create bean of MyThymeleafConfig anymore
public class MyThymeleafConfig {
    /*

    configuration for thymeleaf and template processing

    */

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(thymeleafTemplateResolver());
        return templateEngine;
    }

    @Bean
    public SpringResourceTemplateResolver thymeleafTemplateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("classpath:");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheable(false);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    }
}

@Configuration类返回相同的bean实例,无论您调用bean方法的次数是多少。

现在在您的控制器中:

@Controller
public class MyThymeleafConfig {

    @Autowired
    private SpringTemplateEngine templateEngine;

    @GetMapping("/view-template")
    @ResponseBody
    public void viewTemplates() {

        Context context = new Context();
        context.setVariable("mydata", "this is it");

        String html = templateEngine.process("templates/view-to-process.html", context);
        System.out.println(html);
    }
}

但是说实话,我不知道为什么您必须手动与TemplateEngine / SpringTemplateEngine交互,因为Spring-thymeleaf将自动为您处理具有给定变量的模板。 (例如@sedooe示例)

答案 4 :(得分:2)

不要将请求映射放入配置类中,这违反了关注点分离的原则。您可以采用以下方法。

所有应用程序级bean均在类路径根目录中的Application类中设置。由于Application类具有应用程序作用域,因此Application类也是最好的配置,同时具有百里香静态资源配置。

@SpringBootApplication
@EnableWebMvc
public class Application{

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public ViewResolver viewResolver() {
       ThymeleafViewResolver resolver = new ThymeleafViewResolver();
       resolver.setTemplateEngine(templateEngine());
       resolver.setCharacterEncoding("UTF-8");
       resolver.setCache(false);
       return resolver;
    }

   @Bean
   public TemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setEnableSpringELCompiler(true);
        templateEngine.addDialect(new LayoutDialect());
        templateEngine.addDialect(new Java8TimeDialect());
        templateEngine.setTemplateResolver(templateResolver());
        return templateEngine;
   }

   private ITemplateResolver templateResolver() {
       SpringResourceTemplateResolver resolver = new 
            SpringResourceTemplateResolver();
       resolver.setApplicationContext(applicationContext);
       resolver.setPrefix("classpath:/templates/");
       resolver.setTemplateMode(TemplateMode.HTML);
       return resolver;
   }
}

如果您将静态资源放在类路径中名为 static public 的文件夹中,则springboot会将其标识为静态资源的位置。然后,您无需重写addResourceHandlers方法。如果确实要执行此操作,则可以在扩展WebMvcConfigurerAdapter的Application类中执行此操作。您不需要单独的类即可仅配置静态资源路径。

不要将请求映射放在配置类中,而是将它们放在单独的控制器类中,例如:

@Controller
public class MyController {
   @GetMapping("/view-template")
   @ResponseBody
   public void viewTemplates() {
         Context context = new Context();
         context.setVariable("mydata", "this is it");

         String html = templateEngine().process("templates/view-to-process.html", context);
         System.out.println(html);
    }
}

当然,springboot允许您以自己喜欢的方式进行操作,但最好还是采用一般方法。

答案 5 :(得分:0)

这两个注释用于不同的事物,因此最好不要在同一类上使用它们。因为它违反了Separation of Concerns主体。