Принцип реализации Spring АОП

задняя часть Spring

1. Что такое АОП?

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

2. Как реализован спрингаоп?

Существует два основных способа динамического прокси в Spring AOP: динамический прокси JDK и динамический прокси CGLIB. Динамический прокси-сервер JDK получает проксируемый класс посредством отражения и требует, чтобы проксируемый класс реализовывал интерфейс. Ядром динамического прокси JDK являетсяInvocationHandlerинтерфейс иProxyсвоего рода.

Если целевой класс не реализует интерфейс, Spring AOP выберет использование CGLIB для динамического проксирования целевого класса. CGLIB (библиотека генерации кода) — это библиотека классов, сгенерированная кодом, которая может динамически генерировать подклассы класса во время выполнения.Обратите внимание, что CGLIB — это динамический прокси через наследование, поэтому, если класс помечен какfinal, то он не может использовать CGLIB в качестве динамического прокси.

2.1, динамический прокси JDK

определить класс интерфейса

package org.java.base.springaop;
public interface Service {
 /**
 * add方法
 */
 public void add();

 /**
 * update方法
 */
 public void update();
}

реализовать класс интерфейса

package org.java.base.springaop;
public class ServiceImpl implements Service {
 
 public void add() {
 System.out.println("AService add>>>>>>>>>>>>>>>>>>");
 }


 public void update() {
 System.out.println("AService update>>>>>>>>>>>>>>>");
 }
}

Реализуйте динамический прокси-класс MyInvocationHandler, реализуйте интерфейс InvocationHandler и реализуйте метод вызова в интерфейсе. При внимательном рассмотрении метода вызова в этот метод добавляется логика аспекта. Выполнение метода целевого класса завершается оператором mehod.invoke(target, args).

package org.java.base.springaop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
 private Object target;
 /**
 * 构造函数
 * @param target
 */
 MyInvocationHandler(Object target) {
 super();
 this.target = target;
 }
 public Object invoke(Object proxy, Method method, Object[] args)
 throws Throwable {
 // 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
 System.out.println("before-----------------------------");
 // 程序执行
 Object result = method.invoke(target, args);
 // 程序执行后加入逻辑,MethodAfterAdviceInterceptor
 System.out.println("after------------------------------");
 return result;
 }

}

Тестовый класс, в котором Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler) генерирует расширенный целевой объект.

package org.java.base.springaop;

import java.lang.reflect.Proxy;
public class Test {
 public static void main(String[] args) {
 Service aService = new ServiceImpl();
 MyInvocationHandler handler = new MyInvocationHandler(aService);
 // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
 Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
 .getClass().getClassLoader(), aService.getClass()
 .getInterfaces(), handler);
 // 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
 aServiceProxy.add();
 System.out.println();
 aServiceProxy.update();
 
 }
}

1525849940(1)

Видно, что логика пользовательского аспекта была добавлена ​​до и после методов добавления и обновления целевого класса ServiceImpl, и заработал механизм перехвата АОП.

2.2, динамический прокси CGLIB

целевой класс,cglibНет необходимости определять единый интерфейс для целевого класса

package org.java.base.springaop;

public class Base {
 /**
 * 一个模拟的add方法
 */
 public void add() {
 System.out.println("add ------------");
 }
}

Для реализации динамического прокси-класса CglibProxy необходимо реализовать интерфейс MethodInterceptor и реализовать метод перехвата. Прокси добавляет пользовательскую логику аспекта до и после метода добавления, а инструкция выполнения метода добавления целевого класса — proxy.invokeSuper(object, args);

package org.java.base.springaop;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

 public Object intercept(Object object, Method method, Object[] args,
 MethodProxy proxy) throws Throwable {
 // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
 System.out.println("before-------------");
 // 执行目标类add方法
 proxy.invokeSuper(object, args);
 // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
 System.out.println("after--------------");
 return null;
 }

}

Получите фабрику Factory расширенного целевого класса, в котором объект класса расширенного метода реализован Enhancer, код выглядит следующим образом:

 package org.java.base.springaop;

import org.springframework.cglib.proxy.Enhancer;

public class Factory {
 /**
 * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
 * 
 * @param proxy
 * @return
 */
 public static Base getInstance(CglibProxy proxy) {
 Enhancer enhancer = new Enhancer();
 enhancer.setSuperclass(Base.class);
 //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
 enhancer.setCallback(proxy);
 // 此刻,base不是单纯的目标类,而是增强过的目标类
 Base base = (Base) enhancer.create();
 return base;
 }
}

тестовый класс

package org.java.base.springaop;

public class Testcglib {
 public static void main(String[] args) {
 CglibProxy proxy = new CglibProxy();
 // base为生成的增强过的目标类
 Base base = Factory.getInstance(proxy);
 base.add();
 }
}

С тех пор механизм АОП-перехвата, реализованный динамическим прокси cglib, в основном реализован.Давайте посмотрим на эффект перехвата.Результаты выполнения программы следующие:

до------
добавлять ----
после-------

Видно, что логика пользовательского аспекта была добавлена ​​до и после метода add целевого класса Base, и вступает в силу механизм перехвата АОП.

3. Краткое описание двух методов динамического прокси и CGLIB выглядит следующим образом:

  • Динамический прокси JDK (динамический прокси)
    • Функция динамического прокси на основе стандартного JDK
    • Только для бизнес-объектов, реализующих интерфейс
  • CGLIB
    • Прокси-серверы AOP реализуются путем динамического создания подклассов целевого объекта,
    • необходимо указать@EnableAspectJAutoProxy(proxyTargetClass = true)заставить использовать
    • Если бизнес-объект не реализует никакого интерфейса, по умолчанию выбирается CGLIB.