(语法基础)浅谈面向切面编程(aop)(代码片段)

ruanraun ruanraun     2023-05-04     640

关键词:

一:前言

面向切面编程是一个很重要的思想概念,想要写出一个便于维护的程序,理解AOP并且能熟练的在实际编程中合理的运用AOP思想是很有必要的

二:AOP的基本概念

基础概念:AOP中文翻译面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

三:通过动态代理实现AOP

读完上面的概念,也许还不知道什么是AOP,但是我们只少能从上面概念知道实现AOP的技术基础是预编译方式和运行期动态代理,那么我们就来通过实际代码用动态代理来实现一个简单的程序吧

我们在日常的面向对象编程中常常会遇到这样的场景:
???A类(下面的ClassA)中的很多个方法同时都要调用B类(日志类Logger)的同一个方法
如下:

public class ClassA

    public Logger logger;

    public ClassA()
        logger=new Logger();
    

    public void AFuncA()
        //....做一些其他事情
        System.out.println("AFuncA做一些其他事情");
        //记录日志
        logger.WriteLog();
    

    public void AFuncB()
         //....做一些其他事情
        System.out.println("AFuncB做一些其他事情");
        //记录日志
        logger.WriteLog();
    

    public void AFuncC()
         //....做一些其他事情
        System.out.println("AFuncC做一些其他事情");
        //记录日志
        logger.WriteLog();
    


/*
* 日志类
*/
public class Logger

    public void WriteLog()
        System.out.println("我是工具类Logger的WriteLog方法,我记录了日志"));
    


/*
*单元测试Main方法
*/
@Test
public void TestMain()
        ClassA classa=new ClassA();
        classa.AFuncA();
        classa.AFuncB();
        classa.AFuncC();

输出结果

技术图片

???上面代码这个简单的例子相信只要有一点Java,C#等面向对象语言基础的人都能看明白,我们写了一个类ClassA,封装了一个工具类ClassB,然后ClassA三个方法都调用了Logger的WriteLog()方法。
???这样的设计很明显有缺陷,假如我们项目有几百个地方都是用这个WriteLog()方法来记录日志的,一旦我们要更换这个日志记录类或者全部都取消不再记录日志,那么代码修改起来是很苦恼的,很明显ClassA类对Logger类产生了依赖,代码耦合了,那么我们怎么来优化这个类来解除ClassA对Logger的依赖呢,要解决这样的问题就要牵出我们的面向切面编程(AOP)的思想了。
???优化代码如下:我们可以创建一个动态代理工厂对象DynAgentFactory ,然后用基于子类的动态代理对象Enhancer(需要导入cglib依赖包)去代理ClassA对象,然后在使用ClassA时不直接实例化ClassA对象,而是去调用代理工厂对象DynAgentFactory 去调用ClassA类

具体优化代码如下:

public class ClassA

    public void AFuncA()
        //....做一些其他事情
        System.out.println("AFuncA做一些其他事情");
    

    public void AFuncB()
        //....做一些其他事情
        System.out.println("AFuncB做一些其他事情");
    

    public void AFuncC()
        //....做一些其他事情
        System.out.println("AFuncC做一些其他事情");
    
    //.....

/*
*动态代理工厂
*/
public class DynAgentFactory 

    private ClassA proClassA;
    private Logger logger;

    public DynAgentFactory()
        AFunAgent();
        GetLogger();
    

    public ClassA GetClassA()
        return proClassA;
    


    public Logger GetLogger()
        logger=new Logger();
        return logger;
    
    /*
    *代理ClassA对象
    *此处Enhancer类是基于子类的动态代理对象,需要导入cglib依赖(也可以定义一个接口,然后用Proxy实现基于接口的动态代理)
    */
     public void AFunAgent()
        ClassA classA=new ClassA();
        proClassA=(ClassA) Enhancer.create(classA.getClass(), new MethodInterceptor() 
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 
                Object obj=method.invoke(classA);
                logger.WriteLog();
                return obj;
            
        );
    

/*
* 日志类
*/
public class Logger

    public void WriteLog()
        System.out.println("我是工具类Logger的WriteLog方法,我记录了日志");
    

