侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Gadtry-Aop, Spring Aop之外的新选择

2023-12-11 星期一 / 0 评论 / 0 点赞 / 40 阅读 / 9432 字

Spring Aop之外的新选择Gadtry-Aop作者: harbby(github) email: [email protected] 日期: 2018.12简介​Gadtry 是一

Spring Aop之外的新选择Gadtry-Aop

作者: harbby(github) email: [email protected] 日期: 2018.12

简介

​ Gadtry 是一个构建于java8之上的工具库, 涵盖了Ioc Aop exec graph等等工具库,几乎涵盖了日常开发中非常多工具类,当然它还在不断丰富中.

Aop

​ 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(来自百度百科)

​ Spring-Aop和Ioc组合可以说非常漂亮和完美, Spring-Aop中定义了非常好注解和概念,可以说是对Aop模式完美诠释,Spring体系本身也非常全面, 但是Spring体系过于庞, 且现实中我们有太多的非Spring项目.

Gadtry-Aop

​ Gadtry-Aop是 Gadtry 1.1版本开始加入全新功能,旨在降低Aop使用难度,同时尽量小巧简单, 提供在Spring-Aop之外的新选择.在依赖上除了字节码操作器javassist外无任何依赖.

​ Gadtry-Aop 诞生有两个目的:

​ 其一是非spring项目 也能尽量获得Spring-Aop相似Aop体验

​ 其二: 我非常讨厌Spring-Aop 的expression 字符串表达式和一堆的注解.它难以记忆和阅读, Gadtry-Aop 为了简化使用采用了链式(函数式)Api设计,你只需参照下demo,然后借助ide就预览和使用所有功能,一切都是点点点

下载:

maven:

<dependency>  <groupId>com.github.harbby</groupId>  <artifactId>gadtry</artifactId>  <version>1.1.0</version></dependency>

非容器场景代理

这种场景非常多,就是你的项目中没有使用任何Ioc容器.

举个栗子:

T proxy = AopFactory.proxy(Class<T>)    .byInstance(instance)    .around(proxyContext -> {            String name = proxyContext.getInfo().getName();            System.out.println(name);            Object value = proxyContext.proceed();            switch (name) {                case "add":                    Assert.assertEquals(true, value);  //Set or List                    break;                case "size":                    Assert.assertTrue(value instanceof Integer);                    break;            }    });

这个例子中 Class<T> 指类或接口, instance这个具体的实现类活子类实现类.这个例子中用到了 环绕通知(around) 模式. 这里注意如果Class<T>是接口,则默认使用jdk动态代理, 否则使用字节码技术生成动态代理类.

我们在来看个结合Gadtry-Ioc容器例子

        IocFactory iocFactory = GadTry.create(binder -> {            binder.bind(Map.class).byCreator(HashMap::new).withSingle();            binder.bind(HashSet.class).by(HashSet.class).withSingle();        }).aop(binder -> {            binder.bind("point1")                    .withPackage("com.github.harbby")                    //.subclassOf(Map.class)                    .classAnnotated()                    .classes(HashMap.class, HashSet.class)                    .whereMethod(methodInfo -> methodInfo.getName().startsWith("add"))                    .build()                    .before((info) -> {                        Assert.assertEquals("add", info.getName());                        System.out.println("before1");                    })                    .after(() -> {                        Assert.assertTrue(true);                        System.out.println("after2");                    });        }).initialize();        Set set = iocFactory.getInstance(HashSet.class);

这个例子中结合了Ioc容器来使用Aop, 在Spring开发中基本都是结合Ioc容器来使用的Aop的.

这个例子中我们 使用了扫包,过滤器等来定位需要被代理类和方法, 最后添加了 前置通知(before)后置通知(after)

基本概念

​ 这篇文章中我们不会着重介绍IOC, 我们来重点感谢Aop的几个概念:

    1. Gadtry-Aop支持通知类型
    • 前置通知(Before) 指: 在目标方法被调用之前调用通知功能。

    • 后置通知(After)指: 在目标方法完成之后调用通知,无论方法正确执行与否。

    • 返回通知 (after-Returning) 指: 在目标方法成功执行之后调用通知。

    • 异常通知(After-throwing):在目标方法抛出异常后调用通知。

    • 环绕通知(Around):通知包裹了被通知的方法,由使用者决定和调用目标方法

      *** 关于通知类型须知: **

      ()->{} ,切入逻辑中无法获取目标方法相关信息,如args[]名称等(info)->{info.getArgs()} , 这样就可以获取目标方法相关信息
  • 切点(pointcut)

    组成切面的最小单位, 我们通常使用明确的类和方法名称,或是利用扫描和过滤所匹配的类和方法名称来指定切点.

    切点定义:

                binder.bind("point1")                    .withPackage("com.github.harbby")                    //.subclassOf(Map.class)                    .classAnnotated()                    .classes(HashMap.class, HashSet.class)                    .whereMethod(methodInfo -> methodInfo.getName().startsWith("add"))                    .build()                    .before((info) -> {                        Assert.assertEquals("add", info.getName());                        System.out.println("before1");                    })                    .after(() -> {                        Assert.assertTrue(true);                        System.out.println("after2");                    });
  • 切面(pointcut)

    切面用来组织一组相关的切点.一个切面中可以定义多个切点

    public class DemoAspect implements Aspect {    @Override    public void register(Binder binder) {        binder.bind("point1")                .classes(HashSet.class).build()                .after(() -> {                    System.out.println("after");                });        binder.bind("point2")                .classes(Map.class).build()                .after(() -> {                    System.out.println("after");                });    }}
  • 动态代理

    Gadtry-Aop 优先使用jdk动态代理,如果遇到非接口无法代理时则使用Javassist动态字节码技术来代理

过滤器

​ 在Aop中,我们通常需要指出哪些些类中的哪些些方法需要被代理拦截,Spring-Aop通过中通过expression表达式描述具体被代理的类和方法. 在Gadtry-Aop中我们细化成 类过滤方法过滤,并使用简易函数式Api来表达.

  • 扫描型-类过滤

    • withPackage(String packageName): 包过滤, 匹配此包下的所有类
    • subclassOf(Class<?>... subclasses): 匹配此类的子类型(包括本身类型) 
    • classAnnotated(Class<? extends Annotation>... classAnnotations): 带有此注解的类
    • whereClass((classInfo)-> classInfo.getName().endsWith("impl")): 更灵活的class过滤,如按名称过滤
  • 明确型-类过滤

    注意和上面扫描可同时生效 为 or 的关系

    推荐使用此方式. 大部分场景下需要被代理的类或接口都是可以确定下来的,使用扫描往往带来潜在未知风险

    • classes(HashMap.class, HashSet.class): 如明确只代理HashMap和HashSet两个类(不涵子类)实例
  • 方法过滤

    在通过上面两种方式找出需要被代理的类后, 后需要在最后进行代理方法过滤.

    • returnType(Class<?>... returnTypes): 匹配返回值是此类的子类型(包括本身类型)的方法
    • methodAnnotated(Class<? extends Annotation>... methodAnnotations):带有此注解的方法
    • whereMethod(methodInfo -> methodInfo.getName().startsWith("add")): 更加灵活的方法过滤,如:此处匹配出名称为add开头的方法

最后

​ Spring-Aop太权威了,您可能无权对它做出任何改变,但是Gadtry-Aop才刚诞生不久, 如果您对Aop有什么自己的见解和想法欢迎一起来讨论和完善, 我们的地址是: https://gitee.com/mirrors/Gadtry 欢迎您的访问,里面还有更多精彩!

广告 广告

评论区