Статьи

Настраиваемая магия весны

В Spring Framework есть несколько модулей, которые предоставляют ряд сервисов, многие из которых могут работать только для управляемых объектов (Spring Beans). Некоторыми примерами этих сервисов являются Внедрение зависимостей, Управление транзакциями, Сервисы AOP и т. Д. Все хорошо, когда мы используем Объекты являются сервисами, поэтому Spring управляет ими с определенной областью действия. Но иногда нам нужны наши доменные объекты, имеющие эти сервисы. Обычно доменные объекты создаются с новым ключевым словом, поэтому ими нельзя управлять по умолчанию с помощью Spring.

В моем предыдущем посте ( Как использовать Events в Spring 3.x ) у нас был объект домена с именем Order. для развязки между объектами мы использовали события. Но только управляемые bean-компоненты могут вызывать событие в Spring Framework (вероятно, у каждого Framework, который вы знаете и имеете эту функцию).

Весной введем аннотацию с именем Configurable. Использование этой аннотации на наших доменных объектах делает их управляемыми весной.
Но как это работает: Конфигурируемый для его цели нужен AspectJ Compiler, ваш класс нуждается в улучшении байт-кода во время компиляции или во время загрузки, пока не сможет удовлетворить ваши требования.

Я хочу привести вам простой пример о том, как настроить и использовать настраиваемую мощность в ваших приложениях. Хорошей практикой является наличие объекта среды, чтобы вся система могла получить доступ к его свойствам для получения информации о системе. Например, нам нужно знать текущее время системы, простое решение — использовать Calendar.getInstance().getTime() или new Date()

но есть некоторые дефекты, ваш код не будет тестироваться на части, которые вам нужны для проверки даты проверки (я напишу серию тестовых и тестируемых кодов после боя как можно скорее).

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

Поэтому важно иметь механизм для этих требований. В качестве простого решения в этом примере я создам интерфейс среды с одним методом (getCurrentTime ()). Везде в коде, если мне нужно время системы, я буду использовать этот метод. Интерфейс среды должен быть введен в мои объекты, пока я не смогу счастливо использовать этот метод. У бинов Spring нет проблем с использованием Environment, но в объектах нашего домена мы должны использовать Configurable Annotation.

Если вы используете Maven, вам нужно будет добавить эти зависимости в ваш pom:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.1.RELEASE</version>
 <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.6.8</version>
</dependency>

Для компиляции приложения с помощью maven вы можете использовать следующую конфигурацию Aspectj-maven-plugin:

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
<build>
 <plugins>
 <plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>aspectj-maven-plugin</artifactId>
 <version>1.4</version>
 <configuration>
 <showWeaveInfo>true</showWeaveInfo>
 <source>1.6</source>
 <target>1.6</target>
 <Xlint>ignore</Xlint>
 <complianceLevel>1.6</complianceLevel>
 <encoding>UTF-8</encoding>
 <verbose>false</verbose>
 <aspectLibraries>
 <aspectLibrary>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aspects</artifactId>
 </aspectLibrary>
 </aspectLibraries>
 </configuration>
 <executions>
 <execution>
 <goals>
 <goal>compile</goal>
 <goal>test-compile</goal>
 </goals>
 </execution>
 </executions>
 <dependencies>
 <dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjrt</artifactId>
 <version>1.6.8</version>
 </dependency>
 <dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjtools</artifactId>
 <version>1.6.11</version>
 </dependency>
 </dependencies>
 </plugin>
 </plugins>
 </build>

Если вы используете IDE для компиляции своего кода, не забудьте изменить его компилятор на AspectJ. Вы можете найти его в своем локальном репозитории maven в директории aspectjrt.

Предположим, что есть класс Product, и мы хотели бы знать дату его создания и дату его продажи.

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
@Configurable(preConstruction = true)
 public class Product {
 private final String name;
 private final String description;
 private final Date createDate;
 private Status status;
 private Date saleDate;
 @Autowired
 private Environment environment;
  
public Product(String name, String description) {
 this.name = name;
 this.description = description;
 this.status = Status.PENDING;
 this.createDate = environment.getCurrentDate();
 }
  
public void sell() {
 this.status = Status.SALE;
 this.saleDate = environment.getCurrentDate();
 }
  
public Date getCreateDate() {
 return createDate;
 }
  
public Date getSaleDate() {
 return saleDate;
 }
  
public static enum Status {
 PENDING, SALE;
 }
 }

Product — очень простой класс, мы использовали preConstruction = true, потому что для построения нашего продукта необходимо использовать среду.

Среда и ее реализации тоже очень просты:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface Environment {
 Date getCurrentDate();
 }
  
public class DefaultEnvironment implements Environment {
 @Override
 public Date getCurrentDate() {
 return new Date();
 }
 }
  
public class MockEnvironment implements Environment {
 private Date date;
 @Override
 public Date getCurrentDate() {
 return this.date;
 }
  
public void setCurrentDate(Date date){
 this.date = date;
 }
  
}

MockEnvironment создается в тестовых пакетах, потому что нам нужен этот класс только в наших тестах. Вместо использования этого класса вы можете использовать некоторую фиктивную библиотеку как Mocktio и расширение для нее (Springockito). Но в этом примере мы сосредоточены не на них.

Наши тесты очень просты:

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
@ContextConfiguration({"classpath*:context.xml","classpath*:test-context.xml"})
 public class ProductTest extends AbstractJUnit4SpringContextTests {
 final Date time = Calendar.getInstance().getTime();
 @Autowired
 Environment environment;
  
@Before
 public void before() {
  
((MockEnvironment) this.environment).setCurrentDate(time);
 }
  
@Test
 public void created_product_should_have_current_environment_date() {
 final Product product = new Product("", "");
 Assert.assertEquals(time, product.getCreateDate());
  
}
  
@Test
 public void sell_should_set_createDate_to_now(){
 final Product product = new Product("", "");
 product.sell();
 Assert.assertEquals(time, product.getSaleDate());
  
}
 }

Вы можете скачать исходный код с: https://github.com/psycho-ir/Spring-Configurable.git

Ссылка: Spring Configurable Magic от нашего партнера JCG Сороша Сарабадани в блоге Just Another Java .