springaop和maven中使用aspectj

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編譯,如果手工的話上面的足以

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裏面如何接管事務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章