AOP概念:面向切面编程
目的:扩展功能
实现原理:
一个java类到执行过程需要及经过的流程
.java源码文件--------------->.class字节码文件-------------->loadclass->------->实例化,执行方法
很明显,要想扩展(更改代码)在上面几步里面都可以做到,总体将实现原理分为两类
-
静态代理分为:编译时织入(比如aspectj的ajc编译)、类加载时织入(自定义类加载器实现)。
-
动态代理有 : jdk动态代理(基于接口来实现)、CGlib(基于类实现)。
aspectj环境配置
1.下载并安装aspectj
2.
3.添加环境变量
ASPECTJ_HOME
F:\aspectj\aspectj1.9
path
%ASPECTJ_HOME%\bin
4.添加classpath环境变量,这样子方便命令行手工编译以本人为例
F:\aspectj\aspectj1.9\lib\aspectjrt.jar;.
执行ajc-v看看有没有成功
5.设置idea使用ajc编译,如果手工的话上面的足以
步骤介绍
1.启用ajc编译
2.当前项目编译的版本(本人jdk11在maven环境下还未成功过)
3.就是安装aspectj之后的lib包下的aspectjtools.jar
4.在非aspectj时使用javac编译
上面几步已经可以在普通java项目编译了,为什么我会贴图,因为网上好多人虽然也写了,其实照着抄还真不一定跑的起来
6.在maven项目设置插件编译(本人目前在jdk1.8环境成功过)
<!-- 本人本地的是1.9.5,但是编译说版本不对,写必须这个版本 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Maven 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<compilerVersion>${java.version}</compilerVersion>
<fork>true</fork>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- AspectJ 编译插件 -->
<plugin>
<!-- AspectJ 编译插件这个版本也挺玄乎的,这个版本本人试过了可行 -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<verbose>true</verbose>
<privateScope>true</privateScope>
<showWeaveInfo>true</showWeaveInfo>
<source>${java.version}</source>
<target>${java.version}</target>
<complianceLevel>${java.version}</complianceLevel>
<!-- <encoding>UTF-8</encoding> -->
<verbose>false</verbose>
<outxml>true</outxml>
<aspectLibraries>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal> <goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
aspectj快速入门
三个类一起上
public aspect Aop {
void around():execution(void *.*()){
System.out.println("方法执行前");
proceed();//回调函数
System.out.println("方法执行后");
}
}
public class Main{
public static void main(String[] args) {
User user = new User();
user.say();
}
}
public class User{
public void say(){
System.out.println("我系渣渣辉");
}
}
//编译 ajc -d . *.aj *.java
//运行 java Main
//简单说一下,为什么没有包名,这样子编译简单,毕竟命令行
快速入门
1.这里有一个修改用户的方法
@Service
@Slf4j
public class UserService {
@Autowired
private UserMapper userMapper;
public UserService(){
//log.info("创建userService");
}
@Transactional(rollbackFor = Exception.class)
public void updateUser(User user){
userMapper.updateUser(user);
//throw new RuntimeException();
}
}
2.编写aop类
2-1aspectj语法 AspectjAop
public aspect AspectjAop {
/*
* 环绕通知,语法比较固定,后面是表达式,直接加强
* updateUser方法
*/
void around():execution(void com.git.plus.test.service.UserService.updateUser(*)){
System.out.println("===================aspectjAop方法执行前==============");
proceed();//回调函数
System.out.println("===================aspectjAop方法执行后===============");
}
}
2-2 使用java语法 AspectjAopAnnotation
@Aspect
public class AspectjAopAnnotation {
private static final Logger log = LoggerFactory.getLogger(AspectjAopAnnotation.class);
@Pointcut("execution(* com.git.plus.test.service.*.*(..))")
public void servicePoint() {
}
/**
* @param joinPoint
*/
@Around("servicePoint()")
public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("======================AspectjAopAnnotation方法执行前====================");
Object proceed = joinPoint.proceed();
log.info("=======================AspectjAopAnnotation方法执行后=====================");
return proceed;
}
}
在编译之后第一种和第二种等价的,第二种其实和常见的springaop语法一模一样这两种非spring环境可以单独使用,spring环境可以不必将这两个类交给spring管理,这两种必须使用ajc编译
2-3.pringAspectjAop
@Aspect
@Component
public class SpringAspectjAop {
private static final Logger log = LoggerFactory.getLogger(SpringAspectjAop.class);
@Pointcut("execution(* com.git.plus.test.service.*.*(..))")
public void controllerLog() {
}
/**
* @param joinPoint
*/
@Around("controllerLog()")
public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("======================spring-aspectjAop方法执行前====================");
Object proceed = joinPoint.proceed();
log.info("=======================spring-aspectjAop方法执行后=====================");
return proceed;
}
}
现在最常见的是这种形式,在spring里面使用aspectj的注解,这种是基于动态代理来实现的
2-4 springAop,spring的原生实现(不依赖于aspectj的包)
@Component
public class SpringAop extends StaticMethodMatcherPointcutAdvisor implements MethodBeforeAdvice, AfterReturningAdvice {
private static final Logger log = LoggerFactory.getLogger(SpringAop.class);
public SpringAop(){
setAdvice(this);
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
if(targetClass== UserService.class)return true;
return false;
}
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
log.info("=======================SpringAop方法执行后===================");
}
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
log.info("=====================SpringAopAop开始执行========================");
}
}
上面第三种和第四种的实现原理是一样的,但是第三种依赖了aspectj的包,spring对aspectj语法进行接了解析
需要注意的是,第三种在使用ajc编译的时候和第二种差不多,使用javac的时候和第四种差不多
注意事项:
- aspectj和lombok插件有冲突,不要在同一类里面用这两个(因为这两个都是在编译期间搞事情,lombok利用的是注解处理器,可能两者执行顺序有冲突位深究)
- aop各种术语我这里没多说,切点表达式也不想深究,不会百度就有
- 上文demo是在springboot2.2.6环境下,不用加上@EnableAspectJAutoProxy
- 因为是springboot环境,spring原生aop我就直接用代码写,里面东西也挺多。本人也为深究
- gradle项目还未试过
后续发展:
- spring实现aop的流程
- spring里面如何接管事务