• AOP思想的一点想法


    一、AOP的思想

    都说AOP是OOP的一种升华,我觉得AOP实际上就是在OOP的基础上进行了一次封装,下面的图我觉得画的非常好。

    图片转载于https://blog.csdn.net/q982151756/article/details/80513340

    图中的贷款申请、贷款管理和入出金管理都包含相同的业务处理,我们把业务处理发生的位置叫做Join Point。假如我们的项目有上百个业务模块,那我们就需要写上百个Join Point,很显然这是不合理的。

    我们可以写一个切面类(Aspect),将需要进行的业务(Advice)放在切面类中。现在我们将这些相同的业务处理单独拿了出来,代码的耦合度降低了,不需要重复写上百次,但是,我们怎么把它们放进各个业务模块里执行呢?

    切点(Point Cut)可以解决这个问题,我们可以通过切入点表达式,定义我们需要进行的业务(Advice)将会在哪些Join Point执行。比如,我们需要在贷款申请和贷款管理中执行相同的业务,那么贷款申请和贷款管理的业务类本身,我们称为Target,将服务性代码和业务性代码动态结合的这个过程,我认为就是织入(Weaving)

    二、Spring是如何实现AOP的?

    Spring使用代理来实现AOP,为什么需要使用代理?很简单,因为我们自己做不好,而我们的代理能帮我们做的很好,也就是说代理是对我们本身的一种增强。

    AOP示例项目结构如下,删去了一些不必要的分支。

    AOP包对应静态代理,即一个业务类对应一个切面类。

    AOP2包对应动态代理,将切面类抽象出来,当我们需要使用切面类时进行调用即可。

    IDE:IDEA 2019.1

    JDK:1.8

    ├───src
    │   └───main
    │       ├───java
    │       │   ├───AOP
    │       │   │       FuWu.java
    │       │   │       MyInvocationHandler.java
    │       │   │       Test.java
    │       │   │       TestInterface.java
    │       │   │       YeWu.java
    │       │   │       
    │       │   └───AOP2
    │       │           DaGuanSi.java
    │       │           LvShi.java
    │       │           MyInvocationHandler.java
    │       │           QuQi.java
    │       │           TestDaGuanSi.java
    │       │           
    │       ├───resource
    │       │       ApplicationContext.xml
    │       │       ApplicationContext2.xml
    │       │       log4j.properties

    1、静态代理

    这个示例项目中我写了两个Spring配置文件,一个是注解方式实现的0配置,一个是在xml中注册业务类、切面类以及使用<aop:aspect>进行织入。

    ApplicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
        <aop:aspectj-autoproxy proxy-target-class="false" />
        <!--切面类-->
        <bean id="fw" name="fuwu" class="AOP.FuWu"></bean>
        <!--业务类-->
        <bean id="yw" name="yewu" class="AOP.YeWu"></bean>
    
        <!--织入-->
        <aop:config>
            <aop:aspect ref="fw">
                <aop:before method="fun1" pointcut="execution(* AOP.YeWu.*(..))"></aop:before>
                <aop:after method="fun2" pointcut="execution(* AOP.YeWu.*(..))"></aop:after>
                <aop:around method="around" pointcut="execution(* AOP.YeWu.*(..))"></aop:around>
            </aop:aspect>
        </aop:config>
    
    </beans>

    ApplicationContext2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
        <!--自动扫描AOP包下通过注解配置的组件-->
        <context:component-scan base-package="AOP2"></context:component-scan>
    
        <!--启用AspectJ注解的支持-->
        <aop:aspectj-autoproxy proxy-target-class="true" />
    
    </beans>

    比如我现在要打官司,那我就需要定义“我”这个类,并实现打官司这个接口。

    “我”

    package AOP2;
    
    import org.springframework.stereotype.Component;
    
    /**
     * 我要打官司,所以我实现了打官司这个接口
     */
    @Component
    public class QuQi implements DaGuanSi {
        @Override
        public void souJiZhengJu() {
            System.err.println("我自己搜集证据");
        }
    
        @Override
        public void xieSuZhuang() {
            System.err.println("我自己写诉状");
        }
    }

    “打官司”接口

    package AOP2;
    
    /**
     * 打官司接口
     */
    public interface DaGuanSi {
        //搜集证据
        void souJiZhengJu();
    
        //写诉状
        void xieSuZhuang();
    }

    但我自己打官司打的并不好,由于没有法律知识,我的诉状并不规范,我搜集证据的能力并不足。所以我们需要“律师”来帮我们打官司。

    package AOP2;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    /**
     * 我自己打官司打不赢,请律师来帮我打
     */
    @Component
    @Aspect
    public class LvShi implements DaGuanSi {
        @Override
        @After(value = "execution(* AOP2.QuQi.souJiZhengJu(..))")
        public void souJiZhengJu() {
            System.err.println("律师帮我搜集证据");
        }
    
        @Override
        @After(value = "execution(* AOP2.QuQi.xieSuZhuang(..))")
        public void xieSuZhuang() {
            System.err.println("律师帮我写诉状");
        }
    }

    那我们来写一个测试类,看看律师帮我打官司打的如何了。

    package AOP2;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.lang.reflect.Proxy;
    
    /**
     * 打官司测试类
     */
    public class TestDaGuanSi {
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext2.xml");
            staticProxy(ac);
        }
    
        /**
         * 抽象后的织入
         */
        private static void dynamicProxy() {
            //创建一个打官司的人
            QuQi me = new QuQi();
            //获取类
            Class c = me.getClass();
            //获取类加载器
            ClassLoader cl = c.getClassLoader();
            //获取类实现的接口
            Class[] interfaces = c.getInterfaces();
    
            //创建一个代理类的对象,和被代理对象关联起来,其实就是我要去打官司,我现在就是在找律师
            MyInvocationHandler mih = new MyInvocationHandler(me);
    
            //获取指定接口的代理类实例
            DaGuanSi newProxy = (DaGuanSi) Proxy.newProxyInstance(cl, interfaces, mih);
            newProxy.xieSuZhuang();
            newProxy.souJiZhengJu();
            System.err.println(newProxy.getClass());
        }
    
        /**
         * 传统的织入
         * @param ac
         */
        private static void staticProxy(ApplicationContext ac) {
            //这里由于我们使用注解配置,不能像之前ac.getBean("yw")那样获取Bean了,所以这里只能使用cglib代理方式
            QuQi me = ac.getBean(QuQi.class);
            me.souJiZhengJu();
            me.xieSuZhuang();
            System.err.println(me.getClass().getName());
        }
    }

    测试结果如下:

    从结果可以看到,我们的切面类已经成功织入业务类。代理有JDK代理和cglib代理,这里我们使用的cglib代理。

    那我们想一想,如果还有其他人不擅长打官司找律师帮忙呢?那我们就要为其他人专门写新的律师类,很显然这是不合理的。

    动态代理就是帮助我们解决这个问题的,我们专门为代理写一个MyInvocationHandler类,该类实现InvocationHandler接口。

    比如这次C要来打官司,那我们就调用这个类,同时通过构造方法将C这个人传入这个代理类,和被代理对象关联起来。

    在测试类中执行一下,结果如下:

     那下次别人再想要打官司,他们就只需要找我们抽象出来的这个律师就可以了,流程更加合理。

  • 相关阅读:
    AngularJS各种'service'的区别
    js 斐波那契数列实现
    Mac下Apache+MySQL+PHP开发环境安装过程
    原生封装的js前端工具库(包含了jquery的常用功能)
    BFC 神奇背后的原理
    CSS清浮动处理(Clear与BFC)
    JavaScript实现 页面滚动图片加载(懒加载)
    CodeForce 814B
    排序算法
    uva1610
  • 原文地址:https://www.cnblogs.com/N1ckeyQu/p/11664606.html
Copyright © 2020-2023  润新知