【自研框架二】改良自研框架AOP

Aspect框架

提供了完整的AOP解决方案,是AOP的Java实现版本

◆定义切面语法以及切面语法的解析机制

◆提供了强大的织入工具

SpringAOP只支持方法级别的织入,但是AspectJ几乎支持所有连接点的织入,称为AOP的一套完整的解决方案。

image-20220113142446289

既然AspectJ那么强大,为什么spring不直接复用AspectJ呢?

因为方法级别的织入已经能满足绝大多数的需求了,并且Aspect的学习成本比springAOP高很多,还没有spring AOP用起来简单。花20%的代价完成80%的需求。

AspectJ框架的织入时机:静态织入和LTW

◆编译时织入:利用ajc ,将切面逻辑织入到类里生成class文件

◆编译后织入:利用ajc ,修改javac编译出来的class文件

◆类加载期织入:利用java agent ,在类加载的时候织入切面逻辑

前两种为静态织入,因为在class文件编译好之后切面逻辑已经织入好了(写死进了代码里面)。

CGLIB和JDK动态代理都是依靠继承关系来生成类对应的代理对象,即最终会多出一个动态代理类。

自研框架AOP2.0

1、引入jar包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

折衷方案改进框架里的AOP

使用最小的改造成本,换取尽可能大的收益–理清核心诉求

需求:我们为了让织入目标的选择(Pointcut)更加灵活。

即只需要引入AspectJ的切面表达式和相关的定位解析机制

创建一个解析类解析AspectJ即可。

不再使用固定的注解标签去筛选被代理的类,所以将Aspect标签下的属性名从value改为pointcut,类型也改为String

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    String pointcut();
}

PointcutParser实例是需要被创建出来并且装配上相关的语法树,才能识别注解Aspect属性里面的pointcut表达式

PointcutExpression:是PointcutParser根据表达式解析出来的产物,用来判断某个类或者方法是否匹配pointcut表达式。

针对类级别的判断

/**
 * 判断传入的Class对象是否是Aspect的目标代理类,即匹配PointCut表达式(初筛)
 * @param targetClass 目标类
 * @return  是否匹配
 */
public boolean roughMatches(Class<?> targetClass){
    //couldMatchJoinPointsInType只能校验within
    //不能校验(execution,call、get、set),面对无法校验的表达式,直接返回true
    return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}

精细筛选:需要对被代理类里的方法将他传入到pointcutExpression。。。方法里去做精准的匹配

完全匹配:alwaysMatches

/**
 * 判断传入的Method对象是否是Aspect的目标代理方法,及匹配Pointcut表达式(精筛)
 * @param method
 * @return
 */
public boolean accurateMatches(Method method){
    ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
    if(shadowMatch.alwaysMatches()){//完全匹配:alwaysMatches
        return true;
    }else {
        return false;
    }
}

之前按照aspect标签的属性值进行分类没有意义,因为不同的pointcut表达式可能获取同样的被代理的目标。

例如不同的表达式均可获得一样的目标image-20220113155808278

加上我们为每一个被Aspect标签标记的类创建一个专门的PointcutLoader,去按照Aspect里面对应的expression也就是pointcut表达式匹配目标类以及目标方法,因此就不需要进行Aspect的分类了。

@AllArgsConstructor
@Getter
public class AspectInfo {
    private int orderIndex;
    private DefaultAspect aspectObject;
    private PointcutLocator pointcutLocator;
}

将符合条件的AspectInfo给筛选出来

使用foreach的方式去遍历某个集合,但在遍历的时候会对集合元素进行移除操作,此时会报错,为什么?

因为foreach里面用到的iterator对象是工作在一个独立的线程中,并且会拥有一个mutex的锁,iterator在创建之后会建立一个指向原来对象的单列索引表,当原来的数量发生变化时(即集合发生变化时),索引表的内容不会同步地去改变,所以当索引指针向后移动时,就找不到要迭代的对象了,就会抛出异常。因此iterator在工作时不允许被迭代的对象动态的变化。也就是不支持调用集合的remove方法在遍历的过程中动态的移除元素。但是我们可以使用iterator本身的remove方法来删除对象。因为它会在删除当前迭代对象的同时去维护之前说的索引表的一致性。

对初筛列表的精筛

//此时sortAspectInfoList存储的是对应于某一个被代理类的Aspect初筛列表
//使用初筛列表中的每一个aspect列表对应的pointcutLoader实例分别调用其方法传入被代理方法实例来判断被代理方法实例是否真的匹配此aspect的pointcut表达式
//匹配就保留,不匹配就删除
private void collectAccurateMatchedAspectList(Method method) {
    if(ValidationUtil.isEmpty(sortAspectInfoList)){return;}
    Iterator<AspectInfo> it=sortAspectInfoList.iterator();
    while (it.hasNext()){
        AspectInfo aspectInfo=it.next();
        if(!aspectInfo.getPointcutLocator().accurateMatches(method)){
            it.remove();
        }
    }

}

