Annotated a class with @Configuration and @Controller. Need help in refactoring

前端 未结 6 609
一整个雨季
一整个雨季 2021-01-17 15:49

Below is my class in which i had to use both @Configuration and @Controller as there should be only one instance of Thymeleaf in the e

相关标签:
6条回答
  • 2021-01-17 16:08

    To refactor them, it's easy and straight forward to separate @Bean methods to a separate @Configuration class:

    @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 class return the same bean instance, regardless how many times you call the bean methods.

    Now in your controller:

    @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);
        }
    }
    

    But honestly, I don't know why you have to manually interact with TemplateEngine / SpringTemplateEngine, since Spring-thymeleaf will automatically process the template with given variable for you. (Like @sedooe example)

    0 讨论(0)
  • 2021-01-17 16:09

    These two annotations are for different things, thus it's better to not use them on the same class. Cause it's against Separation of Concerns principal.

    0 讨论(0)
  • 2021-01-17 16:10

    Take look at Spring Boot documentation typical layout

    Also this article SOLID Programming Principles

    And look at Spring Boot guide Spring Boot Thymeleaf (you don't need your @Bean configurations)

    In two words you should separate
    1. MyThymeleafConfig configuration
    2. TemplateController with viewTemplates() and another endpoints

    0 讨论(0)
  • 2021-01-17 16:17

    Assuming you're using Spring Boot, since you have it in tags, you do not need any configuration to use Thymeleaf.

    By just having this dependency, you can:

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

    And it should work.

    By the way, yes, having @Configuration and @Controller in the same class is something you should never need.

    0 讨论(0)
  • 2021-01-17 16:20

    If you see the source codes of the annotations (Spring 5) you have:

    Controller

    @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 "";
    
    }
    

    Configuration

    @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 "";
    }
    

    you notice that they are the same (they both include the more generic @Component annotation). So it doesn't make sense to use them both by seeing this fact. Another thing, more important, is that spring is trying to give a sort of tags meaning of these annotations that should describe the use.

    The Configuration is used to wire in necessary parts to the application to function properly, at startup phase.

    The Controller is used to define a class which is serving as an interface to the outside world, i.e: how can other actors use your application.

    As you can see, it makes very little sense to use those 2 together.

    0 讨论(0)
  • 2021-01-17 16:22

    Don't put request mappings inside configuration classes, it violates the principal of separation of concerns. You can go for a approach like below.

    All the application wide beans are setup in Application class which is present in the root of the classpath. Application class in the best place to have your thymeleaf and static resource configurations too, since the Application class have application-scope.

    @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;
       }
    }
    

    If you put the static resources inside a folder named static or public in the classpath, springboot identify that as the location for static resources. Then you don't need to override addResourceHandlers method. If you really want to do it, you can do it inside the Application class extending WebMvcConfigurerAdapter. You don't need separate class to configure just static resource paths.

    Don't put request mappings inside configuration classes, put them in separate controller classes like:

    @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);
        }
    }
    

    Of cause, springboot allows you to do it the way you like, but you'd better stick to a general approach.

    0 讨论(0)
提交回复
热议问题