栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Aspect.(aspect的用法)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Aspect.(aspect的用法)

一、AOP介绍

AOP:Aspect-Oriented Programming,面向切面编程,是一种新的方法论(编程范式),是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。旨在通过允许横切关注点的分离,提高模块化。如在方法执行前、或执行后、或是在执行中出现异常后这些地方进行拦截处理或叫做增强处理。主要应用于:日志收集、事务管理、安全检查、缓存、对象池管理等。

AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理(例如:spring aop)和动态代理(例如:原生AspectJ)两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。

AOP基本概念:

切入点(pointcut):在哪些类、哪些方法上切入,通常是一个正则表达式执行点(JoinPoint):通过pointcut选取出来的集合中的具体的一个执行点,我们就叫JoinPoint通知(advice):在方法前、方法后、方法前后、异常等做什么。切面(aspect):切面 = pointcut + advice。即在什么时机、什么地方、做什么。织入(weaving):把切面加入对象,并创建出代理对象的过程。 二、AspectJ介绍

AspectJ:全称Eclipse AspectJ,官网The AspectJ Project | The Eclipse Foundation 是Java社区里最完整最流行的AOP框架,即AOP的java版实现,它定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。(除了AspectJ外,还有很多AOP实现,例如ASMDex)

aspectJ可以单独使用,也可以整合到其它框架中。单独使用AspectJ时需要使用专门的编译器ajc。java的编译器是javac,AspectJ的编译器是ajc,aj是首字母缩写,c即compiler。

1、AspectJ原理:

AspectJ属于静态织入,通过修改代码来实现,有如下几个织入的时机:

    ​编译期织入(Compile-time weaving): 如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就需要编译期的时候就进行织入,否则没法编译类 B。编译后织入(Post-compile weaving): 也就是已经生成了 .class 文件,或已经打成 jar 包了,这种情况我们需要增强处理的话,就要用到编译后织入。类加载后织入(Load-time weaving): 指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法。1、自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就可以在加载的时候定义行为了。2、在 JVM 启动的时候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar。

AspectJ可以做Spring AOP干不了的事情,它是AOP编程的完全解决方案,Spring AOP则致力于解决企业级开发中最普遍的AOP(方法织入)。而不是成为像AspectJ一样的AOP方案。因为AspectJ在实际运行之前就完成了织入,所以说它生成的类是没有额外运行时开销的。

Spring AOP

AspectJ

在纯 Java 中实现

使用 Java 编程语言的扩展实现

不需要单独的编译过程

除非设置 LTW,否则需要 AspectJ 编译器 (ajc)

只能使用运行时织入

运行时织入不可用。支持编译时、编译后和加载时织入

功能不强-仅支持方法级编织

更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等......。

只能在由 Spring 容器管理的 bean 上实现

可以在所有域对象上实现

仅支持方法执行切入点

支持所有切入点

代理是由目标对象创建的, 并且切面应用在这些代理上

在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入

比 AspectJ 慢多了

更好的性能

易于学习和应用

相对于 Spring AOP 来说更复杂

在实际生产中,我们用得最多的还是 Spring AOP。

2、AspectJ的使用:https://javadoop.com/post/aspectj

前面介绍了 Spring AOP 的各种用法,包括随着 Spring 的演进而发展出来的几种配置方式。但是我们始终没有使用到 AspectJ,即使是在基于注解的 @AspectJ 的配置方式中,Spring 也仅仅是使用了 AspectJ 包中的一些注解而已,并没有依赖于 AspectJ 实现具体的功能,接下来将介绍AspectJ的使用。

2.1)代码如下

1)maven依赖:


	   org.aspectj
	   aspectjrt
	   1.8.13


	    org.aspectj
	    aspectjweaver
	    1.8.13

2)定义一个类,对其进行weaving:

package a.b.c.tftest.model;

public class Account {
    public int balance = 20;

    public boolean pay(int amount) {
        if (balance < amount) {
            return false;
        }
        balance -= amount;
        return true;
    }
}

3)接下来,我们定义两个Aspect来进行weaving演示:

A、AccountAspect:

用 AspectJ 的语法来写,对交易进行拦截,如此次交易超过余额,直接拒绝。AccountAspect 需要以 .aj 结尾,如我们在a.b.c.tftest.aspectj 下新建文件 AccountAspect.aj,内容如下:

package a.b.c.tftest.aspectj;

import a.b.c.tftest.model.Account;

public aspect AccountAspect {
    pointcut callPay(int amount, Account account):
            call(boolean a.b.c.tftest.model.Account.pay(int)) && args(amount) && target(account);

    before(int amount, Account account): callPay(amount, account) {
        System.out.println("[AccountAspect]付款前总金额: " + account.balance);
        System.out.println("[AccountAspect]需要付款: " + amount);
    }

    boolean around(int amount, Account account): callPay(amount, account) {
        if (account.balance < amount) {
            System.out.println("[AccountAspect]拒绝付款!");
            return false;
        }
        return proceed(amount, account);
    }

    after(int amount, Account balance): callPay(amount, balance) {
        System.out.println("[AccountAspect]付款后,剩余:" + balance.balance);
    }
}

 B、ProfilingAspect:用 Java 来写,用于记录方法的执行时间