修改之前的

image-20220113174227706

image-20220113174346222

到不了的,即没用的pointcut表达式

image-20220113174457604

image-20220113174612844

及只有两个aspect是生效的

pointcut表达式解析及定位,粗筛精筛



/*
* 解析Aspect表达式并且定位被织入的目标
* */
public class PointcutLocator {
    /*
    * Pointcut解析器,直接给他赋值上Aspectj的所有表达式,以便支持对众多表达式的解析
    * */
    private PointcutParser pointcutParser=PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
           PointcutParser.getAllSupportedPointcutPrimitives()
    );
    /*
    * 表达式解析器
    * */
    private PointcutExpression pointcutExpression;

    public PointcutLocator(String expression){
        this.pointcutExpression=pointcutParser.parsePointcutExpression(expression);
    }

    /**
     * 判断传入的Class对象是否是Aspect的目标代理类,即匹配PointCut表达式(初筛)
     * @param targetClass 目标类
     * @return  是否匹配
     */
    public boolean roughMatches(Class<?> targetClass){
        //couldMatchJoinPointsInType只能校验within
        //不能校验(execution,call、get、set),面对无法校验的表达式,直接返回true
        return pointcutExpression.couldMatchJoinPointsInType(targetClass);
    }

    /**
     * 判断传入的Method对象是否是Aspect的目标代理方法,及匹配Pointcut表达式(精筛)
     * @param method
     * @return
     */
    public boolean accurateMatches(Method method){
        ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
        if(shadowMatch.alwaysMatches()){//完全匹配:alwaysMatches
            return true;
        }else {
            return false;
        }
    }
}

进行aop织入



public class AspectWeaver {
    private BeanContainer beanContainer;
    public AspectWeaver(){
        this.beanContainer=BeanContainer.getInstance();
    }

    public void doAop(){
        //1、获取所有的切面类
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        if(ValidationUtil.isEmpty(aspectSet)){return;}//判空处理
        //2、拼接AspectInfoList
        List<AspectInfo> aspectInfoList=packAspectInfoList(aspectSet);
        //3、遍历容器里的类
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> targetClass : classSet) {
            //排除AspectClass自身
            if(targetClass.isAnnotationPresent(Aspect.class)){
                continue;
            }
            //4、粗筛符合条件的Aspect
            List<AspectInfo> roughMatchedAspectList=collectRoughMatchedAspectListForSpecificClass(aspectInfoList,targetClass);
            //5、尝试进行Aspect的织入
            wrapIfNecessary(roughMatchedAspectList,targetClass);
        }


    }

    private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass) {
        if(ValidationUtil.isEmpty(roughMatchedAspectList)){return;}
        //创建动态代理对象
        AspectListExecutor aspectListExecutor=new AspectListExecutor(targetClass,roughMatchedAspectList);
        Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
        beanContainer.addBean(targetClass,proxyBean);
    }

    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList=new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfoList) {
            //粗筛
            if(aspectInfo.getPointcutLocator().roughMatches(targetClass)){
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }


    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> aspectInfoList=new ArrayList<>();
        for (Class<?> aspectClass : aspectSet) {
            if(verifyAspect(aspectClass)){
                Order orderTag=aspectClass.getAnnotation(Order.class);
                Aspect aspectTag=aspectClass.getAnnotation(Aspect.class);
                DefaultAspect defaultAspect= (DefaultAspect) beanContainer.getBean(aspectClass);
               //初始化表达式定位器
                PointcutLocator pointcutLocator=new PointcutLocator(aspectTag.pointcut());
                AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator);
                aspectInfoList.add(aspectInfo);
            }else {
                //不遵循规范直接抛出异常
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfoList;
    }


    /**
     * 框架中一定要遵循给Aspect类添加@Aspect和@Order标签的规范,同时必须继承自DefaultAspect.class
     * 此外@Aspect的属性值不能是它本身
     */
    private boolean verifyAspect(Class<?> aspectClass) {
        return aspectClass.isAnnotationPresent(Aspect.class) &&
                aspectClass.isAnnotationPresent(Order.class) &&
                DefaultAspect.class.isAssignableFrom(aspectClass);
    }
}
.class
     * 此外@Aspect的属性值不能是它本身
     */
    private boolean verifyAspect(Class<?> aspectClass) {
        return aspectClass.isAnnotationPresent(Aspect.class) &&
                aspectClass.isAnnotationPresent(Order.class) &&
                DefaultAspect.class.isAssignableFrom(aspectClass);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值