АОП — это не язык, а подход к разработке программного обеспечения. Как и любая методология, она имеет различные реализации, и AspectJ в настоящее время является самым богатым и полным из всех. После объединения AspectJ и AspectWerkz теперь можно создавать аспекты с использованием аннотаций.
Причина, по которой разработчики пишут код, заключается в том, чтобы обеспечить какую-то функциональность Тип функциональности не важен для этого обсуждения: некоторые могут захотеть предоставить бизнес-функциональность, другие могут написать код для исследовательских целей, другие — просто для удовольствия. Дело в том, что у любой информационной системы есть основной мотив, ключевая функциональность, которую она хочет предоставить. Например, я недавно написал PODAM , инструмент тестирования, конечной целью которого является автоматическое заполнение свойств POJO / JavaBean.
У каждой информационной системы также есть потребность в ортогональных услугах (то, что АОП называет сквозными проблемами); например, ведение журнала, безопасность, аудит, управление исключениями и так далее. В то время как информационная система может быть разделена на отдельные части функциональности (то, что АОП определяет точки соединения), ортогональные услуги необходимы по всем направлениям. Например, если кто-то хочет записать, сколько времени занимает выполнение каждого открытого метода, каждый открытый метод должен иметь что-то вроде следующего псевдокода:
01
02
03
04
05
06
07
08
09
10
11
|
public void someBusinessMethod() { long start = System.currentTimeInMilliseconds(); doTheBusinessFunctionality(); long end = System.currentTimeInMilliseconds(); log.debug( "The execution of someBusinessMethod took " + (end - start) + " milliseconds" ); } |
В приведенном выше методе базовая функциональность определяется только функцией someBusinessMethod (), тогда как все остальное — просто логирование. Было бы неплохо иметь что-то вроде:
1
2
3
4
5
6
7
|
//Some external magic happens before the invocation of this method to take the start time public void someBusinessMethod() { doTheBusinessFunctionality(); } //Some external magic happens after the invocation of this method to take the end time and logs how long the execution took. |
Разработчики обычно хотят вести журнал, безопасность и т. Д. Во всем приложении, а не для одного метода; AOP позволяет разработчикам достичь этой цели путем определения где-то извне (называемого аспектом) поведения, применяемого ко всему коду, соответствующему некоторому шаблону (на самом деле AOP допускает более широкий набор функций, таких как возможность добавления интерфейсов, переменных экземпляра, методов, и т. д. в класс, просто чтобы назвать один). Это уполномоченное поведение затем несколько добавляется к окончательному исполняемому коду с помощью того, что AOP называет Weaver.
Это может быть достигнуто различными способами: переплетение может происходить на уровне источника, на двоичном уровне и во время загрузки. Вы можете думать о ткаче как о компоновщике в C и C ++; исходники и библиотеки связаны между собой для создания исполняемого файла; ткач сочетает в себе Java-код и аспекты для создания расширенного поведения.
Spring достигает этого уполномоченного поведения, создавая прокси-сервер AOP вокруг кода, чье поведение должно быть обогащено. Следующий код показывает очень простой пример, основанный на AspectJ; Пример окружает выполнение простого метода некоторой службой аутентификации.
Сервисы аутентификации выглядят очень просто (дело не в том, как реализована эта функциональность, а в том, что сервис аутентификации доступен):
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
|
/** * */ package uk.co.jemos.aop; /** * A simple authenticator service. * * @author mtedone * */ public class Authenticator { public void authenticate() { System.out.println( "Authenticated" ); } } Now let's have a look at the business logic: /** * */ package uk.co.jemos.aop; /** * A simple service which delivers messages * @author mtedone * */ public class MessageCommunicator { public void deliver(String message) { System.out.println(message); } public void deliver(String person, String message) { System.out.println(person + ", " + message); } } |
Нам хотелось бы, чтобы Authenticator вызывался до вызова любого из бизнес-методов MessageCommunicator. Используя синтаксис аннотации AspectJ, мы пишем в Aspect на чистом Java:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package uk.co.jemos.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class SecurityAspect { private Authenticator authenticator = new Authenticator(); @Pointcut ( "execution(* uk.co.jemos.aop.MessageCommunicator.deliver(..))" ) public void secureAccess() { }; @Before ( "secureAccess()" ) public void secure() { System.out.println( "Checking and authenticating user..." ); authenticator.authenticate(); } } |
1
|
|
Код выше немного интереснее. Аспект помечен аннотацией @Aspect. Pointcut — это некоторая точка интереса в нашем коде, где мы бы хотели, чтобы наш Aspect включился. Синтаксис
@Pointcut («выполнение (* uk.co.jemos.aop.MessageCommunicator.deliver (..))») public void secureAccess () {};
означает: «Определить Pointcut с именем secureAccess, который применяется ко всем методам доставки в классе MessageCommunicator, независимо от типа возврата такого метода». То, что следует, называется советом, и именно здесь AOP усиливает поведение нашего класса:
1
2
3
4
5
6
7
|
@Before ( "secureAccess()" ) public void secure() { System.out.println( "Checking and authenticating user..." ); authenticator.authenticate(); } |
Приведенный выше код гласит: «Перед любым соответствием SecureAccess () Pointcut применяйте код в блоке». Все вышеперечисленное — чисто Java, хотя аннотации принадлежат среде исполнения AspectJ. Чтобы использовать вышеуказанный аспект с Spring, я определил контекстный файл Spring:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> < aop:aspectj-autoproxy /> < bean id = "messageCommunicator" /> < bean id = "securityAspect" /> </ beans > |
Элемент XML: <aop: aspectj-autoproxy /> указывает Spring создать прокси для каждого аспекта. Теперь, когда я использую MessageCommunicator от клиента:
01
02
03
04
05
06
07
08
09
10
11
12
|
/** * @param args */ public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:aop-appContext.xml" ); MessageCommunicator communicator = ctx.getBean( "messageCommunicator" , MessageCommunicator. class ); communicator.deliver( "Hello World" ); communicator.deliver( "Marco" , "Hello World" ); } |
Я получаю следующий вывод:
ИНФОРМАЦИЯ: Загрузка определений XML-бинов из ресурса пути к классу [aop-appContext.xml] 15 мая 2011 г. 11:51:41
org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
ИНФОРМАЦИЯ: Предварительные экземпляры синглетов в org.springframework.beans.factory.support.DefaultListableBeanFactory@21b64e6a: определение bean-компонентов [org.springframework.aop.config.internalAutoProxyCreator, messageCommunicator, securityAspect];
корень фабричной иерархии Проверка и аутентификация пользователя… Аутентифицированный Hello World
Проверка и аутентификация пользователя… Аутентифицированный Marco, Hello World
АОП существенно меняет наши взгляды на разработку программного обеспечения, позволяя нам выявлять сквозные проблемы во внешних компонентах, которые затем включаются в наш код при необходимости. Это позволяет создавать более чистый и более обслуживаемый код, а реализации безграничны. Кроме того, если мы будем осторожны в написании наших Аспектов, делая их многократно используемыми, мы можем быстро создать библиотеку универсальных, многократно используемых аспектов, которые добавляют функциональность нашему коду встроенным способом.
Очевидно, что при внедрении AOP существуют недостатки, в основном это кривая обучения, которая требуется разработчикам для ознакомления с технологией. AspectJ определяет свой собственный язык и синтаксис, как показано в примере выше); аннотация @Before является лишь одной из возможностей: советы могут применяться до, после, вокруг объектов; Кроме того, синтаксис для определения Pointcuts не Java, а скорее как скрипт. Аспекты AspectJ также имеют ключевые слова и нативные объекты для захвата контекста точек соединения, которые они советуют, и этот синтаксис необходимо изучить. Тем не менее, потенциальные выгоды значительно увеличиваются благодаря дополнительным усилиям, необходимым для изучения этой новой и интересной технологии.
Справка: AOP стало проще с AspectJ и Spring от нашего партнера по JCG