Статьи

Spring — добавление поддержки AOP

Я слышал историю об одном старшем (и довольно высокооплачиваемом) инженере-программисте. Ему было дано задание регистрировать каждый метод в каждом контроллере в проекте, над которым он работал. Инженер переписал все методы контроллера, поэтому из кода:

1
2
3
4
5
6
7
@RequestMapping(method = RequestMethod.GET)
public String showEmployees(Model model) {
    List<Employee> employees = employeeDao.list();
    model.addAttribute('employees', employees);
 
    return 'employees/list';
}

он сделал следующий код:

01
02
03
04
05
06
07
08
09
10
   @RequestMapping(method = RequestMethod.GET)
   public String showEmployees(Model model) {
LOGGER.log('Invoking method showEmployees');
 
       List<Employee> employees = employeeDao.list();
       model.addAttribute('employees', employees);
 
LOGGER.log('Returning from method showEmployees');
       return 'employees/list';
   }

Что не так с этим кодом? Что ж:

  • Требуется много времени, чтобы изменить каждый метод с таким кодом
  • Это подвержено ошибкам — вы можете ввести опечатки или забыть добавить журналирование где-нибудь
  • Это смешивание сквозных вопросов . Это означает, что вы добавляете такой же тип повторяющегося, стандартного и несвязанного кода в места, где он не принадлежит.
  • Например, какова ответственность метода showEmployees? Это вызов службы, привлечение сотрудников и их моделирование. Ведение журнала — это не ответственность, так зачем смешивать эти проблемы?

    Если бы упомянутый мной инженер знал об аспектно-ориентированном программировании, он бы сэкономил много времени и сделал код лучше и читабельнее. Spring поддерживает то, что называется «Аспекты», которые созданы именно для таких задач. Аспекты позволяют нам определять общую функциональность в одном месте. Прежде чем писать какой-либо код, необходимо разобраться в терминологии. Эта терминология довольно обширна, и я не собираюсь ее здесь писать, но я призываю вас прочитать официальную справочную страницу Spring на АОП, если вы хотите узнать больше. Вы должны по крайней мере понять, что такое Advice, Join Point, Pointcut, Aspect и Weaving.

    Хорошо, давайте добавим Aspect для регистрации методов контроллера, именно то, что должен был сделать инженер из истории в начале.

    Сначала мы должны добавить зависимости в pom.xml в библиотеке AspectJ:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.11</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId>
        <version>1.6.11</version>
    </dependency>

    Также проверьте, есть ли у вас зависимость от Spring AOP (но если вы следуете этому руководству с самого начала, оно у вас уже есть):

    1
    2
    3
    4
    5
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aop</artifactId>
     <version>3.1.0.RELEASE</version>
    </dependency>

    Теперь давайте напишем код Аспекта. Создать пакет org.timesheet. аспекты и добавить класс ControllerLoggingAspect:

    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
    43
    package org.timesheet.aspects;
     
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
     
    import java.util.Arrays;
     
    /**
     * Will log every invokation of @RequestMapping annotated methods
     * in @Controller annotated beans.
     */
    @Aspect
    public class ControllerLoggingAspect {
     
        @Pointcut('within(@org.springframework.stereotype.Controller *)')
        public void controller() {}
     
        @Pointcut('execution(* *(..))')
        public void methodPointcut() {}
     
        @Pointcut('within(@org.springframework.web.bind.annotation.RequestMapping *)')
        public void requestMapping() {}
     
        @Before('controller() && methodPointcut() && requestMapping()')
        public void aroundControllerMethod(JoinPoint joinPoint) throws Throwable {
            System.out.println('Invoked: ' + niceName(joinPoint));
        }
     
        @AfterReturning('controller() && methodPointcut() && requestMapping()')
        public void afterControllerMethod(JoinPoint joinPoint) {
            System.out.println('Finished: ' + niceName(joinPoint));
        }
     
        private String niceName(JoinPoint joinPoint) {
            return joinPoint.getTarget().getClass()
                    + '#' + joinPoint.getSignature().getName()
                    + '\n\targs:' + Arrays.toString(joinPoint.getArgs());
        }
     
    }

    Этот код говорит, что @Before и @AfterReturning из метода контроллера мы будем регистрировать информацию о его вызове (имя и аргументы). Этот совет выполняется, когда все три pointcut совпадают. controller () pointcut помечает совпадающую точку соединения (которая соответствует стереотипу Controller), в которой должен быть создан совет. methodPointcut () отмечает, что мы имеем дело с вызовом метода, а requestMapping () pointcut отмечает методы, аннотированные @RequestMapping.

    Чтобы это работало, мы добавим конфигурационный файл Spring aop.xml в src / main / resources:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    <?xml version='1.0' encoding='UTF-8'?>
     
        <!-- AOP support -->
        <bean id='controllerAspect' class='org.timesheet.aspects.ControllerLoggingAspect' />
        <aop:aspectj-autoproxy>
            <aop:include name='controllerAspect' />
        </aop:aspectj-autoproxy>
     
    </beans>

    И затем мы импортируем его в конфигурационный файл timesheet-servlet.xml Spring:

    1
    <import resource='classpath:aop.xml' />

    Это была последняя часть урока. Я надеюсь, что теперь вы лучше понимаете, что такое Spring и как он помогает решать ваши проблемы. Помните, что мы рассмотрели только маленький кусочек Spring в этом уроке. Еще многое предстоит изучить!

    Ссылка: Часть 6. Добавление поддержки AOP от нашего партнера JCG Михала Вртиака в блоге vrtoonjava .