Статьи

Начальная загрузка веб-приложения с помощью Spring 3.1 и конфигурации на основе Java, часть 1

1. Обзор

Это первая из серии публикаций о настройке веб-приложения RESTfull с использованием Spring 3.1 с настройкой на основе Java. Эта статья будет посвящена начальной загрузке веб-приложения и обсуждению того, как перейти с XML на Java без полной миграции всей конфигурации XML.

2. Maven pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schem	aLocation=
   "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org</groupId>
   <artifactId>rest</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>war</packaging>

   <dependencies>
      
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>${spring.version}</version>
         <exclusions>
            <exclusion>
               <artifactId>commons-logging</artifactId>
               <groupId>commons-logging</groupId>
            </exclusion>
         </exclusions>
      </dependency>
      <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib-nodep</artifactId>
         <version>${cglib.version}</version>
         <scope>runtime</scope>
      </dependency>
      
   </dependencies>

   <build>
      <finalName>rest</finalName>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
               <source>1.6</source>
               <target>1.6</target>
               <encoding>UTF-8</encoding>
            </configuration>
         </plugin>
      </plugins>
   </build>
   
   <repositories>
      <repository>
         <id>spring-maven-snapshot</id>
         <name>Springframework Maven Snapshot Repository</name>
         <url>http://maven.springframework.org/snapshot</url>
         <snapshots>
            <enabled>true</enabled>
         </snapshots>
      </repository>
   </repositories>

   <properties>
      <spring.version>3.2.2.RELEASE</spring.version>
      <cglib.version>2.2.2</cglib.version>
   </properties>

</project>

2.1. Обоснование зависимости cglib

Вы можете задаться вопросом, почему cglib является зависимостью — оказывается, есть веская причина для ее включения — вся конфигурация не может функционировать без нее. Если удалить, Spring бросит:

Причина: java.lang.IllegalStateException: CGLIB требуется для обработки классов @Configuration. Либо добавьте CGLIB в путь к классам, либо удалите следующие определения bean-компонента @Configuration

Причина, по которой это происходит, объясняется тем, как Spring работает с классами @Configuration . Эти классы фактически являются компонентами, и поэтому им необходимо знать контекст и уважать область действия и другую семантику компонентов. Это достигается путем динамического создания прокси-сервера cglib с этой осведомленностью для каждого класса @Configuration , отсюда и зависимость cglib.

Кроме того, из-за этого существует несколько ограничений для аннотированных классов Configuration :

  • Классы конфигурации не должны быть окончательными
  • У них должен быть конструктор без аргументов

2.2. CGLIB зависимость весной 3.2

Начиная с Spring 3.2, больше не нужно добавлять cglib в качестве явной зависимости. Это связано с тем, что в Spring сейчас встроен cglib, что обеспечит полную функциональность прокси на основе классов.

Новый код cglib находится в пакете Spring: org.springframework.cglib (заменив исходный файл net.sf.cglib ). Причина изменения пакета состоит в том, чтобы избежать конфликтов с любыми версиями cglib, уже существующими в пути к классам.

Кроме того, теперь используется новый cglib 3.0, обновленный с более старой зависимости 2.2 ( для получения дополнительной информации см. Эту проблему JIRA ).

3. Веб-конфигурация на основе Java

@Configuration
@ImportResource( { "classpath*:/rest_config.xml" } )
@ComponentScan( basePackages = "org.rest", excludeFilters = { @Filter( Configuration.class ) } )
public class WebConfig{
   
   @Bean
   public PropertyPlaceholderConfigurer properties(){
      PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
      		Resource[] resources = new ClassPathResource[ ] {
         new ClassPathResource( "persistence.properties" ),
         new ClassPathResource( "restfull.properties" )
      };
      ppc.setLocations( resources );
      ppc.setIgnoreUnresolvablePlaceholders( true );
      return ppc;
   }
}	

Во-первых, аннотация @Configuration — это основной артефакт, используемый конфигурацией Spring на основе Java; он сам мета-аннотируется с помощью @Component , что делает аннотированные классы стандартными компонентами и, следовательно , также кандидатами на сканирование компонентов. Основное назначение классов @Configuration — быть источниками определений бинов для контейнера Spring IoC. Для более подробного описания смотрите официальные документы .

Затем @ImportResource используется для импорта существующей конфигурации Spring на основе XML. Это может быть конфигурация, которая все еще переносится из XML в Java, или просто устаревшая конфигурация, которую вы хотите сохранить. В любом случае, его импорт в Контейнер необходим для успешной миграции, позволяя выполнять небольшие шаги без особого риска. Эквивалентная аннотация XML, которая заменяется:

<import resource = ”classpath *: / rest_config.xml” />

Переход к @ComponentScan — это настраивает директиву сканирования компонента, эффективно заменяя XML:

<context: component-scan base-package = ”org.rest” />

Классы конфигурации фильтруются / исключаются из сканирования компонентов; это связано с тем, что они уже определены и используются Контейнером, что позволяет повторно открыть их и ввести в контекст Spring, что приведет к следующей ошибке:

Вызывается: org.springframework.context.annotation.ConflictingBeanDefinitionException: указанное в аннотации имя компонента ‘webConfig’ для класса компонента [org.rest.spring.WebConfig] конфликтует с существующим несовместимым определением компонента с тем же именем и классом [org. rest.spring.WebConfig]

И, наконец, использование аннотации @Bean для настройки поддержки свойствPropertyPlaceholderConfigurer инициализируется в аннотированном методе @Bean , указывая, что он создаст bean-компонент Spring, управляемый контейнером. Эта новая конфигурация заменила следующий XML:

<context: property-placeholder
location = ”classpath: persistence.properties, classpath: restfull.properties”
ignore-unresolvable = ”true” />

3.1. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id="WebApp_ID" version="3.0">
   <display-name>rest</display-name>
   
   <servlet>
      <servlet-name>rest</servlet-name>
      <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <init-param>
         <param-name>contextClass</param-name>
         <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
         </param-value>
      </init-param>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>org.rest.spring.WebConfig</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>
   
   <servlet-mapping>
      <servlet-name>rest</servlet-name>
      <url-pattern>/api/*</url-pattern>
   </servlet-mapping>
   
   <welcome-file-list>
      <welcome-file />
   </welcome-file-list>
</web-app>

Во-первых, DispatcherServlet настроен для использования AnnotationConfigWebApplicationContext вместо стандартного XmlWebApplicationContext . Более новый AnnotationConfigWebApplicationContext принимает аннотированные классы @Configuration в качестве входных данных для конфигурации контейнера и необходим для настройки контекста на основе Java.

В отличие от XmlWebApplicationContext , он не предполагает расположения классов конфигурации по умолчанию, поэтому для сервлета должен быть задан init-параметр contextConfigLocation . Это будет указывать на полное имя (имена) конфигурации Spring на основе Java.

Кроме этого, web.xml на самом деле не меняется с XML на конфигурацию на основе Java.

4. Вывод

Представленный подход обеспечивает плавную миграцию конфигурации Spring из XML в Java, смешивая старое и новое. Это важно для более старых проектов, которые могут иметь множество конфигураций на основе XML, которые нельзя перенести сразу. Таким образом, web.xml и начальная загрузка приложения являются первым шагом в миграции, после чего оставшиеся компоненты XML можно переносить небольшими порциями.

Во второй части я расскажу о конфигурации MVC и о том, как настроить RESTful API с полезной нагрузкой JSON только с конфигурацией на основе Java. А пока вы можете проверить проект github .