Статьи

Аудит Spring MVC Webapp с AspectJ. Часть 1

Если вы похожи на меня, то у вас будут такие дни программирования, когда кажется, что все идет очень хорошо. Вы пишете код и тесты, и это просто работает. И потом, и есть другие дни, действительно плохие, когда вы знаете, что все, что вы написали, настолько хорошо, насколько это возможно, и код отказывается работать: что-то явно не так, но вы не представляете, какие. У меня был один из таких дней, когда я писал код для этого блога. Идея состояла в том, чтобы продемонстрировать, как использовать Spring и Aspectj для аудита посещений пользователем экрана. 

Аудит посещений пользователем экрана — одна из немногих сквозных задач, которые очень хорошо решает Аспектно-ориентированное программирование (AOP). Идея моего демо-кода заключается в том, что вы добавляете аннотацию к соответствующим контроллерам, и каждый раз, когда пользователь посещает страницу, этот визит записывается. Используя эту технику, вы можете создать изображение самых популярных экранов и, следовательно, самых популярных функциональных блоков в вашем приложении. Знание этих подробностей облегчает решение о том, куда направить ваши усилия по разработке, поскольку разработка тех частей вашего приложения, которые вряд ли кто-либо когда-либо использует, не окупается.

Я уже говорил об AspectJ и AOP в следующих блогах, и это здорово, поскольку они демонстрируют основы, но они не являются реально работающим Spring MVC-приложением. Мои предыдущие блоги были:

На этот раз я подумал, что придумаю полнофункциональное приложение Spring MVC, использующее полезную сквозную задачу AOP. 

Для демонстрационного кода я создал простое приложение Spring MVC, которое имеет два экрана: домашняя страница и страница справки. Вдобавок к этому я создал простую аннотацию: @Audit, которая используется для обозначения контроллера как требующего аудита (не все из них будут, особенно если вы решите проверять функциональные точки, а не отдельные экраны) и сообщать объект совета — идентификатор экрана, как показано во фрагменте кода ниже:

@Audit("Home")@RequestMapping(value = "/", method = RequestMethod.GET)public String home(Locale locale, Model model) {

То есть, пока все не стало грушевидным …

Планом атаки было написать мою простую аннотацию @Audit и обработать ее с помощью простого класса AuditAdvice с методом, аннотированным аннотацией @Before Aspectj. Затем я бы притворился, что это реальный совет, что означало делегирование фактического аудита объекту AuditService с автопроводкой.

Я начал с создания примера приложения Spring MVC с использованием шаблона проекта Spring:

Затем я собрал весь код и ожидал, что он будет работать, за исключением того, что это не так: Spring не будет, независимо от того, что я пытался, автоматически связатьAuditService с классом AuditAdvice. Это означало, что когда вызывался мой аннотированный метод @Before, он вызывал исключение aNullPointerException

Когда вы сильно сомневаетесь, что ваш код верен, и он просто не будет работать, то одной из областей исследования является файл POM проекта. и настройка.

Дело в том, что когда вы используете чужой API, настройку проекта или другой инструмент, вы склонны доверять ему больше, чем своему собственному коду. Я предполагаю, что причина этого в том, что она обычно написана весьма уважаемой организацией, что заставляет вас думать, что у них есть какой-то волшебный способ написания действительно хорошего кода, плюс он обычно содержит целую кучу вещей, которые вы не делаете действительно понимаю.

Это действительно нерационально, поскольку API, инструмент, файл конфигурации и т. Д. Были написаны такими программистами, как вы и я, которые, вероятно, совершают столько же ошибок, сколько и мы.


Этот факт обычно беспокоит меня каждый раз, когда я вступаю в самолет, поскольку ошибка в программном обеспечении на высоте 30 000 футов — это не то, чего вы хотите.

Проблема с POM проекта Spring MVC состоит в том, что он немного устарел и полон вещей, которые вам просто не нужны для стандартного Java-приложения Spring MVC, плюс, нет никаких ссылок на какую-либо документацию по плагину, поэтому узнайте, что это все делает и что означают различные настройки сложно.

 Некоторое время назад
я написал блог под названием 
Dissecting Spring MVC Project POM , в котором пытался объяснить, как работает POM шаблонного приложения Spring MVC.

Это изменения, которые мне пришлось внести в стандартный POM-файл Spring MVC-шаблона, чтобы заставить мой код работать. 

1) Обновите версию Spring, используемую проектом, до последней: 3.2.3.RELEASE

2) Обновите версию AspectJ до 1.7.1

3) Удалите номер версии Spring Roo.

Это создаст следующие свойства версии:

<org.springframework-version>3.2.3.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.1</org.aspectj-version>

4) Удалите другие зависимости Spring Roo:

<!-- Roo dependencies -->
<dependency>
<groupId>org.springframework.roo</groupId>
<artifactId>org.springframework.roo.annotations</artifactId>
<version>${org.springframework.roo-version}</version>
<scope>provided</scope>
</dependency>

Это не проект Roo, и я ненавижу ненужные настройки.

5) Удалить ссылки на репозитории Spring:

<repositories>
<!-- For testing against latest Spring snapshots -->
<repository>
<id>org.springframework.maven.snapshot</id>
<name>Spring Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<!-- For developing against latest Spring milestones -->
<repository>
<id>org.springframework.maven.milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>

6) В этом примере используется имя файла WAR по умолчанию, поэтому удалите ссылку на плагин maven-war:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>abc</warName>
</configuration>
</plugin>

7) Обновите плагин Surefire, чтобы удалить ссылки Roo:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<junitArtifactName>junit:junit</junitArtifactName>
<!-- Remove the excludes -->
<excludes>
<exclude>**/*_Roo_*</exclude>
</excludes>
</configuration>
</plugin>

8) Добавьте аспектjweaver как зависимость ниже аспекта

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>

9) Удалите ссылку на плагин AspectJ:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<!-- Have to use version 1.2 since version 1.3 does not appear to work with ITDs -->
<version>1.2</version>
<dependencies>
<!-- You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>

Это ссылка, которая вызывает все проблемы. Без этого используются значения по умолчанию и приложение работает.

10) Обновите плагин tomcat-maven для автоматического развертывания.

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<server>myserver</server>
<url>http://localhost:8080/manager/text</url>
</configuration>
</plugin>

Это оставляет следующий рабочий файл POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.captaindebug</groupId>
<artifactId>audit</artifactId>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.7</java-version>
<org.springframework-version>3.2.3.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.1</org.aspectj-version>
<org.slf4j-version>1.5.10</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>




<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>




<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>




<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>




<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>




<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>




</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<server>myserver</server>
<url>http://localhost:8080/manager/text</url>
</configuration>
</plugin>




</plugins>
</build>
</project>

Наконец … после правильной настройки проекта, следующее, что нужно сделать, — перейти к коду, о котором я расскажу в следующий раз.


Код для этого и следующего блога доступен на github: 
https://github.com/roghughe/captaindebug/tree/master/audit-aspectj