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裏面如何接管事務