package a.b.c.tftest.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class ProfilingAspect {
    
    @Pointcut("execution(* a.b.c.tftest.model.*.*(..))")
    public void modelLayer() {
    }

    @Around("modelLayer()")
    public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        System.out.println("[ProfilingAspect]方法: 【" + joinPoint.getSignature() + "】结束,用时: " + (System.currentTimeMillis() - start));

        return result;
    }
}

4)定一个主类:

package a.b.c.tftest;

import a.b.c.tftest.model.Account;
import cn.edu.nuc.test.User;

public class App {
    public static void main(String[] args) {
        testCompileTime();
    }
    public static void testCompileTime() {
        Account account = new Account();
        System.out.println("==================");
        account.pay(10);
        account.pay(50);
        System.out.println("==================");
    }
}

可以看到:AspectJ的语法比较难理解,使用AspectJ提供的注解来写还是比较方便的(Spring AOP也是使用了AspectJ提供的注解)。代码结构如下:

接下来,我们讨论怎么样将定义好的两个 Aspects 织入到我们的 Account 的付款方法 pay(amount) 中,也就是三种织入时机分别是怎么实现的。

2.2【Complie-Time Weaving】示例

这是最简单的使用方式,在编译期的时候进行织入,这样编译出来的 .class 文件已经织入了我们的代码,在 JVM 运行的时候其实就是加载了一个普通的被织入了代码的类。

1)aspject-maven插件:

采用 maven 进行管理,可以在 中加入以下的插件:


    tftest
    
        
        
            org.codehaus.mojo
            aspectj-maven-plugin
            1.11
            
                1.8
                1.8
                1.8
                true
                true
                ignore
                UTF-8
            
            
                
                    
                        false
                    
                    
                        compile
                    
                
            
        
        
        
            org.apache.maven.plugins
            maven-assembly-plugin
            2.4
            
                
                    
                        a.b.c.tftest.Test
                    
                
                
                    assembly/assembly.xml
                
                  
                    jar-with-dependencies  
                  
            
            
                
                    make-assembly
                    package
                    
                        single
                    
                
            
         
    
    
    
    
        
        
            org.eclipse.m2e
            lifecycle-mapping
            1.0.0
            
                
                    
                        
                            
                                org.codehaus.mojo
                                aspectj-maven-plugin
                                [1.0,)
                                
                                    test-compile
                                    compile
                                
                            
                            
                                
                            
                        
                    
                
            
        
    

说明:如果是eclipse,使用这个插件可能会报如下错误:Plugin execution not covered by lifecycle configuration: org.codehaus.mojo:aspectj-maven-plugin:1.11:compile (execution: default, phase: compile) ,解决方法就是按照上面方式添加一个

2)然后通过mvn package打包:

编译后,可以看到AccountAspect.aj已经被编译成了class,如下:

 此外,App代码也被修改了:

Account类也被修改了:

 执行App后,输出:

 2.3 【Post-Compile Weaving】示例:

Post-Compile Weaving 和 Compile-Time Weaving 非常类似,我们也是直接用场景来说。

假设上面的 Account 类在test.jar 包中,我们的工程 tftest.jar依赖了这个 jar 包。由于 Account 这个类已经被编译出来了,我们要对它的方法进行织入,就需要用到编译后织入。

为了方便测试,新建一个test工程,里面就一个类 User,代码和 Account 一样,mvn package打成test.jar包,里面就这一个User类。同时在tftest工程里引入test.jar,然后也复制 AccountAspect 一份出来,命名为 UserAspect,稍微修改修改就可以用来处理 User 类了。

1)test工程代码:

mvn信息如下:




  4.0.0

  cn.edu.nuc
  test
  0.0.1-SNAPSHOT
  test

  
    UTF-8
    1.8
    1.8
  

  
  

  
    
      
        
          maven-clean-plugin
          3.1.0
        
        
          maven-compiler-plugin
          3.8.0
        
        
          maven-jar-plugin
          3.0.2
        
        
          maven-install-plugin
          2.5.2
        
        
          maven-deploy-plugin
          2.8.2
        
      
    
  

代码如下: 

package cn.edu.nuc.test;

public class User {
    public int balance = 20;

    public boolean pay(int amount) {
        if (balance < amount) {
            return false;
        }
        balance -= amount;
        return true;
    }
}

然后使用mvn install 将test.jar安装到本地仓库。

2)tftest工程:

首先引入test依赖


    	cn.edu.nuc
		test
		0.0.1-SNAPSHOT

然后,添加UserAspect类:

package a.b.c.tftest.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class UserAspect {
    @Pointcut("execution(* cn.edu.nuc.test.*.*(..))")
    public void modelLayer() {
    }

    @Around("modelLayer()")
    public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        System.out.println("[userAspect]方法: 【" + joinPoint.getSignature() + "】结束,用时: " + (System.currentTimeMillis() - start));

        return result;
    }
}

最后修改App主类:

