Пружинный сердечник серии АОП (2)
Всем привет, вПружинный сердечник серии АОП (1)В этой статье я рассказал вам о самом мощном методе аннотирования Spring AOP для реализации АОП (вам лучше взглянуть на него, если вы его не читали, большинство примеров далее в этой статье разработаны с использованием аннотаций), это статья расскажет вам, как работает Spring. Чтобы сделать АОП на основе XML, структура статьи:
- Spring AOP — конфигурация XML
- Приоритет аспекта
- Сценарии практического применения Spring AOP
- Выбор базовой реализации Spring AOP
1. Spring АОП — XML-конфигурация
Вот краткий анализ формы разработки xml непосредственно в виде кейса, и определен класс аспекта MyAspectXML:
public class MyAspectXML {
public void before(){
System.out.println("MyAspectXML====前置通知");
}
public void afterReturn(Object returnVal){
System.out.println("后置通知-->返回值:"+returnVal);
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("MyAspectXML=====环绕通知前");
Object object= joinPoint.proceed();
System.out.println("MyAspectXML=====环绕通知后");
return object;
}
public void afterThrowing(Throwable throwable){
System.out.println("MyAspectXML======异常通知:"+ throwable.getMessage());
}
public void after(){
System.out.println("MyAspectXML=====最终通知..来了");
}
}
Обратите внимание, что этот класс не имеет аннотаций. Затем взгляните на наш XML-файл:
<!-- 把切面引入到Spring容器中 -->
<bean name="myAspectXML" class="com.zdy.MyAspectXML" />
<!-- 配置AOP 切面 -->
<aop:config>
<!-- 定义切点 -->
<aop:pointcut id="pointcut" expression="execution(...)" />
<!-- 定义其他切点函数 -->
<aop:pointcut id="otherPointcut" expression="execution(...)" />
<!-- 定义通知 order 定义优先级,值越小优先级越大-->
<aop:aspect ref="myAspectXML" order="0">
<!-- 定义通知
method 指定通知方法名,必须与MyAspectXML中的相同
pointcut 指定切点函数
-->
<aop:before method="before" pointcut-ref="pointcut" />
<!-- 后置通知 returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
<aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut" />
<!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
<!--
method : 通知的方法(最终通知)
pointcut-ref : 通知应用到的切点方法
-->
<aop:after method="after" pointcut-ref="otherPointcut"/>
</aop:aspect>
</aop:config>
Как видите, тег
2. Приоритет аспекта
Сначала позвольте мне сказать,Предпосылка проектирования с учетом проблемы приоритета заключается в том, что расширенный Pointcut имеет пересечение.Существует два типа приоритетов Аспекта: один заключается в том, что в Аспекте определено несколько улучшений. Второй — множественные улучшения в разных Аспектах.Начну с заключения, а потом приведу два примера для разных ситуаций.
- Расширенный @Before перед целевым методом имеет более высокий приоритет и выполняется первым. Улучшение @After, @AfterReturning после целевого метода, чем выше приоритет, тем позже выполнение.
- Несколько улучшений определены в Аспекте. Это связано с порядком определений, чем раньше оно появляется, тем выше приоритет.
- Несколько улучшений в разных аспектах. Интерфейс Ordered реализован по Aspect, а возвращаемое значение метода getOrder связано, чем меньше возвращаемое значение, тем выше приоритет.
2.1 Определение нескольких улучшений в Аспекте
@Aspect
public class AspectOne {
/**
* Pointcut定义切点函数
*/
@Pointcut("execution(...)")
private void myPointcut(){}
@Before("myPointcut()")
public void beforeOne(){
System.out.println("前置通知....执行顺序1");
}
@Before("myPointcut()")
public void beforeTwo(){
System.out.println("前置通知....执行顺序2");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningThree(){
System.out.println("后置通知....执行顺序3");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningFour(){
System.out.println("后置通知....执行顺序4");
}
}
распечатать результат:
前置通知....执行顺序1
前置通知....执行顺序2
后置通知....执行顺序4
后置通知....执行顺序3
2.2 Несколько улучшений в разных аспектах
@Aspect
public class AspectOne implements Ordered {
/**
* Pointcut定义切点函数
*/
@Pointcut("execution(...)")
private void myPointcut(){}
@Before("myPointcut()")
public void beforeOne(){
System.out.println("前置通知..AspectOne..执行顺序1");
}
@Before("myPointcut()")
public void beforeTwo(){
System.out.println("前置通知..AspectOne..执行顺序2");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningThree(){
System.out.println("后置通知..AspectOne..执行顺序3");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningFour(){
System.out.println("后置通知..AspectOne..执行顺序4");
}
/**
* 定义优先级,值越低,优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
//切面类 AspectTwo.java
@Aspect
public class AspectTwo implements Ordered {
/**
* Pointcut定义切点函数
*/
@Pointcut("execution(...)")
private void myPointcut(){}
@Before("myPointcut()")
public void beforeOne(){
System.out.println("前置通知....执行顺序1--AspectTwo");
}
@Before("myPointcut()")
public void beforeTwo(){
System.out.println("前置通知....执行顺序2--AspectTwo");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningThree(){
System.out.println("后置通知....执行顺序3--AspectTwo");
}
@AfterReturning(value = "myPointcut()")
public void AfterReturningFour(){
System.out.println("后置通知....执行顺序4--AspectTwo");
}
/**
* 定义优先级,值越低,优先级越高
* @return
*/
@Override
public int getOrder() {
return 1;
}
}
Выходной результат:
前置通知..AspectOne..执行顺序1
前置通知..AspectOne..执行顺序2
前置通知....执行顺序1--AspectTwo
前置通知....执行顺序2--AspectTwo
后置通知....执行顺序4--AspectTwo
后置通知....执行顺序3--AspectTwo
后置通知..AspectOne..执行顺序4
后置通知..AspectOne..执行顺序3
Что ж, приоритет AspectJ был разделен, каждый должен быть в состоянии понять, обратившись к выводам, изложенным выше, и посмотрев на следующие два примера. Но, по правде говоря, он используется меньше. (^_^)
3. Сценарии практического применения Spring AOP
На самом деле существует много реальных сценариев применения Spring AOP. Во всяком случае, собственное управление транзакциями Spring фактически использует AOP. Здесь я возьму случай, который относительно близок к разработчикам.мониторинг производительности. По сути, это мониторинг производительности, он не такой уж и высокий, это не что иное, как подсчет времени обращения к веб-интерфейсу. Затем логируйте или пишите на другие платформы мониторинга (гранафа и т.д.) Не говорите ерунды, просто напишите код напрямую:
Сначала определите класс сущности для хранения некоторой информации для мониторинга:
public class MonitorData {
//类名
private String className;
//方法名
private String methodName;
//消耗时间
private String consumeTime;
//记录时间
private Date logTime;
//gettter setter toString什么的省略
....
}
@Aspect
@Component
public class MonitorAspectJ {
/**
* 定义切点函数,过滤controller包下的名称以Controller结尾的类所有方法
*/
@Pointcut("execution(* com..*Controller.*(..))")
void timer() {
}
@Around("timer()")
public Object logTimer(ProceedingJoinPoint thisJoinPoint) throws Throwable {
MonitorData monitorData=new MonitorData();
//获取目标类名称
String clazzName = thisJoinPoint.getTarget().getClass().getName();
//获取目标类方法名称
String methodName = thisJoinPoint.getSignature().getName();
//记录类名称
monitorData.setClassName(clazzName);
//记录对应方法名称
monitorData.setMethodName(methodName);
//记录时间
monitorData.setLogTime(new Date());
// 计时并调用目标函数
long start = System.currentTimeMillis();
Object result = thisJoinPoint.proceed();
Long time = System.currentTimeMillis() - start;
//设置消耗时间
monitorData.setConsumeTime(time.toString());
//把monitorTime记录的信息上传给监控系统,并没有实现,需要自行实现即可
//MonitoruUtils.report(monitorTime)
System.out.println(monitorData.toString());
return result;
}
}
На самом деле это относительно легко понять, это не что иное, как добавление объемного звука ко всем методам всех классов, заканчивающихся на Controller, для записи времени. Затем сохраните его (я только что распечатал).
Применение АОП — это гораздо больше, чем эти два, такие как кэширование, проверка авторизации, обработка контента, управление транзакциями и т. д. Среди них управление транзакциями Spring предоставляет специальные методы обработки, которые будут обсуждаться в первую очередь из-за ограничения пространства.
4. Выбор базовой реализации Spring AOP
Существует два варианта базовой реализации Spring AOP: один — динамический прокси-сервер JDK, а другой — динамический прокси-сервер CGLib. Давайте сначала поговорим о выводе: если у цели, которая будет прокси, есть интерфейс, по умолчанию используется динамический прокси JDK. В противном случае используется динамический прокси CGLib. Конечно, вы также можете принудительно использовать динамический прокси CGLib. метод:
- Конфигурация XML AOP:
- Конфигурация аннотации AOP:
Если проксируемый объект не реализует какой-либо интерфейс, вы должны использовать прокси-сервер CGLIb, если он есть, вы можете использовать прокси-сервер JDK и прокси-сервер CGLib., Что касается того, почему, на самом деле это немного сложно объяснить, и я не собираюсь здесь это расширять. Позже у меня будет возможность написать о динамических прокси, а затем расширить их. Для одноэлементных классов, которые мы обычно используем в веб-проектах, попробуйте использовать динамический прокси-сервер CGLib для реализации Spring AOP.
Возможности CGlib следующие:Технология байт-кода, медленный запуск, индексация для каждого метода, высокая эффективность.Функции динамического прокси JDK:Запуск быстрый, каждый метод вызывается через рефлексию, эффективность низкая, метод не имеет индекса.
Эпилог
Что ж, Spring AOP закончил делиться с вами.Жаль, что базовые динамические прокси JDK и динамические прокси CGLib не были расширены. Но это не имеет значения, на более позднем этапе будет время опубликовать статью о динамических прокси, Я надеюсь, что благодаря двум статьям Spring AOP каждый сможет освоить идею АОП на уровне использования и применять его в работе. Spring, как и Spring boot, фактически использует Spring AOP во многих местах, и в следующих статьях будет указано, если они будут задействованы.Over ,Have a good day .