Если вы похожи на меня, то у вас будут такие дни программирования, когда кажется, что все идет очень хорошо. Вы пишете код и тесты, и это просто работает. И потом, и есть другие дни, действительно плохие, когда вы знаете, что все, что вы написали, настолько хорошо, насколько это возможно, и код отказывается работать: что-то явно не так, но вы не представляете, какие. У меня был один из таких дней, когда я писал код для этого блога. Идея состояла в том, чтобы продемонстрировать, как использовать Spring и Aspectj для аудита посещений пользователем экрана.
Аудит посещений пользователем экрана — одна из немногих сквозных задач, которые очень хорошо решает Аспектно-ориентированное программирование (AOP). Идея моего демо-кода заключается в том, что вы добавляете аннотацию к соответствующим контроллерам, и каждый раз, когда пользователь посещает страницу, этот визит записывается. Используя эту технику, вы можете создать изображение самых популярных экранов и, следовательно, самых популярных функциональных блоков в вашем приложении. Знание этих подробностей облегчает решение о том, куда направить ваши усилия по разработке, поскольку разработка тех частей вашего приложения, которые вряд ли кто-либо когда-либо использует, не окупается.
Я уже говорил об AspectJ и AOP в следующих блогах, и это здорово, поскольку они демонстрируют основы, но они не являются реально работающим Spring MVC-приложением. Мои предыдущие блоги были:
- АОП и АспектJ Терминология
- Определение Spring’s AspectJ Советы Типов
- Использование @After Advice от AspectJ в вашем приложении Spring
- Использование поддержки AspectJ в Spring и аннотации @Before
- Использование совета AspectJ @AfterThrowing в приложении Spring
На этот раз я подумал, что придумаю полнофункциональное приложение 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