共计 5506 个字符,预计需要花费 14 分钟才能阅读完成。
AOP 和 Aspect 是什么?
AOP 即 Aspect Orient Programming 是以一种编程范式,在不同业务中横着切一刀造成一个切面,在此切面上做一些雷同的事件。Aspect 就是切面。
规定了一些概念性的货色:
- Pointcut:是一个(组)基于正则表达式的表达式,有点绕,就是说他自身是一个表达式,然而他是基于正则语法的。通常一个 pointcut,会选取程序中的某些咱们感兴趣的执行点,或者说是程序执行点的汇合。
- JoinPoint:通过 pointcut 选取进去的汇合中的具体的一个执行点,咱们就叫 JoinPoint.
- Advice:在选取进去的 JoinPoint 上要执行的操作、逻辑。对于5种类型,我不多说,不懂的同学本人补根底。
- Aspect:就是咱们关注点的模块化。这个关注点可能会横切多个对象和模块,事务管理是横切关注点的很好的例子。它是一个形象的概念,从软件的角度来说是指在应用程序不同模块中的某一个畛域或方面。又 pointcut 和 advice 组成。
- Weaving:把切面利用到指标对象来创立新的 advised 对象的过程。
这些都是概念性的货色。本意是冀望通过抽离雷同业务逻辑,通过将加强代码动静织入(将加强代理和原有代码联合到一起造成加强后的代码),实现一次编写到处应用。
动静代理和 AOP 关系
动静代理跟 AOP 的思维跟代理有相似之处,都是代码复用。一套加强代码复用到不同中央。区别在于指定复用的形式不同。
动静代理指定加强代码复用在哪里是通过手动编写被代理类来的,而 AOP 则是申明式的,之后通过其余形式主动创立出代理类。相似于一个是 命令式 ,一个是 申明式。AOP 是动静代理的一次简化,暗藏了实现细节,让编写和应用动静代理更加简便。
但无论怎么简化,基本都是要生成代理类,只不过这个过程是编译期做,还是在运行时做,是开发人员手动编写还是框架主动生成。
AspectJ 就是编译期生成,能够由开发人员手动执行命令,也能够放在 maven 等主动执行。
Spring AOP 则是应用 JDK 或 CGLib 动静代理,在运行时动静生成的。
AspectJ 和 Spring AOP 都是什么关系
AspectJ 是 eclipse 下的我的项目,是一个 AOP 的实现框架,是一种对 Java 语言的扩大语言,在编译器将原来的 Java 代码中织入加强代码,生成加强后的 class 文件。
AspectJ 分为
- 编译时织入:通过 ajc 用编译生成 class 文件
- 编译后织入:曾经 javac 生成 class 文件后,通过解决 class 文件失去新的织入后的 class 文件
- 加载时织入(LTW):通过 java agent 机制在内存中操作类文件,能够不须要 ajc 的反对做到动静织入。
AspectJ 有本人的语法和编译命令,pointcut 定义切点,after 定义告诉等。
上面是编译时织入的例子
// 定义被加强类
public class App {public void say() {System.out.println("App say");
}
public static void main(String[] args) {App app = new App();
app.say();}
}
// 定义切面
public aspect AjAspect {
// 切入点
pointcut say():
execution(* App.say(..));
// 前置告诉
before(): say() {System.out.println("AjAspect before say");
}
// 后置告诉
after(): say() {System.out.println("AjAspect after say");
}
}
AspectJ 语法是 Java 的扩大,所以 javac 无奈编译,须要应用 AspectJ 提供的编译命令 ajc:
#!/usr/bin/env bash
ASPECTJ_TOOLS=/home/myths/.m2/repository/org/aspectj/aspectjtools/1.8.9/aspectjtools-1.8.9.jar
ASPECTJ_RT=/home/myths/.m2/repository/org/aspectj/aspectjrt/1.8.9/aspectjrt-1.8.9.jar
java -jar $ASPECTJ_TOOLS -cp $ASPECTJ_RT -sourceroots .
生成 class 文件:
// AjAspect 文件
import java.io.PrintStream;
import org.aspectj.lang.NoAspectBoundException;
public class AjAspect
{
private static Throwable ajc$initFailureCause;
public static final AjAspect ajc$perSingletonInstance;
public static AjAspect aspectOf()
{if (ajc$perSingletonInstance == null) {throw new NoAspectBoundException("AjAspect", ajc$initFailureCause);
}
return ajc$perSingletonInstance;
}
public static boolean hasAspect()
{return ajc$perSingletonInstance != null;}
private static void ajc$postClinit()
{ajc$perSingletonInstance = new AjAspect();
}
static
{
try
{ }
catch (Throwable localThrowable)
{ajc$initFailureCause = localThrowable;}
}
public void ajc$before$AjAspect$1$682722c()
{System.out.println("AjAspect before say");
}
public void ajc$after$AjAspect$2$682722c()
{System.out.println("AjAspect after say");
}
}
// App.class 文件
import java.io.PrintStream;
public class App
{public void say()
{
try
{
// 先调用 AjAspect 的 before,在执行原有的 say 办法内容
AjAspect.aspectOf().ajc$before$AjAspect$1$682722c();System.out.println("App say");
}
catch (Throwable localThrowable)
{
// 最初调用 AjAspect 的 after
AjAspect.aspectOf().ajc$after$AjAspect$2$682722c();throw localThrowable;
}
AjAspect.aspectOf().ajc$after$AjAspect$2$682722c();
}
public static void main(String[] args)
{App app = new App();
app.say();}
}
Spring AOP 经常与 AspectJ 混在一起,因为 Spring AOP 应用了 AspectJ 的注解,如:
package com.ywsc.fenfenzhong.aspectj.learn;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {@After("execution(* com.ywsc.fenfenzhong.aspectj.learn.SayHelloService.*(..))")
public void log(){System.out.println("记录日志 ...");
}
}
代码中的 @Aspect、@After 都是 org.aspectj.lang.annotation 包中的注解。
尽管 spring 中应用了这些 AspectJ 的注解定义 AOP,但理论织入则用的是动静代理,是运行时动静执行的,而没用应用 AspectJ 在编译器织入。
Spring 中通过解析标注有 @Aspect 注解的类,通过反射和动静代理的形式生成代理类实现加强。
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {String value() default "";
}
编写 Aspect 切面:
@Aspect
@Component
public class SysLogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(SysLogAspect.class);
@Pointcut("@annotation(com.aldeo.common.annotation.Log)")
public void logPointCut() {}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {long beginTime = System.currentTimeMillis();
// 指标办法
Object result = point.proceed();
long time = System.currentTimeMillis() - beginTime;
Log syslog = method.getAnnotation(Log.class);
if (syslog != null) {
// 注解上的形容
LOGGER.info(syslog.value());
}
// 保留日志
try {saveLog(point, time);
} catch (Exception e) {LOGGER.error("==================================> saveSysLog.around.exception:" + e.getMessage(), e);
}
return result;
}
在指定办法上标注注解,应用切面,实现 AOP 编程
@Log("测试自定义注解")
public String restPassword(){return "胜利";}
Springboot 中的 AOP 实现是基于 JDK 还是 CGLib?
默认是:
- 如果代理对象有接口,就用 JDK 动静代理
- 如果代理对象没有接口,那么就间接是 Cglib 动静代理。
SpringBoot 2.0 之后提供了一个配置项 spring.aop.proxy-target-class,他的主动配置文件中能够看到:
@Configuration
@ConditionalOnClass({EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class})
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration { }
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {}}
如果此配置没有配置,或配置成了 true,都是用 CGlib,只有配置成 false 时才应用 JDK。
参考:
- 原生 AspectJ 用法剖析以及 Spring-AOP 原理剖析:https://blog.mythsman.com/post/5d301cf2976abc05b34546be/
- Spring Boot 中的 AOP,到底是 JDK 动静代理还是 Cglib 动静代理?:https://bbs.huaweicloud.com/blogs/detail/314044
- Spring AOP,AspectJ, CGLIB 有点晕:https://www.jianshu.com/p/fe8d1e8bd63e
本文由 mdnice 多平台公布