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这个人传入这个代理类,和被代理对象关联起来。

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

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

原文地址:https://www.cnblogs.com/N1ckeyQu/p/11664606.html