Статьи

Spring: сделайте вашу Java-конфигурацию более элегантной

Привет всем, я не писал новые статьи в течение длительного периода времени. Было накоплено много материалов, которые должны быть размещены в моем блоге в ближайшее время. Но теперь я хочу поговорить о конфигурациях приложений Spring MVC. Если быть более точным, я хочу поговорить о конфигурациях Spring на основе Java.

Несмотря на то, что конфигурация Spring на основе Java была представлена ​​в версии 3.0, многие разработчики все еще используют подход на основе XML. Лично я использую конфигурации на основе аннотаций, потому что они более удобны в управлении, разработке и обслуживании. Если вы читали мой блог раньше, вы могли заметить, что во всех примерах кода я использовал именно конфигурации на основе Java.

Не так давно я сделал обзор кода одного моего проекта. Я заметил, что что-то не так со структурой конфигураций. Два аспекта были плохими для меня:

  • Все бины были настроены в одном классе
  • Инициализация в контексте веб-приложения была слишком сложной

Вы можете увидеть эти два недостатка во всех моих примерах приложений Spring MVC. Например, вы можете открыть одно из моих последних руководств о сервисах Spring REST с CNVR . Взгляните там на два класса: WebAppConfig и Initializer .

WebAppConfig

Первый представляет первый пункт в этой статье. Определенно мне нужно сделать что-то, чтобы логически разделить конфигурацию бобов. Чтобы решить эту проблему, я решил сделать два шага:

  1. Переместить конфигурации БД в отдельный класс
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories("com.mobapp.repository")
    public class DataBaseConfig {
     
        private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
        private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
        private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
        private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
     
        private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
        private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
     
            @Resource
            private Environment env;
     
            @Bean
            public DataSource dataSource() {
                    DriverManagerDataSource dataSource = new DriverManagerDataSource();
     
                    dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
                    dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
                    dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
                    dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
     
                    return dataSource;
            }
     
            @Bean
            public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
                    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
                    entityManagerFactoryBean.setDataSource(dataSource());
                    entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
                    entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
     
                    entityManagerFactoryBean.setJpaProperties(hibProperties());
     
                    return entityManagerFactoryBean;
            }
     
            private Properties hibProperties() {
                    Properties properties = new Properties();
                    properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
                    properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
                    return properties;       
            }
     
            @Bean
            public JpaTransactionManager transactionManager() {
                    JpaTransactionManager transactionManager = new JpaTransactionManager();
                    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
                    return transactionManager;
            }
     
    }
  2. Преобразуйте класс WebAppConfig в основной класс конфигурации и назначьте ему оставшиеся классы конфигурации (в данном случае это был бы просто класс DataBaseConfig ) с помощью аннотации @Import .
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    @Configuration
    @EnableWebMvc
    @Import({DataBaseConfig.class})
    @ComponentScan("com.mobapp")
    @PropertySource("classpath:application.properties")
    public class WebAppConfig extends WebMvcConfigurerAdapter {
     
            @Resource
            private Environment env;
     
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
                    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
            }
     
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                    configurer.favorPathExtension(true)
                            .useJaf(false)
                            .ignoreAcceptHeader(true)
                            .mediaType("html", MediaType.TEXT_HTML)
                            .mediaType("json", MediaType.APPLICATION_JSON)
                            .defaultContentType(MediaType.TEXT_HTML);
            }
     
            @Bean
            public ViewResolver contentNegotiatingViewResolver(
                            ContentNegotiationManager manager) {
     
                    List< ViewResolver > resolvers = new ArrayList< ViewResolver >();
     
                    InternalResourceViewResolver r1 = new InternalResourceViewResolver();
                    r1.setPrefix("/WEB-INF/pages/");
                    r1.setSuffix(".jsp");
                    r1.setViewClass(JstlView.class);
                    resolvers.add(r1);
     
                    JsonViewResolver r2 = new JsonViewResolver();
                    resolvers.add(r2);
     
                    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
                    resolver.setViewResolvers(resolvers);
                    resolver.setContentNegotiationManager(manager);
             return resolver;
     
            }
     
            /**
            * View resolver for returning JSON in a view-based system. Always returns a
            * {@link MappingJacksonJsonView}.
            */
            public class JsonViewResolver implements ViewResolver {
                    public View resolveViewName(String viewName, Locale locale)
                                    throws Exception {
                                    MappingJacksonJsonView view = new MappingJacksonJsonView();
                                    view.setPrettyPrint(true);
                                    return view;
                    }
            }
     
    }

    Таким образом, вы можете разделить один большой класс конфигурации на несколько меньших, которые будут содержать специальные конфигурации для них.

Initializer

Код класса Initializer слишком многословен в примере, который я упомянул выше и дал ссылку на него. Я зарегистрировал там корневую конфигурацию веб-приложения, отображение и фильтр. Как я могу уменьшить количество строк кода? Ответ я получил, посмотрев на класс AbstractAnnotationConfigDispatcherServletInitializer . Посмотрите на класс, и вы заметите, что он реализует интерфейс WebApplicationInitializer , который я реализовал в моей предыдущей версии класса Initializer. Итак, вот новая версия инициализатора :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {
 
        @Override
        protected Class< ? >[] getRootConfigClasses() {
                return null;
        }
 
        @Override
        protected Class< ? >[] getServletConfigClasses() {
                return new Class< ? >[] { WebAppConfig.class };
        }
 
        @Override
        protected String[] getServletMappings() {
                return new String[] { "/" };
        }
 
        @Override
        protected Filter[] getServletFilters() {
                return new Filter[] { new HiddenHttpMethodFilter() };
        }
 
}

Благодаря этим двум шагам я сделал свои настройки для приложения Spring MVC более элегантными. Теперь вы тоже можете это сделать. Удачи