/*
*单元测试Main方法
*/
@Test
public void TestMain()
    DynAgentFactory dynAgentFactory=new DynAgentFactory();
    dynAgentFactory.GetClassA().AFuncA();
    dynAgentFactory.GetClassA().AFuncB();
    dynAgentFactory.GetClassA().AFuncC();

输出结果和之前一样

技术图片

???我们可以看到通动态代理工厂对象DynAgentFactory,我们实现了ClassA与Logger类的解耦,如果以后我们要移除或者更换日志记录类Logger,都只需要统一对DynAgentFactory 里的Logger对象操作即可,相当于把ClassA的公共部分抽离的出来这就前面概念所说的:运行期动态代理

上面的代码是否已经完美了呢?其实还是有一定的问题,那就是我建立的动态代理工厂只是单纯的代理了ClassA,那么假如随着项目的逐渐扩展,我们会新加入ClassB,ClassC...也要用这个统一的记录日志类Logger,我们是否又要重复写一个BFunAgent,CFunAgent的代码去代理记录日志呢,那AFunAgent,BFunAgent,CFunAgent里面的代码不就又重复了吗,所以我们还需要继续优化这个工厂类DynAgentFactory,可以用泛型来解决这个问题

/*
*ClassA代码同上,此处省略
*/
//....
/*
*新加入的ClassB
*/
public class ClassB

    public void BFuncA()
        //....做一些其他事情
        System.out.println("BFuncA做一些其他事情");
    

    public void BFuncB()
        //....做一些其他事情
        System.out.println("BFuncB做一些其他事情");
    

    public void BFuncC()
        //....做一些其他事情
        System.out.println("BFuncC做一些其他事情");
    
    //.....

/*
*泛型动态代理工厂
*/
public class DynAgentFactory<T> 

    private Logger logger;

    public DynAgentFactory(T _proClass)
        TFunAgent(_proClass);
        GetLogger();
    

    private T proClass;

    public T GetProClass()
        return proClass;
    

    public Logger GetLogger()
        logger=new Logger();
        return logger;
    
    /*
    *代理ClassA对象
    *此处Enhancer类是基于子类的动态代理对象,需要导入cglib依赖(也可以定义一个接口,然后用Proxy实现基于接口的动态代理)
    *T pro:传入依赖对象
    */
    public void TFunAgent(T pro)
        proClass=(T) Enhancer.create(pro.getClass(), new MethodInterceptor() 
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 
                Object obj=method.invoke(pro);
                logger.WriteLog();
                return obj;
            
        );
    

/*
*日志类Logger代码同上,此处省略
*/
//...
/*
*单元测试Main方法
*/
@Test
public void TestMain()
    DynAgentFactory<ClassA> dynAgentFactory=new DynAgentFactory(new ClassA());
    dynAgentFactory.GetProClass().AFuncA();
    dynAgentFactory.GetProClass().AFuncB();
    dynAgentFactory.GetProClass().AFuncC();
    System.out.println("-------------------------------分割线------------------------------------------");
    DynAgentFactory<ClassB> dynAgentFactory2=new DynAgentFactory(new ClassB());
    dynAgentFactory2.GetProClass().BFuncA();
    dynAgentFactory2.GetProClass().BFuncB();
    dynAgentFactory2.GetProClass().BFuncC();

输出结果

技术图片
这样进一步优化之后,不管是ClassA,还是ClassB等其他类,如果想要用到Logger类记录日志,都可以用DynAgentFactory来创建对应代理对象就可以了
>这里插入一点题外话:由于Java语言里的泛型完全是由编译器实现的,JVM在这里不提供任何支持,所以不能像C#支持T t=new T()这种写法,所以每次还需要将实例化的对象通过构造函数的方式注入到工厂对象当中去。

动态代理总结

