Почти все наши проекты имеют постоянный уровень: реляционная база данных, хранилища документов или просто файлы XML . И обычно вы будете использовать шаблон DAO для реализации абстрактного интерфейса между вашими бизнес-объектами и хранилищем данных.
В этом посте я собираюсь объяснить другой шаблон, который можно использовать вместо шаблона DAO . Шаблон активной записи — это архитектурный шаблон, который вынуждает вас выполнять CRUD- операции в вашем классе модели, поэтому сам класс модели отвечает за сохранение, удаление и загрузку из базы данных.
Существует много стратегий для реализации этого шаблона, но для меня лучшая из них — использование Аспектно-ориентированного программирования , потому что мы по-прежнему поддерживаем разделение задач, предпочитая изолированное модульное тестирование, а не нарушая инкапсуляцию.
Аспектно-ориентированное программирование влечет за собой разбиение логики программы на отдельные части. Эти части известны как сквозные задачи, потому что они « пересекают » несколько абстракций в программе. Примером сквозных проблем может быть ведение журнала, менеджер транзакций, менеджер ошибок или разбиение больших наборов данных . Для людей, которые работали с аспектами, которые не слишком секретны, чтобы использовать их, вы просто создаете аспект, определяющий рекомендации и точку отсчета, и ваш аспект готов к выполнению.
Я предполагаю, что большинство из нас использует аспектно-ориентированное программирование, как я описал в предыдущем параграфе, но будет меньше тех, кто использует функцию ITD (Inter-type объявления) .
Объявления между типами предоставляют способ выразить сквозные проблемы, влияющие на структуру модулей, позволяя программистам объявлять члены другого класса.
Как мы говорим в моей стране « плохо сказано, но хорошо понято », ITD — это способ объявления новых компонентов ( атрибутов, методов, аннотаций ) класса с точки зрения.
AspectJ является аспектно-ориентированным расширением для Java. AspectJ поддерживает ITD , и по этой причине будет использоваться в этом посте. Более того, я рекомендую вам установить плагин AJDT, потому что он поможет вам разработать аспекты и получить краткий обзор того, какие классы Java относятся к различным аспектам.
Если вы не поняли, что такое ITD , не беспокойтесь, это типичный пример концепции, который лучше всего понять на примере.
Давайте начнем с простого примера:
Представьте себе необходимость моделировать автомобиль. У вас будет класс автомобиля с некоторыми атрибутами, для этого примера достаточно трех атрибутов (номер вин , пройденные мили и модель ).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public class Car { public void setVin(String vin) { this .vin = vin;} public String getVin() { return this .vin;} private String vin; public void setMileNum( int mileNum) { this .mileNum = mileNum;} public int getMileNum() { return this .mileNum;} private int mileNum; public void setModel(String model) { this .model = model;} public String getModel() { return this .model;} private String model; } |
Это POJO с тремя атрибутами и их получателями и установщиками .
Теперь мы хотим добавить постоянный слой, но в этом случае мы собираемся сохранить наши POJO в XML- файле вместо базы данных. Таким образом, объекты Car должны быть преобразованы в поток XML . Для этого будут использоваться аннотации JAXB . Для тех, кто не знает, JAXB позволяет разработчикам отображать классы Java в представления XML и наоборот.
Я уверен, что первая идея, которая приходит в голову, — это аннотировать класс Car с помощью @XmlRootElement (аннотация для отображения корневого элемента в JAXB ). Не делайте этого, используйте аспекты . Ваша первая миссия — сохранить файл Car как можно проще. Чтобы добавить аннотацию с использованием ITD , достаточно просто:
1
2
3
4
|
public aspect Car_Jaxb { declare @type : Car: @XmlRootElement ; } |
С @type вы выставляете аннотацию к члену. В этом случае только класс. Другие возможности: @method , @constructor и @field . Затем элементы шаблона, которые должны быть аннотированы, в данном случае классом Car , но вы можете использовать любые регулярные выражения, например, o rg.alexsotob .. *. Наконец аннотация .
Следующий шаг — использование классов JAXB для маршалинга / демаршаллинга объектов. В этом примере я использую пакет spring-oxm , и вкратце вы поймете почему. Spring-oxm является частью Spring- Core, который содержит классы для работы с O / X Mapping .
Этот весенний модуль содержит один класс для каждой поддерживаемой привязки Xml . В нашем случае Jaxb2Marshaller используется как маршаллер и демаршаллер.
Возможно, вы думаете о создании класса обслуживания, в который вы добавляете экземпляр Jaxb2Marshaller . Этот сервис будет включать два метода (сохранить и загрузить) с классом Car в качестве аргумента или возвращаемого значения. Извините, но, делая это, вы реализуете шаблон DAO . Давайте реализуем шаблонный подход Active Record . И, как вы можете предположить, aspectj спасает вас, чтобы избежать смешивания понятий в одном и том же исходном файле.
Давайте обновим предыдущий файл аспектов, чтобы вся необходимая логика JAXB была в одном файле.
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
|
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.oxm.jaxb.Jaxb2Marshaller; public aspect Car_Jaxb { declare @type : Car: @XmlRootElement ; @Autowired transient Jaxb2Marshaller Car.marshaller; public void Car.save(OutputStream outputStream) throws IOException { this .marshaller.marshal( this , new StreamResult(outputStream)); } public Car Car.load(InputStream inputStream) throws IOException { return (Car) this .marshaller.unmarshal( new StreamSource(inputStream)); } } |
Обратите внимание, что помимо аннотирования класса Car мы создаем два метода и аннотированный атрибут. Атрибуты должны следовать тому же правилу, что и методы: < имя класса > точка (.) И < имя атрибута >. Обратите внимание, что в этом случае атрибут является временным, поскольку не должен быть связан в файле XML .
Последний шаг — настройка маршаллера в весеннем контекстном файле.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> < oxm:jaxb2-marshaller id = "marshaller" > < oxm:class-to-be-bound name = "org.alexsotob.itd.Car" /> </ oxm:jaxb2-marshaller > </ beans > |
Не большой секрет. Теперь давайте закодируем юнит-тест.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations= "/context.xml" ) public class CarOxmBehaviour { @Test public void shouldSaveCarToXml() throws Exception { //Given Car car = new Car(); car.setMileNum( 1000 ); car.setModel( "Ferrari" ); //When ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); car.save(byteArrayOutputStream); //Then String expectedMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><car><mileNum>1000</mileNum><model>Ferrari</model><vin>1M8GDM9AXKP042788</vin></car>" ; String xmlMessage = byteArrayOutputStream.toString( "UTF-8" ); assertThat(the(xmlMessage), isEquivalentTo(the(expectedMessage))); } } |
Запустите класс junit и BOOM все красные, с удивительным NullPointerException . Marshaller создается в контексте Spring , но не внедряется в класс Car (Car не управляется контейнером Spring , поэтому его невозможно внедрить). А теперь, я полагаю, вы говорите себе: « Я говорил вам, что уровень обслуживания будет лучше, потому что он будет управляться Spring, а autowired будет работать отлично ». Но подожди и посмотри. Как насчет использования модуля Spring-аспекты? Spring Aspect содержит аспект, управляемый аннотациями ( @Configurable ), позволяющий внедрять зависимости любого объекта, независимо от того, контролируется ли он контейнером или нет. Итак, давайте применим два последних изменения, и приложение запустится.
Прежде всего, создается новый файл aspectj для аннотирования класса Car как Configurable .
1
2
3
4
5
6
7
|
import org.springframework.beans.factory.annotation.Configurable; public aspect Car_Configurable { declare @type : Car: @Configurable ; } |
И, наконец, измените контекстный файл Spring, чтобы разрешить аннотацию @Configurable .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> < oxm:jaxb2-marshaller id = "marshaller" > < oxm:class-to-be-bound name = "org.alexsotob.itd.Car" /> </ oxm:jaxb2-marshaller > < context:spring-configured ></ context:spring-configured > </ beans > |
Достаточно добавить пространство имен < context: spring-configure > </ context: spring-configure >. В результате, всякий раз, когда вы создаете экземпляр объекта (с помощью ключевого слова « new »), Spring будет пытаться выполнить внедрение зависимостей для этого объекта.
Теперь снова запустите юнит-тест, и зеленый цвет проникнет в ваш компьютер: D
ITD — действительно хорошее решение для проектирования классов со своими обязанностями. Это дает вам возможность писать поддерживаемый и понятный код без потери инкапсуляции. Конечно, вы должны позаботиться о том, чтобы не иметь высокой связи в аспектных классах, и конвертировать их в «Бог-классы».
Обратите внимание, что реализуя тот же подход, но используя реляционную базу данных, это так же просто, как изменить Jaxb2Marshaller на EntityManager .
Я желаю, чтобы вы нашли этот пост полезным.
Справка: внедрение шаблона активной записи с помощью Spring AOP от нашего партнера JCG