Post on 22-Jun-2015
transcript
Знакомство с аспектно-ориентированнымпрограммированием
Михаил Крестьянинов,Новотелеком
Эволюция парадигм
2
Сферический быдлокод в вакууме
public class WebService {
public BookDTO getBook(Integer bookId) {
BookDTO book = bookDAO.readBook(bookId);
return book; }
}
3
Сферический быдлокод:Логирование
public BookDTO getBook(Integer bookId) {
LOG.debug("Call method getBook with id " + bookId);
BookDTO book = bookDAO.readBook(bookId);
LOG.debug("Book info is: " + book.toString()); return book;}
4
Сферический быдлокод:Обработка исключенийpublic BookDTO getBook(Integer bookId) throws ServiceException {
LOG.debug("Call method getBook with id " + bookId); BookDTO book = null; try { BookDTO book = bookDAO.readBook(bookId); } catch(SQLException e) { throw new ServiceException(e); }
LOG.debug("Book info is: " + book.toString()); return book;}
5
Сферический быдлокод:Авторизацияpublic BookDTO getBook(Integer bookId)
throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight("GetBook")) throw new AuthException("Permission Denied");
LOG.debug("Call method getBook with id " + bookId); BookDTO book = null; try { BookDTO book = bookDAO.readBook(bookId); } catch(SQLException e) { throw new ServiceException(e); }
LOG.debug("Book info is: " + book.toString()); return book;}
6
Сферический быдлокод:Кеширование
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight("GetBook")) throw new AuthException("Permission Denied");
LOG.debug("Call method getBook with id " + bookId); BookDTO book = null; String cacheKey = "getBook:" + bookId; try {
if (cache.contains(cacheKey)) { book = (BookDTO) cache.get(cacheKey); } else { BookDTO book = bookDAO.readBook(bookId);
cache.put(cacheKey, book); } } catch(SQLException e) { throw new ServiceException(e); }
LOG.debug("Book info is: " + book.toString()); return book;}
7
Сферический быдлокод:Идеальный вариант
8
Сквозная функциональность
• логирование;
• профилирование;
• обработка транзакций;
• обработка ошибок;
• авторизация и проверка прав;
• кеширование;
• контрактное программирование.
9
Что такое АОП
Основной задачей аспектно-ориентированного программирования (АОП) является модуляризация сквозной функциональности, выделение её в аспекты.
10
AspectJ: Пример аспекта@Aspectpublic class WebServiceLogger { private final static Logger LOG = Logger.getLogger(WebServiceLogger.class);
@Pointcut("execution(* example.WebService.*(..)) && @annotation(example.Loggable)") public void loggableMethod() { }
@Around(“loggableMethod()") public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) { String methodName = thisJoinPoint.getSignature().getName(); Object[] methodArgs = thisJoinPoint.getArgs();
LOG.debug("Call method " + methodName + " with args " + methodArgs); Object result = thisJoinPoint.proceed(); LOG.debug("Method " + methodName + " returns " + result); return result; }}
11
Термины АОП
• аспект (aspect);
• совет (advice);
• точка соединения (join point);
• срез (pointcut);
• внедрение (introduction).
12
AspectJ: точки соединения
Создание объекта
• initialization(MyClass || MyOtherClass);
• staticinitialization(MyClass+ && !MyClass);
Вызов метода, обращение к свойству
• execution(static * com.xyz..*.*(..));
• call(void MyInterface.*(..));
• handler(ArrayOutOfBoundsException);
• get/set(static int MyClass.x);
13
AspectJ: точки соединения
Вспомагательные
• this/target(MyClass)
• args(Integer)
• within/withincode(MyClass)
• cflow/cflowbelow(call(void MyClass.test()))
• @annotation(MyAnnotation)
14
AspectJ: советы
• before – запуск совета до выполнения точки соединения;
• after returning — запуск совета после нормального выполнения точки соединения;
• after throwing — запуск совета после выброса исключения в процессе выполнения точки соединения;
• after — запуск совета после любого варианта выполнения точки соединения;
• around – запуск совета вместо выполнения точки соединения (выполнение точки соединения может быть вызвано внутри совета).
15
AspectJ: вторая попытка
@Aspectpublic class WebServiceLogger { private final static Logger LOG = Logger.getLogger(WebServiceLogger.class);
@Pointcut("execution(* example.WebService.*(..)) && @annotation(example.Loggable)") public void loggableMethod() { }
@Around(“loggableMethod()") public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) { String methodName = thisJoinPoint.getSignature().getName(); Object[] methodArgs = thisJoinPoint.getArgs();
LOG.debug("Call method " + methodName + " with args " + methodArgs); Object result = thisJoinPoint.proceed(); LOG.debug("Method " + methodName + " returns " + result); return result; }}
16
PostSharpАспект обработки исключений:
public class ExceptionDialogAttribute : OnExceptionAspect{ public override void OnException(MethodExecutionEventArgs e) { string message = e.Exception.Message; Window window = Window.GetWindow((DependencyObject)eventArgs.Instance);
MessageBox.Show(window, message, "Exception");
eventArgs.FlowBehavior = FlowBehavior.Continue; }}
17
PostSharp
Применение аспекта через настройки сборки:
[assembly: ExceptionDialog ( AttributeTargetTypes="Example.WorkflowService.*", AttributeTargetMemberAttributes = AttributeTargetElements.Public )]
Применение аспекта с помощью атрибута:
[ExceptionDialog]public BookDTO GetBook(Integer bookId)
18
PostSharp
Aспекты:
• OnMethodBoundary/OnMethodInvocation – обращение к методу (начало, конец, выход, выход с исключением);
• OnFieldAccess – обращение к свойству;
• OnException – обработка исключения;
• Composition – внедрение кода.
19
Как это работает?
20
Как это работает?
• Изменение байт-кода:
21
Как это работает?
• Автоматическое создание proxy-объектов:
22
От теории к практике
23
От теории к практике
24
От теории к практике
• Отталкивает необходимость использования дополнительных компиляторов;
• Смущает отсутствие широкой известности и большого сообщества.
26
Spring AOP
• написан на чистом Java/C#;
• не использует сторонних компиляторов;
• урезанная поддержка АОП;
• интеграция с AspectJ;
• поддержка аннотаций @AspectJ;
• часть Spring Framework.
26
Spring AOP: Примеры
Ограничение прав доступа:
<sec:protect-pointcut expression="execution(* ru.novotelecom.xxx.Subscription.*(..))" access="ROLE_SUBSCRIPTION_MANAGER" />
Или
@Aspectpublic class SecurityManager {
@Before("execution(* ru.novotelecom.xxx.Subscription.*(..))") public void doAccessCheck() {… }}
27
Spring AOP: ПримерыОбработка исключений:
@Aspectpublic class WebServiceException {
@Pointcut("execution(* example.WebService.*(..)) && @annotation(example.Loggable)") public void loggableMethod() { }
@Around(“loggableMethod()") public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) throws ServiceException { Object result = null;
try { result = thisJoinPoint.proceed(); } catch (Exception e) { throw new ServiceException(e); }
return result; }}
28
Альтернативы
Шаблоны проектирования:
• Proxy
• Decorator
• Chain of responsibility
Конструкции языка:
• Аннотации / Атрибуты
29
АОП в других языках
• python (Aspyct)
• perl (Aspect CPAN)
• ruby (AspectR)
• c++ (AspectC++)
• php (Seasar, GAP)
• javascript (Ajaxpect)
30
Резюме
• Основная цель АОП — выноса «общей» (сквозной) функциональности «за скобки» (модуляризация сквозной функциональности);
• Для Java AOP доступен через проект AspectJ, для .NET – через PostSharp;
• Наиболее простая и проверенная реализация AOP – Spring AOP.
31
От теории к практике
32
Ссылки
• http://habrahabr.ru/blogs/programming/114649/
• http://www.eclipse.org/aspectj/
• http://www.sharpcrafters.com/postsharp
• https://www.ibm.com/developerworks/ru/library/j-aopwork15/
• http://en.wikipedia.org/wiki/Aspect-oriented_programming
33
Вопросы?
Михаил Крестьянинов,Новотелеком
mikhail.krestjaninoff@gmail.com