Try English version of Quizful



Раздаем бесплатные Q! подробности в группе Quizful.Alpha-test
Партнеры
Рекрутерам: Прескрининг кандидатов about
Топ контрибуторов
loading
loading
Знаете ли Вы, что

Вы можете подписаться на RSS ленту новых тестов сервиса Quizful, в том числе и отдельно по каждой категории

Лента обновлений
ссылка 20:57:36
Комментарий от Recrut_rf:
Спасибо, сохранил функцию, может пригодится когда - ни...
ссылка 20:53:59
Добавлен вопрос в тест ASP.NET - Основы
ссылка 08:41:09
Комментарий от Krosster:
Гарний сайт для новачків. Правда деякі питання підступн...
ссылка Apr 22 22:06
Добавлен вопрос в тест SQL - Средний уровень
ссылка Apr 22 10:13
Комментарий от Entrery:
вроде выбрал ООП в сишарпе, а тут вопросы по джаве...
Статистика

Тестов: 153, вопросов: 8596. Пройдено: 402470 / 1961086.

Аспектно-ориентированное программирование в Spring Framework

head tail Статья
категория
Java
дата04.09.2013
авторrustock
голосов26

Данная статья предназначена для тех, кто уже хотя бы немного знаком со Spring: представляет как все работает в контейнере фреймворка , умеет организовывать бины в xml файлах и т.д. Итак приступим.

Поначалу я не буду грузить вас теорией, поэтому давайте пока создадим мавеновский проект и добавим в помник следующие зависимости:


      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>3.2.3.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>3.2.3.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.7.3</version>
      </dependency>
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
      </dependency>

Теперь создадим интерфейс Performer

public interface Performer {
    void doSmth();
    String print();
}

И заимплементим его в классе PerformerImpl

public class PerformerImpl implements Performer {
    @Override
    public void doSmth() {
        System.out.println(print());
    }
    @Override
    public String print(){
        return "Performer is working...";
    }
}

Далее нам нужно создать класс с советами, они же advices. Назовем его Aspect.

public class Aspect {
    private Logger logger;

    public void addAppender(){
        logger = Logger.getRootLogger();
        logger.setLevel(Level.INFO);
        PatternLayout layout = new PatternLayout("%d{ISO8601} [%t] %-5p %c %x - %m%n");
        logger.addAppender(new ConsoleAppender(layout));
    }
    public void before(){
        logger.info("Before method...");
    }
    public void after(){
        logger.info("After method...");
    }
    public void afterReturning(){
        logger.info("After returning...");
    }
    public void afterThrowing(){
        logger.info("After throwing...");
    }
}

Теперь создадим xml, где будем настраивать наше спринговое приложение.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="performerBean" class="net.quizful.aop.PerformerImpl"/>
    <bean id="aspectBean" class="net.quizful.aop.Aspect" init-method="addAppender"/>
    <aop:config>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="performerPointcut" expression="execution (* net.quizful.aop.PerformerImpl.doSmth(..))"/>
            <aop:before method="before" pointcut-ref="performerPointcut"/>
            <aop:after method="after" pointcut-ref="performerPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

Итак немного теории. 

Аспектно-ориентированное программирование (АОП) - это методика программирования в рамках классовой парадигмы, основанная на понятии аспекта - блока кода, инкапсулирующего сквозное поведение в составе классов и повторно используемых модулей.  

В нашем случае аспект - это класс, который мы добавили, а методы в нем называются советами или advices. Они делятся на 5 типов:

  • Before (до выполнения метода)
  • After (после выполнения)
  • After returning (после возвращения результата)
  • After throwing (после выбрасывания исключения)
  • Around (объединение несколько советов в 1)
Первые 2 мы уже добавили, остальные рассмотрим чуть позже. 
Итак, в xml мы создали новый конфиг, в который добавили аспект, ссылающийся на бин. Объявили 2 совета: до и после. Но стоп. Что такое pointcut?!
Для того чтобы привести АОП в действие, мы должны обернуть необходимый код в совет(ы). Этот код называется join point или точка соединения, а pointcut является набором join points. 

Как это работает, и зачем это нужно?

Screenshot
Нужный функционал (join points) мы объединяем в pointcut, объявляем советы, которые будем использовать для данного pointcut'а, и средствами АОП (создается так называемый перехватчик или Proxy, который берет управление на себя) "внедряем" в код приложения. Такую функциональность еще называют сквозной или cross-cutting. 
Подробнее о Proxy: http://en.wikipedia.org/wiki/Proxy_pattern
Если вы подумали, что с помощью АОП, мы можем выполнять дополнительные действия над существующим кодом, при этом не изменяя его, то вы правы! 
Способов применению этому много: логирование, безопасность, управление транзакциями и т.д., когда существующий класс или метод должен знать только то, что он выполняет. В нашем случае мы используем логирование. Согласитесь, что нерезонно его "пихать" прямо внутрь метода. Методу неважно, что и как мы логируем, ему главное выполнить свою задачу. Используя АОП, мы разбиваем код на несколько подмодулей, тем самым делая его более читаемым и поддерживаемым. 

