Статьи

Внедрение компонентов Spring в неуправляемые объекты

Преимущества, возникающие в результате внедрения зависимости, могут вызывать зависимость. Настроить структуру приложения с помощью инъекций намного проще, чем делать все разрешения вручную. Трудно отказаться от него, когда у нас есть некоторые неуправляемые классы, которые создаются вне контейнера, например, являясь частью других сред, таких как компоненты пользовательского интерфейса Vaadin или сущности JPA. Последние особенно важны, когда мы используем Domain Driven Design . Во время обучения DDD, проводимого Slawek Sobotka, мы говорили о вариантах устранения «плохой связи» с агрегатных заводов. Я уверен, что вы признаете, что лучше иметь универсальный механизм, способный «возбудить» объекты с помощью зависимостей, определенных, например, аннотацией @Inject, чем внедрить все необходимые зависимости в конкретную фабрику и затем передать их в объект с помощью конструктора, конструктора или простого сеттера.

Spring Framework предоставляет нам два разных решения для достижения такого требования. Теперь я опишу их обоих. Давайте начнем с более простого.

Это особенно полезно, когда у нас есть такой случай, как универсальный репозиторий, упомянутая ранее агрегатная фабрика или даже любая другая фабрика, просто гарантирующая, что в нашем коде есть только несколько мест, где будут создаваться экземпляры объектов вне контейнера. В этом случае мы можем использовать класс AutowireCapableBeanFactory . В частности нас будут интересовать два метода:

  • void autowireBean (Объект существующийБин)
  • Объект initializeBean (Объект существующийBean, String beanName)

Первый просто заполняет наш компонент без применения определенных постпроцессоров (например, @PostConstruct и т. Д.). Второй дополнительно применяет фабричные обратные вызовы, такие как setBeanName и setBeanFactory, а также любые другие постпроцессоры с @PostConstruct, конечно. В нашем коде это будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
public abstract class GenericFactory<T> {
 
  @Autowired
  private AutowireCapableBeanFactory autowireBeanFactory;
 
  public T createBean() {
    // creation logic
    autowireBeanFactory.autowireBean(createdBean);
    return createdBean;
  }
}

Простая и мощная — моя любимая композиция.

Но что мы можем сделать, когда в нашем коде много мест, где рождаются объекты? Это, например, случай создания макетов Vaadin в веб-приложении Spring. Внедрение пользовательских объектов-конфигураторов bean-компонентов, вызывающих метод autowireBean, не будет пиковой производительностью. К счастью, разработчики Spring принесли нам аннотацию @Configurable . Эта аннотация, связанная с аспектами, настроит каждый аннотированный объект, даже если мы создадим его вне контейнера с помощью оператора new. Как и с любыми другими аспектами, мы можем выбирать между

  • время загрузки (LTW)
  • время от времени (BTW).

Первый проще в настройке, но мы становимся (из-за инструментовки) зависимыми от сервера приложений, что может быть нежелательно в некоторых случаях. Чтобы использовать его, нам нужно аннотировать наш класс @Configuration с помощью @EnableLoadTimeWeaving (или добавить тег <context: load-time-weaver />, если вам нравятся настройки Flintstones и XML). После завершения настройки просто аннотируйте класс с помощью @Configurable:

1
2
3
4
5
6
7
8
@Configurable
public class MyCustomButton extends Button {
 
  @Autowired
  private MyAwesomeService myAwesomeService;
 
  // handlers making use of injected service
}

Второй вариант немного сложнее в настройке, но после этого он будет намного легче во время выполнения. Поскольку теперь мы хотим загружать аспекты до компиляции, мы должны интегрировать компилятор aspectj в нашу сборку. В Maven вам нужно добавить несколько зависимостей:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.7.3</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>3.2.4.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>3.2.4.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.persistence</groupId>
  <artifactId>persistence-api</artifactId>
  <version>1.0</version>
  <scope>provided</scope>
</dependency>

Я надеюсь, что вам любопытно, почему выше вы можете увидеть постоянство-API. Это было также странно для меня, когда я увидел ошибку «не могу определить аннотации отсутствующего типа javax.persistence.Entity» во время компиляции aspectj. Ответ можно найти в SpringFramework JIRA в выпуске SPR-6819 . Это происходит, когда вы настраиваете Spring-аспекты как aspectLibrary в 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
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>1.5</version>
  <configuration>
    <source>1.7</source>
    <target>1.7</target>
    <complianceLevel>1.7</complianceLevel>
    <showWeaveInfo>true</showWeaveInfo>
    <aspectLibraries>
      <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
      </aspectLibrary>
    </aspectLibraries>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

И это все, ребята!

Ссылка: Внедрение компонентов Spring в неуправляемые объекты от нашего партнера по JCG Якуба Кубрински из блога Java (B) Log .