首页 > 其他分享 >详解面向切面AOP

详解面向切面AOP

时间:2023-03-31 09:55:34浏览次数:30  
标签:target class maven 编译 详解 切面 AOP public

AOP

再看AOP,这是一种面向切面编程思想,相比面向对象编程,可以说是站在更改维度关注对象,我们知道,对象包含由属性和行为。 基于AOP,我们可以把一段代码插入到对象中形成新的对象,这是织入的过程,目的是将公共的内容写入到业务代码中,通过配置或简单的编码完成整个过程。 这样一来不用修改原有的业务代码,同时又能自由完成目标代码的增强,按照代码的设计思想,确实是降低业务与功能的耦合。

大部分框架都是为我们提供切面织入目标过程的封装。

实现

通过该图可以看到AOP相关的实现主要包括ASM、Cglib、JDK Proxy、AspectJ、Javassit,这些实现主要都是对字节码直接操作,只不过对目标对象的增强可以发生在编译时、编译后或运行时。

关于AOP我们说的比较多的就是代理,这属于设计模式的一种,但是AOP真正做的不仅仅是对目标的代理,更多的是修改,像我们常用的代理工具Cglib、JDK Proxy,都是基于面向对象的特性,生成新的 目标对象,通过继承与代理模式来实现最终的增强效果。

在Java中,大部分情况下都是对方法的增强,比如Spring AOP,这样可以解决几乎所有的业务问题;当然切点不局限于类方法,还可以包括字段、方法、构造函数、静态初始值等,比如AspectJ,只不过需要特定的 编译器来实现。


下面我们看下剩下的几项实现AOP的技术,前面说到,Spring AOP主要基于Cglib、JDK Proxy,在运行时实现目标对象的代理。但是Spring中却引入了aspectj相关的依赖,但没有用到AspectJ编译器

JDK Proxy

JDK动态代理,主要是基于目标接口,通过ByteArrayOutputStream直接构建字节数组,最终生成代理接口的实现类,基于InvocationHandler实现代码的扩展与增强,通过反射来调用目标代码的调用。

1.目标接口

public interface HelloService {

    String hello(String name);

}

2.目标实现类

@Slf4j
public class HelloServiceImpl implements HelloService{

    @Override
    public String hello(String name) {
        log.info("+++ 执行方法:hello");
        return String.format("hello, %s", name);
    }
}

3.代理工厂

public class JdkProxyFactory {