Вернемся к нашем коду 

Какие параметры есть у каждого тега я описывать не буду. Расскажу лишь о параметре тега pointсut - expression.

expression="execution (* net.quizful.aop.PerformerImpl.doSmth(..))"

execution означает, что аспект выполняется только при запуске соответствующего метода doSmth. Звездочка перед путем означает, что возвращаемое значение может быть любое, а две точки в скобочках, что аргументы могут быть любые. Мы также можем указывать путь к интерфейсу, а не класса. Тогда аспект будет работать для всех классов, которые имплементируют данный интерфейс.

Итак мы разобрались, как все работает и для чего это нужно. Так давайте запустим наше приложение. Для этого создадим новый класс с методом main.

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Performer performer = (Performer)context.getBean("performerBean");
        performer.doSmth();
    }
}

При запуске мы видим следующее:

2013-08-31 13:57:53,179 [main] INFO  root  - Before method...
Performer is working...
2013-08-31 13:57:53,181 [main] INFO  root  - After method...

Итак, не изменяя интерфейс и класс его реализующий, мы подключили логирование, которое запускается до и после выполнения метода. А теперь давайте проверим работу остальных советов. Для этого создадим новый класс, назовем его Worker:

public class Worker implements Performer {
    @Override
    public void doSmth() {
        System.out.println(print());
    }
    @Override
    public String print() {
        if(Math.random() < 0.5)
            throw new RuntimeException("Worker exception");
        return "Worker is working...";
    }
}

Теперь изменим наш xml файл: добавим новый бин и аспект с новыми советами.

<!--?xml version="1.0" encoding="UTF-8"?-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="aspectBean" class="net.quizful.aop.Aspect" init-method="addAppender">
    <bean id="performerBean" class="net.quizful.aop.PerformerImpl">
    <bean id="workerBean" class="net.quizful.aop.Worker">
    <aop:config>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="performerPointcut" expression="execution (* net.quizful.aop.PerformerImpl.doSmth(..))">
            <aop:before method="before" pointcut-ref="performerPointcut">
            <aop:after method="after" pointcut-ref="performerPointcut">
        </aop:after></aop:before></aop:pointcut></aop:aspect>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="workerPointcut" expression="execution(* net.quizful.aop.Worker.doSmth(..))">
            <aop:after-throwing method="afterThrowing" pointcut-ref="workerPointcut">
            <aop:after-returning method="afterReturning" pointcut-ref="workerPointcut">
        </aop:after-returning></aop:after-throwing></aop:pointcut></aop:aspect>
    </aop:config>
</bean></bean></bean></beans>

И теперь метод main:

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Performer worker = (Performer)context.getBean("workerBean");
        worker.doSmth();
    }

При запуске, с шансом 50% мы будем видеть либо

Worker is working...
2013-08-31 14:15:06,322 [main] INFO  root  - After returning...

либо

Exception in thread "main" java.lang.RuntimeException: Worker exception
2013-08-31 14:22:48,084 [main] INFO  root  - After throwing...

Теперь разберемся с последним советом, а именно around, который объединяет несколько советов в один. Для этого добавим новый метод в класс Aspect:

    public void around(ProceedingJoinPoint joinPoint){ //добавление этого аргумента обязательно!
        try {
            before();
            joinPoint.proceed(); //запускаем наш joinpoint
            after();
            afterReturning();
        } catch (Throwable throwable) {
            afterThrowing();
        }
    }

Теперь вернемся к нашему перформеру и изменим наш аспект в xml файле.

        <aop:aspect ref="aspectBean">
            <aop:pointcut id="performerPointcut" expression="execution (* net.quizful.aop.PerformerImpl.doSmth(..))">
            <aop:around method="around" pointcut-ref="performerPointcut">
        </aop:around></aop:pointcut></aop:aspect>


Запустим наше приложение уже от не от воркера, а перформера. Думаю, вы знаете что делать ;)
После запуска мы должны увидеть:

2013-08-31 14:37:57,706 [main] INFO  root  - Before method...
Performer is working...
2013-08-31 14:37:57,708 [main] INFO  root  - After method...
2013-08-31 14:37:57,708 [main] INFO  root  - After returning...

Тем самым с помощью around мы объединили все 4 совета в 1. 

Заключение

АОП - мощный инструмент в руках разработчика, который позволяет вынести сквозной функционал "за сцену" нашего приложения. А Spring AOP  делает это максимально удобно и понятно. Конечно же Spring AOP является не единственным вариантом аспектно-ориентированного программирования на языке Java, достойной альтернативой могу назвать AspectJ, но в этой статье я его рассматривать не буду. 

Если Вам понравилась статья, проголосуйте за нее

Голосов: 26  loading...
baxxabit   Grishman   rustock   liberal4ik   schyzoo   publo   fike   lano4ka   akmil   VasilievAleksey   SamTan   Jack_killer   Nadya2308   Garazd   com   alexvv   iceknight   serega123   error666   Nicolas_As   jibiday   GreyGoblin   captian   BeLucky   Fevor   duhno