动态代理技术的使用可以更好的降低代码之间的耦合,提升代码功能的专一性,除了上面的全局记录日志之外,我们还可以用它来处理做一些过滤器,通用事务等等功能,知道了动态代理,我们再去回看spring框架中看到的<aop:ADVICE NAME>标签,是不是会对这些标签有更深刻的理解,因为spring aop的实现基础就是动态代理,只不过将代理的前后做了细分,如下为spring通过<aop:ADVICE NAME>元素在一个中声明五个adivce

<aop:config>
   <aop:aspect id="myAspect" ref="aBean">
      <aop:pointcut id="businessService"
         expression="execution(* com.xyz.myapp.service.*.*(..))"/>
      <!-- a before advice definition -->
      <aop:before pointcut-ref="businessService" 
         method="doRequiredTask"/>
      <!-- an after advice definition -->
      <aop:after pointcut-ref="businessService" 
         method="doRequiredTask"/>
      <!-- an after-returning advice definition -->
      <!--The doRequiredTask method must have parameter named retVal -->
      <aop:after-returning pointcut-ref="businessService"
         returning="retVal"
         method="doRequiredTask"/>
      <!-- an after-throwing advice definition -->
      <!--The doRequiredTask method must have parameter named ex -->
      <aop:after-throwing pointcut-ref="businessService"
         throwing="ex"
         method="doRequiredTask"/>
      <!-- an around advice definition -->
      <aop:around pointcut-ref="businessService" 
         method="doRequiredTask"/>
   ...
   </aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

三:扩展和总结

通过上面简单的例子我们了解了通过动态代理实现AOP,但这里我们需要知道的是AOP是一种编程思想,所以通过动态代理实现AOP只是其中一种实现方式,我们还可以通过预编译实现AOP,这里就不得不说一下AspectJ面向切面框架了,AspectJ能够在编译期间直接修改源代码生成class。

什么是AspectJ?此AspectJ非彼@AspectJ

在网上一搜一大片所谓AspectJ的用法,其实都是AspectJ的“切面语法”,只是AspectJ框架的冰山一角,AspectJ是完全独立于Spring存在的一个Eclipse发起的项目,官方关于AspectJ的描述是:
Eclipse AspectJ is a seamless aspect-oriented extension to the Java? programming language. It is Java platform compatible easy to learn and use.

是的,AspectJ甚至可以说是一门独立的语言,我们常看到的在spring中用的@Aspect注解只不过是Spring2.0以后使用了AspectJ的风格而已本质上还是Spring的原生实现,关于这点Spring的手册中有提及:
@AspectJ使用了Java 5的注解,可以将切面声明为普通的Java类。@AspectJ样式在AspectJ 5发布的AspectJ project部分中被引入。Spring 2.0使用了和AspectJ 5一样的注解,并使用AspectJ来做切入点解析和匹配。但是,AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器(weaver)。
因此我们常用的org.aspectj.lang.annotation包下的AspectJ相关注解只是使用了AspectJ的样式,至于全套的AspectJ以及织入器,那完全是另一套独立的东西。至于AspectJ具体要怎么玩儿我也没玩儿过,有兴趣的小伙伴可以自行维基

spring基础aop面向切面编程(代码片段)

1、代理模式代理模式是二十三种设计模式中较为重要的一种,作用是增强被代理对象的功能,保护被代理对象。代理模式分为静态代理与动态代理两种。1.1、静态代理静态代理非常简单,就是通过代理固定的类来增... 查看详情

面向切面编程(aop)(代码片段)

结合设计模式,通过代码理解面向切面编程通过,结构型设计模式,装饰器模式来实现AOP///<summary>///装饰器模式实现静态代理///AOP在方法前后增加自定义的方法///</summary>publicclassDecoratorAOPpublicstaticvoidShow()Useruser=newUser()Name="... 查看详情

aop面向切面编程(个人笔记1.1)(代码片段)

AOP面向切面编程AOP面向切面编程存在的实现方式经典的基于代理的AOP:AspectJAOP面向切面编程AOP(Aspect-OrientedProgramming:面向切面编程)将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权... 查看详情

spring框架--aop面向切面编程(代码片段)