    public static <T> T create(Class<T> targetClass, InvocationHandler invocationHandler){
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{targetClass}, invocationHandler);
    }

    @Slf4j
    public static class LogInvocationHandler implements InvocationHandler{

        private Object target;

        public LogInvocationHandler(Object target) {
            this.target = target;
        }

        /**
         *
         * @param proxy
         * @param method
         * @param args
         *
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                log.info(">>> before");
                Object result = method.invoke(target, args); // 执行被代理方法
                log.info(">>> afterReturning : {}", result);
                return result;
            } catch (Throwable e) {
                log.info(">>> afterThrowing : {}", e.getMessage());
                throw e;
            } finally {
                log.info(">>> after");
            }
        }
    }

}

4.执行测试

public class JdkProxyTests {
    
    @Test
    public void testJdkProxy(){
        HelloService helloService = JdkProxyFactory.create(HelloService.class, new JdkProxyFactory.LogInvocationHandler(new HelloServiceImpl()));
        helloService.hello("JDK Proxy");
    }

}

Cglib

Cglib基于目标类来实现代理,已目标类为参考基于ASM直接操作字节码,构造目标对象的子类行,基于MethodInterceptor接口实现目标代码的增强,通过父类调用来执行原目标代码,因此在执行效率上会高于JDK动态代理。

添加依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

1.目标类

@Slf4j
public class HiService {

    public String hi(String name){
        log.info("+++ 执行方法:hi");
        return String.format("hi, %s", name);
    }
}

2.代理工厂

public class CglibFactory{

    /**
     *
     * @param targetClass
     * @param methodInterceptor
     * @return
     * @param <T>
     */
    public static  <T> T create(Class<T> targetClass, MethodInterceptor methodInterceptor){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(methodInterceptor);
        return (T) enhancer.create();
    }

    @Slf4j
    public static class LogMethodInterceptor implements MethodInterceptor {

        /**
         *
         * @param target 目标对象
         * @param method 目标方法
         * @param args 参数
         * @param methodProxy 代理方法,注意执行方式  methodProxy.invokeSuper
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            try {
                log.info(">>> before");
                Object result = methodProxy.invokeSuper(target, args); // 执行被代理方法
                log.info(">>> afterReturning : {}", result);
                return result;
            } catch (Throwable e) {
                log.info(">>> afterThrowing : {}", e.getMessage());
                throw e;
            } finally {
                log.info(">>> after");
            }
        }
    }
}

3.执行测试

public class CglibTests {

    /**
     *
     */
    @Test
    public void testCglib(){
        HiService hiService = CglibFactory.create(HiService.class, new CglibFactory.LogMethodInterceptor());
        hiService.hi("Cglib");
    }

}

AspectJ

AspectJ是一个功能强大的面向切面编程框架,是对Java面向对象的扩展,支持编译时、编译后、加载时为目标对象(不仅仅是类方法)织入代理。

切面织入时机:

  • 编译期织入(compiler-time weaving):在类进行编译的时候就将相应的代码织入到元类文件的.class文件中
  • 编译后织入(post-compiler weaving):在类编译后,再将相关的代码织入到.class文件中
  • 加载时织入(load-time weaving):在JVM加载.class 文件的时候将代码织入

我们可以通过AspectJ编译器或者maven插件aspectj-maven-plugin来实现。

AspectJ编译器

  • 下载

aspectj

  • 安装

java -jar aspectj-1.9.6.jar 配置环境变量PATH与系统变量CLASSPATH

  • 使用

通过下面的命令可实现编译时织入的效果:

#  ajc [Options] [file... | @file... | -argfile file...]
ajc -1.8 -sourceroots .\src\main\java\ -cp %CLASS_PATH% -outjar main.jar

通过ajc编译后并打包成main.jar,即是编译时实现了目标对象的代理,通过反编译工具可以查看到编译后的目标对象已经被修改。

AspectJ使用

编译时织入(Compile-Time Weaving)

编译时织入

1.目标对象:

public class CTWObject {

    public void run() {
        System.out.println("-- Compile-Time Weaving --");
    }

}

2.Aspect:

public aspect CTWAspect {

    pointcut pc():
            execution(* com.sucl.blog.aspectj.target.CTWObject.*());

    before(): pc(){
        System.out.println(" >> before CTW << ");
    }

    void around(): pc(){
        System.out.println(" >> around before CTW << ");
        proceed();
        System.out.println(" >> around before CTW << ");
    }

    after(): pc(){
        System.out.println(" >> after CTW << ");
    }
}

3.配置maven插件 aspectj-maven-plugin

 <!-- 编译期织入 -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.14.0</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <Xlint>ignore</Xlint>
        <encoding>UTF-8</encoding>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

4.执行测试

public class AspectJCTWTests {

    @Test
    public void call() {
        CTWObject CTWObject = new CTWObject();
        CTWObject.run();
    }
}

编译后织入(Post-Compile Weaving)

针对编译好的文件,比如jar中的class文件

1.编写测试的目标对象,并打包成jar文件

public class PCWObject {

    public void run() {
        System.out.println("-- Post-Compile Weaving --");
    }

}

2.引入上面的目标jar

<dependency>
    <groupId>com.sucls.blog</groupId>
    <artifactId>PCW-target</artifactId>
    <version>${project.version}</version>
</dependency>

3.配置maven插件 aspectj-maven-plugin

 <!-- 编译后织入 -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <!--                <version>1.14.0</version>-->
    <version>1.11</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
        <weaveDependencies>
            <weaveDependency>
                <groupId>com.sucls.blog</groupId>
                <artifactId>PCW-target</artifactId>
            </weaveDependency>
        </weaveDependencies>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

4.编译

mvn clean compile

5.执行测试

public class AspectJPCWTests {
    
    @Test
    public void call(){
        PCWObject pcwObject = new PCWObject();
        pcwObject.run();
    }
}

运行时织入(Load-Time Weaving)

配置VM参数

-javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar

或者配置maven-surefire-plugin插件

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <version>2.10</version>
   <configuration>
       <argLine>
           -javaagent:${project.basedir}/lib/aspectjweaver-1.9.7.jar
       </argLine>
       <useSystemClassLoader>true</useSystemClassLoader>
       <forkMode>always</forkMode>
   </configuration>
</plugin>
  • 配置aop.xml

/src/main/resources/META-INF/aop.xml

  <aspectj>
    <aspects>
        <!-- 以@Aspect形式编写切面(aj需要对应编译器编译)-->
        <aspect name="com.sucl.blog.aspectj.aspect.LogAspect"/>
    </aspects>
</aspectj>

启动测试

public class AspectJLTWTests {

    @Test
    public void call(){
        LTWObject LTWObject = new LTWObject();
        LTWObject.run();
    }
}

结束语

不管是javassit,还是jdk proxy或者cglib来实现AOP,都是通过对字节码的修改,只不过对字节码操作方式不一样。通过上面的例子我们可以认识到各种AOP框架的使用方式。在究其原理时, 能够能够知道这些工具到底为我们做了什么。

标签:target,class,maven,编译,详解,切面,AOP,public
From: https://www.cnblogs.com/kenmeon/p/17275288.html

相关文章

  • Spring AOP官方文档学习笔记(一)之AOP概述
    1.AOP简介(1)Spring的关键组件之一就是AOP框架,它是对SpringIoC的补充(这意味着如果我们的IOC容器不需要AOP的话就不用引入AOP),此外,AOP亦是对OOP的补充,OOP的关注点在于类,而AOP的关注点在于切面,它可以将分散在不同类不同方法中重复的代码逻辑抽取出来,称之为通知(Advice),然后在运行......
  • 谷歌收录网站要多久,谷歌网站收录入口详解
    答:谷歌收录网站通常在提交网站后的几小时到几周之间随着互联网的快速发展,越来越多的网站应运而生。对于网站的运营者来说,被搜索引擎收录是非常重要的,因为这将直接影响网站的访问量和知名度。而谷歌作为全球最大的搜索引擎之一,自然也成为了网站运营者们关注的重点。本文将为您详细介......
  • 22、MySQL主从复制详解及配置
    主从复制的介绍MySQL主从复制是指将一个MySQL服务器的数据复制到其他MySQL服务器上的过程。在主从复制中,一个MySQL服务器(称为“主服务器”或“主节点”)充当源,另一个或多个MySQL服务器(称为“从服务器”或“从节点”)充当目标。主服务器将更新和更改记录到二进制日志(binaryl......
  • 【入门】Go语言Map集合详解
    目录一、Map集合介绍1.1什么是Map集合?1.2Map集合语法二、Map基本操作2.1定义Map2.2获取Map2.3修改Map元素2.4删除Map元素2.5遍历Map三、案例3.1案例一3.2案例二一、Map集合介绍1.1什么是Map集合?map集合是一种无序的键值对的集合map是通过key来快速检索数据,key类似......
  • Java并发编程——Thread详解
    前言操作系统中,一个进程往往代表着一个应用程序实例,而线程是进程中轻量级的调度单元,也可以看作是轻量级的进程,可以共享进程资源。下面简单介绍在操作系统中线程通用实现方式。接下来内容主要对线程模型进行简单介绍,然后对Java线程实现Thread类进行了解。一、线程模型暂且抛开Jav......
  • Mybatis面向切面编程(AOP)的applicationContext配置文件
    面向切面编程(AOP)applicationContext.xml配置文件<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="ht......
  • 微服务架构详解(史上最全图文解读)
    微服务架构详解(史上最全图文解读) 目录微服务架构定义微服务架构特点什么时候需要微服务架构微服务架构组件微服务架构有哪些SpringCloudSpringCloud......
  • Aop面向方面编程之PostSharp框架
    官网:https://www.postsharp.net/aop.net/msil-injection向切面编程的概念已经盛行很久了,可以很好的将我们代码的各个关注方面分离开来.比如:事务,日志,异常处理,数......
  • 一文详解ODBC、OLEDB、ADO、ADO.NET之间的关系
    目录一、ODBC二、OLEDB三、ADO四、ADO.NET五、总结名词解释版权声明推荐阅读相信看到这篇文章的人,心中肯定有这样的想法:ODBC、OLEDB、ADO、ADO.NET貌似都是访问数据库的......
  • mysql导出表结构,数据,mysqldump详解
    mysqldump定义:mysqldump是MySQL系统自带的逻辑备份工具,在对数据库进行导出工作时,经常会用到mysqldump。   mysqldump作用:mysqldump主要产生一系列的SQL语句。例如:备......