Разделить конфигурацию Spring по модулям

Я пытаюсь разбить работоспособное весеннее приложение на логически разделенные модули. Давайте упростим условие и представим, что у нас есть только один модуль BusinessAppConf (только для целей взаимодействия с пользователем через WEB) и основной инициализатор контекста приложения.

Это работоспособная схема, но с использованием статических методов для регистрации классов конфигурации, связанных с бизнес-модулем.

WebApplicationInitializer (web.xml был заменен реализацией этого интерфейса)

public class AppInitializer implements WebApplicationInitializer {

    @Override
    @Autowired
    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext context = getContext();
        container.addListener(new ContextLoaderListener(context));

        ServletRegistration.Dynamic mainDispatcher =
                container.addServlet("dispatcher", new DispatcherServlet(context));
        ServletRegistration.Dynamic businessDispatcher =
                container.addServlet("businessDispatcher", BusinessAppConfig.createDispatcherServlet(context));
        ServletRegistration.Dynamic ppaDispatcher =
                container.addServlet("ppaDispatcher", PpaAppConfig.createDispatcherServlet(context));

        initDispatcher(mainDispatcher, 1, "/");
        initDispatcher(businessDispatcher, 2, "/business");
        initDispatcher(businessDispatcher, 3, "/ppa");
    }

    private void initDispatcher(ServletRegistration.Dynamic dispatcher, int loadOnStartUp, String mapping) {
        if (dispatcher == null) {
            System.out.println("Servlet" + dispatcher.getName() + " is already added");
        } else {
            dispatcher.setLoadOnStartup(loadOnStartUp);
            dispatcher.addMapping(mapping);
        }
    }

    public AnnotationConfigWebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(MvcConfiguration.class);
        return context;
    }

    @Bean(name = "propertyConfigurer")
    public PropertySourcesPlaceholderConfigurer getPropertyPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        placeholderConfigurer.setLocation(new ClassPathResource("common.properties"));
        placeholderConfigurer.setLocation(new ClassPathResource("amazon.S3Storage.properties"));
        placeholderConfigurer.setLocation(new ClassPathResource("local.storage.properties"));
        placeholderConfigurer.setLocation(new ClassPathResource("log4j.properties"));
        placeholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
        return placeholderConfigurer;
    }
}

Модуль Business Config со статическими методами:

Файл класса BusinessAppConfig

@Configuration
public class BusinessAppConfig {

    public static Servlet createDispatcherServlet(AnnotationConfigWebApplicationContext context) {
        context.register(BusinessMvcConfig.class);
        return new DispatcherServlet(context);
    }
}

а также

Конфигурация класса BusinessMvcConfig

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(basePackages = {"business"})
@EnableWebMvc
public class BusinessMvcConfig extends WebMvcConfigurationSupport {

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

    @Bean(name = "multipartResolver")
    public CommonsMultipartResolver multipartResolver() {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setMaxUploadSize(500000000L);
        return multipartResolver;
    }
}

BusinessHibernateConfig.class

@Configuration
@EnableTransactionManagement
//@EnableJpaRepositories(basePackages = {"business.dao", "business.model", "ppa.dao", "ppa.model"})
@PropertySource("classpath:rdbmsDev.properties")
public class BusinessHibernateConfig {

@Autowired
private Environment env;

@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException, IOException {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();

    Properties createStrategy = new Properties();
    createStrategy.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));

    emf.setJpaProperties(createStrategy);
    emf.setPackagesToScan("business");
    emf.setJpaVendorAdapter(getJpaVendorAdapter());
    BasicDataSource dataSource = getDataSource();
    emf.setDataSource(dataSource);

    return emf;
}

@Bean
public DatabasePopulator createDatabasePopulator(BasicDataSource dataSource) {
    ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
    databasePopulator.setContinueOnError(false);
    databasePopulator.addScript(new ClassPathResource("create.sql"));
    DatabasePopulatorUtils.execute(databasePopulator, dataSource);
    return databasePopulator;
}

private JpaVendorAdapter getJpaVendorAdapter() {
    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
    jpaVendorAdapter.setShowSql(true);
    jpaVendorAdapter.setGenerateDdl(false);
    jpaVendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
    return jpaVendorAdapter;
}

@Bean
public BasicDataSource getDataSource() {
    BasicDataSource basicDataSource = new BasicDataSource();
    basicDataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
    basicDataSource.setUsername(env.getProperty("jdbc.username"));
    basicDataSource.setPassword(env.getProperty("jdbc.password"));
    basicDataSource.setUrl(env.getProperty("jdbc.url"));
    basicDataSource.setInitialSize(Ints.tryParse(env.getProperty("connection.init_size")));
    basicDataSource.setMaxIdle(Ints.tryParse(env.getProperty("connection.pool_size")));
    return basicDataSource;
}

@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    return new JpaTransactionManager(entityManagerFactory);
}

}

Мне не нравится, что здесь используются статические методы. Вроде некорректные реализации. Итак, следующий вопрос: правильно ли настроены эти файлы? Если нет, как это должно быть настроено?

Полный проект здесь github.com/BessonovEvgeniy/Octava

Evgeniy 08.04.2018 23:10
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
130
1

Ответы 1

Используйте AbstractContextLoaderInitializer вместо WebApplicationInitializer, потому что

  • он регистрирует для вас ContextLoaderListener
  • вы можете использовать свой код для регистрации диспетчерского сервлета или метода onStartu
  • вы можете переопределить метод createRootApplicationContext для создания корневого контекста приложения, который содержит бизнес-сервисы, и он будет предоставлен ContextLoaderListener, в вашем случае это BusinessAppConfig

Привет, спасибо за вашу рекомендацию. Я пробовал этот подход, и у меня возникло непонимание того, как его следует использовать. У вас есть пример такой реализации? Как использовать AbstractContextLoaderInitializer Если мне нужно: 1) добавить новый сервлет для каждого отдельного модуля (в нашем случае это единственный бизнес-модуль) и 2) зарегистрировать все, что связано с настроенными классами бизнес-модуля (BusinessHibernateConf.class, BusinessMvcConfig.class) в одном конкретном месте?

Evgeniy 14.04.2018 23:43

Другие вопросы по теме