2w字搞懂Spring AOP的前世今生(一)

发布于 2022-6-2 16:38
浏览
0收藏

 

Spring AOP概述
最近看seata源码的时候,随处可见spring aop的api,于是一边看seata,一边又把spring aop总结了一下

我们在使用Spring框架的时候,经常需要和Spring的2大特性,IOC和AOP打交道,之前写了一篇《2w字搞懂Spring Bean的一生》从源码层面分析了IOC的执行流程,本篇文章就接着分享一下AOP的底层实现,比较基础的内容本篇文章就不多做介绍了,主要侧重于底层api的设计理念

「AOP这种设计理念常见的概念如下」 

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

「AOP的主要应用场景如下」

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

「Spring AOP的实现主要经历了2代」

第一代:spring1.x版本,自己实现了AOP的功能 第二代:spring2.x版本,Spring集成了AspectJ的实现

Spring AOP一代
「当我们要基于现成的实现增加横切逻辑时,首先需要找到哪些地方增强,我们就用Pointcut来进行筛选吧」

先写一个Service方便后面的演示

public interface EchoService {

    String echo(String message);
}
public class DefaultEchoService implements EchoService {

    @Override
    public String echo(String message) {
        return message;
    }
}

Pointcut

Pointcut接口定义如下

public interface Pointcut {

 // 通过类过滤
 ClassFilter getClassFilter();

 // 通过方法过滤
 MethodMatcher getMethodMatcher();

 Pointcut TRUE = TruePointcut.INSTANCE;

}

「当我们想筛选出EchoService的echo方法时,就可以定义如下的Pointcut」

public class EchoPointcut implements Pointcut {

    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return EchoService.class.isAssignableFrom(clazz);
            }
        };
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return "echo".equals(method.getName()) &&
                        method.getParameterTypes().length == 1 &&
                        Objects.equals(String.class, method.getParameterTypes()[0]);
            }

            @Override
            public boolean isRuntime() {
                return false;
            }

            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                return false;
            }
        };
    }
}

 

看起来还是很麻烦的,因此Spring内置了很多实现,一般情况下我们用内置的实现即可,不用自己定义,上面的筛选过程就可以改为如下

// 方法名为 echo 会被拦截
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedName("echo");

Spring提供的部分Pointcut实现如下 

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

Jointpoint

「通过Pointcut筛选出来的要增加横切逻辑的地方就是Jointpoint。」 在AOP理念中,很多地方可以增加横切逻辑,如方法执行,字段设置等。但是「Spring只支持方法执行这一种Joinpoint」,因为这种类型的Jointpoint基本上已经满足80%的场景了

Joinpoint类型中 「方法调用优于方法执行」

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

因为Spring中只支持方法执行这一种Joinpoint,所以我们可以从Joinpoint实现类中获取增强的方法信息 

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

Advice

当筛选出Jointpoint时,我们就需要在这些Jointpoint上增加横切逻辑,这些横切逻辑被称为Advice

2w字搞懂Spring AOP的前世今生(一)-开源基础软件社区

在Spring中实现横切逻辑的方式有两类

  1. 实现Advice接口
  2. 实现IntroductionInfo接口
    实现Advice接口的方式我们最常用,后面会详细分析。实现IntroductionInfo接口的方式基本不会用,这里演示一下具体的用法,方便理解整个AOP API的设计理念

「IntroductionInfo主要是通过给目标类实现特定接口来增加新功能」

public interface SayName {

    String getName();
}
public class DefaultSayName implements SayName {

    @Override
    public String getName() {
        return "I am service";
    }
}
public static void main(String[] args) {
    SayName sayName = new DefaultSayName();
    EchoService echoService = new DefaultEchoService();
    // IntroductionInfo接口的内置实现
    DelegatingIntroductionInterceptor interceptor =
            new DelegatingIntroductionInterceptor(sayName);
    Advisor advisor = new DefaultIntroductionAdvisor(interceptor, SayName.class);
    ProxyFactory proxyFactory = new ProxyFactory(echoService);
    proxyFactory.addAdvisor(advisor);
    // hello world
    EchoService proxyService = (EchoService) proxyFactory.getProxy();
    System.out.println(proxyService.echo("hello world"));
    // I am service
    SayName proxySayName = (SayName) proxyFactory.getProxy();
    System.out.println(proxySayName.getName());
}

可能你对这个例子中的Advisor和ProxyFactory比较陌生,不知道起了啥作用,不着急,我们后面会详细分析这2个类的作用

「实现Advice接口的方式,应该是Spring AOP一代中最常见的使用方式了」

「对HashMap的put方法增加执行前的横切逻辑」, 打印放入HashMap的key和value的值

public static void main(String[] args) {
    JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
    pointcut.setPattern(".*put.*");
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
    advisor.setPointcut(pointcut);
    advisor.setAdvice(new MethodBeforeAdvice() {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.printf("当前存放的key为 %s,值为 %s", args[0], args[1]);
        }
    });

    ProxyFactory proxyFactory = new ProxyFactory(new HashMap());
    proxyFactory.addAdvisor(advisor);
    Map<String, String> proxyMap = (Map<String, String>) proxyFactory.getProxy();
    // 当前存放的key为 a,值为 a
    proxyMap.put("a", "a");
}

 

文章转自公众号:Java识堂

分类
标签
已于2022-6-2 16:38:45修改
收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