Статьи

Создайте свой собственный АОП на Java

Вступление

Как вы знаете, AOP — это одна из лучших функций, предоставляемых средой Spring, которая обеспечивает максимальную гибкость при решении сквозных задач. Задумывались ли вы о том, как АОП работает весной? Иногда этот вопрос задают в случае технического собеседования на высоком уровне. Иногда этот вопрос становится более значимым, когда речь идет только о ядре Java. Недавно один из моих друзей пошел на собеседование и столкнулся с неловким вопросом о том, как использовать AOP только в ядре Java без использования Spring и связанных библиотек. В этой статье я предоставлю вам общее представление о том, как создать свой собственный АОП только в ядре Java с определенными ограничениями. Это не сравнительное исследование между Spring AOP и Java AOP. Однако вы можете достичь AOP в Java в определенной степени, используя надлежащие шаблоны проектирования.

Все вы знаете AOP и как его использовать с помощью Spring Framework, эта статья даст вам представление о способах достижения AOP в Java без использования Spring. Для вашей информации Spring использует как прокси JDK, так и CGlib для обеспечения реализации AOP. Динамический прокси JDK обеспечивает гибкий подход для подключения метода и выполнения определенных операций с определенными ограничениями. Ограничение состоит в том, что должен быть интерфейс и должна быть реализация для этого интерфейса. На данный момент ничего не ясно. Давайте возьмем пример. У нас есть калькулятор, с помощью которого мы хотим выполнить некоторые математические операции. Рассмотрим для деления числа на другое число. Теперь вопрос: кто-то уже предоставил реализацию для операции деления в базовой структуре, можно ли в методе выполнить джокер для выполнения дополнительной проверки / проверок? Да, это. Для этого я приведу ниже фрагмент кода для этого простого случая. Основной абстрактный код приведен ниже.

1
2
3
public interface Calculator {
    public int calculate( int a , int b);
}

Код для реализации приведен ниже.

1
2
3
4
5
6
7
public class CalculatorImpl implements Calculator {
 
    @Override
    public int calculate(int a, int b) {
        return a/b;
    }
}

Представьте, что приведенные выше коды были заморожены, и в ядре не может быть больше изменений, но вы должны достичь функциональности без каких-либо проблем. Как это сделать ? Давайте использовать функцию динамического прокси JDK.

01
02
03
04
05
06
07
08
09
10
11
12
public class SomeHandler implements InvocationHandler {
 
// Code omitted for simplicity…..
     
    @Override
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// Your complex business validation and logic
        Object result = method.invoke(targetObject ,params);
        return result;
    }
 
}

Давайте посмотрим на тестовый класс для выполнения желаемой функциональности, используя наш динамический прокси-сервер jdk.

1
2
3
4
5
6
7
public static void main(String[] args) {
        CalculatorImpl calcImpl = new CalculatorImpl();
        Calculator proxied = (Calculator) getProxy (Calculator.class, calcImpl,
                new SomeHandler(calcImpl));
        int result = proxied.calculate(20, 10);
        System.out.println("FInal Result :::" + result);
    }

Как вы уже видели, мы только что предоставили реализацию перехвата, просто реализовав замечательный интерфейсный вызов InvocationHandler . Согласно документации Java, он обрабатывает вызов метода на экземпляре прокси.

Теперь вы видели, что мы можем что-то сделать, используя метод InvocationHandler's invoke() чтобы получить желаемый результат. Теперь возникает вопрос, как мы можем что-то сделать до и после фактического выполнения метода. Возможно ли, что мы можем что-то сделать до того, как метод будет выполнен, и сделать что-то после того, как метод будет выполнен? Чтобы сделать его более конкретным, можем ли мы добавить несколько aops (до, после, вокруг) для подключения метода? Мы можем добиться этого, сделав упрощенный шаблон кода. Давайте следовать приведенным ниже шагам, чтобы достичь этого.

  1. Создайте абстрактный способ наложения собственного aop на целевой объект.
  2. Создайте свой собственный AOP, скажем, BeforeHandler и AfterHandler, где первый выполняет некоторую работу до выполнения метода, а позднее — после выполнения метода.
  3. Создайте прокси-класс, чтобы подружиться с разработчиками, чтобы можно было легко передавать все обработчики aop и объект targettd для создания ловушки.
  4. Предоставьте свою собственную бизнес-логику или сквозную задачу.
  5. Наконец, создайте прокси-объект, передав необходимые параметры.

Краткая техническая реализация

Создайте абстрактный обработчик следующим образом.

1
2
3
4
5
6
7
8
public abstract class AbstractHandler implements InvocationHandler {
 
    private Object targetObject;
         
    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }
}

Создайте гибкие дружественные для разработчиков обработчики, такие как BeforeHandler и AfterHandler .

1
2
3
4
5
6
7
8
9
apublic abstract class BeforeHandler extends AbstractHandler {
 
    public abstract void handleBefore(Object proxy, Method method, Object[] args);
     
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        handleBefore(proxy, method, args);
        return method.invoke(getTargetObject(), args);
    }
}
01
02
03
04
05
06
07
08
09
10
public abstract class AfterHandler extends AbstractHandler {
 
    public abstract void handleAfter(Object proxy, Method method, Object[] args);
     
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(getTargetObject(), args);
        handleAfter(proxy, method, args);
        return result;
    }
}

Создайте утилиту прокси.

01
02
03
04
05
06
07
08
09
10
11
public class ProxyFactory {
 
    public static Object getProxy(Object targetObject,
            List<AbstractHandler> handlers) {
            //Code to get the proxy
            return proxyObject;
        } else {
            return targetObject;
        }
    }
}

Теперь фрагмент кода тестового жгута приведен ниже.

1
2
3
4
5
6
7
8
9
CalculatorImpl calcImpl = new CalculatorImpl();
BeforeHandler before = new BeforeHandlerImpl();
AfterHandler after = new AfterHandlerImpl();
List<AbstractHandler> handlers = new ArrayList<AbstractHandler>();
handlers.add(before);
handlers.add(after);
Calculator proxy = (Calculator) ProxyFactory.getProxy(calcImpl,
                handlers);
int result = proxy.calculate(20, 10);

конфигурация

Все приведенные выше фрагменты кода очень кратки, чтобы дать вам больше ясности о структурной реализации. Всегда лучше иметь настоящий вкус / тест, чтобы воплотить реальность. Загрузите полный проект по следующей ссылке на github, настройте его в своем любимом редакторе java и запустите тестовый класс, чтобы увидеть эффект.

Вывод

Надеюсь, вам понравится моя небольшая статья об АОП на Java, оставьте комментарий, чтобы обогатить и расширить знания обеих сторон. В этой статье, которую я создал до и после, я оставляю «Around» и «Throw» AOP для читателя.