public class App {
    public static void main(String[] args) {
        testCompileTime();
    }
    public static void testCompileTime() {
        User user = new User();
        user.pay(10);
    }
}

3)修改插件:


    tftest
    
        
        
            org.codehaus.mojo
            aspectj-maven-plugin
            1.11
            
                1.8
                
                    
                        cn.edu.nuc
                        test
                    
                
            
            
                
                    
                        compile
                    
                
            
        
    
        
            org.apache.maven.plugins
            maven-assembly-plugin
            2.4
            
                
                    
                        a.b.c.tftest.Test
                    
                
                
                    assembly/assembly.xml
                
                  
                    jar-with-dependencies  
                  
            
            
                
                    make-assembly
                    package
                    
                        single
                    
                
            
         
    
    
    
    
        
        
            org.eclipse.m2e
            lifecycle-mapping
            1.0.0
            
                
                    
                        
                            
                                org.codehaus.mojo
                                aspectj-maven-plugin
                                [1.0,)
                                
                                    test-compile
                                    compile
                                
                            
                            
                                
                            
                        
                    
                
            
        
    


然后执行,mvn clean package 最后,运行App主类,输出:

[userAspect]方法: 【boolean cn.edu.nuc.test.User.pay(int)】结束,用时: 1

从输出上看,UserAspect 对 User 进行了织入。而且是在User已经编译后进行的织入。

2.4 【Load-Time Weaving】示例:

最后,我们要介绍的是 LTW 织入,正如 Load-Time 的名字所示,它是在 JVM 加载类的时候做的织入。AspectJ 允许我们在启动的时候指定 agent 来实现这个功能。

这里还是用到了最初的tftest工程,首先,要注释掉之前在 pom.xml 中用于编译期和编译后织入使用的插件,免得影响我们的测试。(一旦我们去掉了 aspectj 的编译插件,那么 .aj 的文件是不会被编译的)

1)main/resource/meta-INF下建立aop.xml文件



  
    
    
      
    
  

定义了切面类和要织入的目标类范围。

2)切面类:

package a.b.c.tftest.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class ProfilingAspect {
    @Pointcut("execution(* a.b.c.tftest.model.*.*(..))")
    public void modelLayer() {
    }

    @Around("modelLayer()")
    public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        System.out.println("[ProfilingAspect]方法: 【" + joinPoint.getSignature() + "】结束,用时: " + (System.currentTimeMillis() - start));

        return result;
    }
}

目标类:Account.java 同上

 3)App主类:

package a.b.c.tftest;

import a.b.c.tftest.model.Account;

public class App {
    public static void main(String[] args) {
        testCompileTime();
    }
    public static void testCompileTime() {
        Account account = new Account();
        System.out.println("==================");
        account.pay(10);
        account.pay(50);
        System.out.println("==================");

    }
}

4)pom.xml文件:




  4.0.0

  a.b.c
  tftest
  0.0.1-SNAPSHOT
  tftest

  
    UTF-8
    1.8
    1.8
  

  
    
    
       org.aspectj
       aspectjrt
       1.8.13
    
    
        org.aspectj
        aspectjweaver
        1.8.13
    
  

  
        tftest
        
            
                org.apache.maven.plugins
                maven-assembly-plugin
                2.4
                
                    
                        
                            a.b.c.tftest.App
                        
                    
                    
                        assembly/assembly.xml
                    
                      
                        jar-with-dependencies  
                      
                
                
                    
                        make-assembly
                        package
                        
                            single
                        
                    
                
             
        
  

5) 编译/打包:

代码结构如下,

 使用mvn package打包后,反编译结构如下:

可以看到,编译后没有进行织入。

然后执行:

java -jar ./target/tftest-jar-with-dependencies.jar

输出:

==================

==================

然后执行:

java -javaagent:/Users/knowliu/documents/mvn_repo/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar -jar ./target/tftest-jar-with-dependencies.jar

[AppClassLoader@18b4aac2] info AspectJ Weaver Version 1.8.13 built on Wednesday Nov 15, 2017 at 19:26:44 GMT

[AppClassLoader@18b4aac2] info register classloader sun.misc.Launcher$AppClassLoader@18b4aac2

[AppClassLoader@18b4aac2] info using configuration file:/Users/knowliu/documents/workspace1/tftest/target/tftest-jar-with-dependencies.jar!/meta-INF/aop.xml

[AppClassLoader@18b4aac2] info register aspect a.b.c.tftest.aspectj.ProfilingAspect

[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(boolean a.b.c.tftest.model.Account.pay(int))' in Type 'a.b.c.tftest.model.Account' (Account.java:11) advised by around advice from 'a.b.c.tftest.aspectj.ProfilingAspect' (ProfilingAspect.java)

输出:

==================

[ProfilingAspect]方法: 【boolean a.b.c.tftest.model.Account.pay(int)】结束,用时: 1

[ProfilingAspect]方法: 【boolean a.b.c.tftest.model.Account.pay(int)】结束,用时: 0

==================

参考:https://javadoop.com/post/aspectj 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/776687.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号