...)–SpringBean管理Spring框架(三)–SpringJDBCSpring框架(四)–AOP面向切面编程Spring框架(五)–Spring事务管理和Spring事务传播行为AOP(AspectOrientedProgramming面向切面编程)不使用AOP的开发方式的例子先定义好接口与一个实现类 查看详情

aop面向切面编程aop简介(aspectj简介|aspectj下载)(代码片段)

...三、AspectJ下载一、AOP简介AOP是AspectOrientedProgramming的缩写,面向切面编程;利用AOP面向切面编程,可以将业务逻辑的各个部分进行隔离,每个业务逻辑部分放在一个切面中实现,降低了各个业务逻辑之间的耦合程度,提高了程序的灵活性,... 查看详情

aop面向切面编程(代码片段)

概念AOP(AspectOrientedProgramming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。AOP利用一种称为"横切"的技术,将那些影响了多个类的公共行... 查看详情

aop面向切面编程(代码片段)

概念AOP(AspectOrientedProgramming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。AOP利用一种称为"横切"的技术,将那些影响了多个类的公共行... 查看详情

面试题思考:解释一下什么叫aop(面向切面编程)(代码片段)

...将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,... 查看详情

前端解读面向切面编程(aop)(代码片段)

前言面向对象(OOP)作为经典的设计范式,对于我们来说可谓无人不知,还记得我们入行起始时那句经典的总结吗-万事万物皆对象。是的,基于OOP思想封装、继承、多态的特点,我们会自然而然的遵循模块化、组件化的思维来设计... 查看详情

基于标注的aop面向切面编程(代码片段)

...是AOP  Aspect Orientied  Programming的简称,即面向(方面)切面编程,不改变一个组件源代码的情况下可以对组件功能进行增强。 例如:servlet中的过滤器,继承,装饰者模式,代理模式,JDK的代理必须有统一接口目标类和代... 查看详情

浅谈aop和代理模式(代码片段)

什么是AOP首先AOP是一种叫面向切面编程的思想,他并不是只用在Spring中,在其他很多AOP的框架中都有用到,如AspectJ,AspectWerkz。众所周知在Java中是面向对象编程,而AOP的面向切面编程更像是对面向对象编程的... 查看详情

spring面向切面编程(aop)(代码片段)

Spring系列教程Spring框架介绍Spring框架模块Spring开发环境搭建(Eclipse)创建一个简单的Spring应用Spring控制反转容器(InversionofControl–IOC)理解依赖注入(DI–DependencyInjection)BeanXML配置(1)-通过XML配置加载BeanBeanXML配置(2)-Bean作... 查看详情

(2021.9.25)面向切面编程简述(代码片段)

说到面向切面编程,想必大家都不会陌生。不就是AOP——AspectOrientProgramming但是说了那么久,你所理解的面向切面编程,用一句话概括到底是什么呢?目录1.什么是AOP(面向切面编程)2.AOP相关术语1.连接点(... 查看详情

(2021.9.25)面向切面编程简述(代码片段)

说到面向切面编程,想必大家都不会陌生。不就是AOP——AspectOrientProgramming但是说了那么久,你所理解的面向切面编程,用一句话概括到底是什么呢?目录1.什么是AOP(面向切面编程)2.AOP相关术语1.连接点(... 查看详情

aop(面向切面编程)初识demo(代码片段)

刚学习了AOP的前值增强和后置增强,个人感觉就是在调用一些方法前,或调用一些方法后绑定一个方法,让这些方法被调用之前或者调用结束后执行这个方法。 例子:MyAdvice类:存放调用service方法前或后需要执行的方法:pub... 查看详情

spring学习5:面向切面编程(aop)(代码片段)

什么是AOPAOP(AspectOrientedProgramming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内... 查看详情

切面编程(代码片段)

...,即通过代理访问目标对象。这样可以在目标对象实现的基础上,增 查看详情

spring万字带你深入学习面向切面编程aop(代码片段)

...如何组织程序结构。  作用:在不惊动原始设计的基础上为其进行功能增强。  首先我们先来看看代码环境,在主方法中获取BookDao对象,并调用它的save()方法,在BookDaoImpl中save()方法是测试它的万次执行效率,... 查看详情