【小家Spring】Spring AOP原理使用的基础类打点(AopInfrastructureBean、ProxyProcessorSupport、Advised、AjType)(下)

简介: 【小家Spring】Spring AOP原理使用的基础类打点(AopInfrastructureBean、ProxyProcessorSupport、Advised、AjType)(下)

介绍org.aspectj包下的几个类(单独导入的Jar包)


AjTypeSystem:从@Aspect的Class到AjType的工具类


public class AjTypeSystem {
    // 每个切面都给缓存上   注意:此处使用的是WeakReference 一定程度上节约内存
    private static Map<Class, WeakReference<AjType>> ajTypes = 
      Collections.synchronizedMap(new WeakHashMap<Class,WeakReference<AjType>>());
    public static <T> AjType<T> getAjType(Class<T> fromClass) {
      WeakReference<AjType> weakRefToAjType =  ajTypes.get(fromClass);
      if (weakRefToAjType!=null) {
        AjType<T> theAjType = weakRefToAjType.get();
        if (theAjType != null) {
          return theAjType;
        } else {
          // 其实只有这一步操作:new AjTypeImpl~~~  AjTypeImpl就相当于代理了Class的很多事情~~~~
          theAjType = new AjTypeImpl<T>(fromClass);
          ajTypes.put(fromClass, new WeakReference<AjType>(theAjType));
          return theAjType;
        }
      }
      // neither key nor value was found
      AjType<T> theAjType =  new AjTypeImpl<T>(fromClass);
      ajTypes.put(fromClass, new WeakReference<AjType>(theAjType));
      return theAjType;
    }
}


AjType


// 它继承自Java得Type和AnnotatedElement  它自己还提供了非常非常多的方法,基本都是获取元数据的一些方法,等到具体使用到的时候再来看也可以
public interface AjType<T> extends Type, AnnotatedElement {
  ...
}


AjTypeImpl


AjTypeImpl是AjType的唯一实现类,因为方法实在是太多了,因此下面我只展示一些觉得比较有意思的方法实现:


public class AjTypeImpl<T> implements AjType<T> {
  private static final String ajcMagic = "ajc$";
  // 它真正传进来的,只是这个class,它是一个标注了@Aspect注解的Class类
  private Class<T> clazz;
  private Pointcut[] declaredPointcuts = null;
  private Pointcut[] pointcuts = null;
  private Advice[] declaredAdvice = null;
  private Advice[] advice = null;
  private InterTypeMethodDeclaration[] declaredITDMethods = null;
  private InterTypeMethodDeclaration[] itdMethods = null;
  private InterTypeFieldDeclaration[] declaredITDFields = null;
  private InterTypeFieldDeclaration[] itdFields = null;
  private InterTypeConstructorDeclaration[] itdCons = null;
  private InterTypeConstructorDeclaration[] declaredITDCons = null;
  // 唯一的一个构造函数
  public AjTypeImpl(Class<T> fromClass) {
    this.clazz = fromClass;
  }
  // 这个方法有意思的地方在于:它把所有的接口类,都变成AjType类型了
  public AjType<?>[] getInterfaces() {
    Class<?>[] baseInterfaces = clazz.getInterfaces();
    return toAjTypeArray(baseInterfaces);
  }
  private AjType<?>[] toAjTypeArray(Class<?>[] classes) {
    AjType<?>[] ajtypes = new AjType<?>[classes.length];
    for (int i = 0; i < ajtypes.length; i++) {
      ajtypes[i] = AjTypeSystem.getAjType(classes[i]);
    }
    return ajtypes;
  }
  // 就是把clazz返回出去
  public Class<T> getJavaClass() {
    return clazz;
  }
  public AjType<? super T> getSupertype() {
    Class<? super T> superclass = clazz.getSuperclass();
    return superclass==null ? null : (AjType<? super T>) new AjTypeImpl(superclass);
  }
  // 判断是否是切面,就看是否有这个注解~~
  public boolean isAspect() {
    return clazz.getAnnotation(Aspect.class) != null;
  }
  // 这个方法很重要:PerClause AspectJ切面的表现形式
  // 备注:虽然有这么多(参考这个类PerClauseKind),但是Spring AOP只支持前三种~~~
  public PerClause getPerClause() {
    if (isAspect()) {
      Aspect aspectAnn = clazz.getAnnotation(Aspect.class);
      String perClause = aspectAnn.value();
      if (perClause.equals("")) {
        // 如果自己没写,但是存在父类的话并且父类是切面,那就以父类的为准~~~~
        if (getSupertype().isAspect()) {
          return getSupertype().getPerClause();
        } 
        // 不写默认是单例的,下面的就不一一解释了
        return new PerClauseImpl(PerClauseKind.SINGLETON);
      } else if (perClause.startsWith("perthis(")) {
        return new PointcutBasedPerClauseImpl(PerClauseKind.PERTHIS,perClause.substring("perthis(".length(),perClause.length() - 1));
      } else if (perClause.startsWith("pertarget(")) {
        return new PointcutBasedPerClauseImpl(PerClauseKind.PERTARGET,perClause.substring("pertarget(".length(),perClause.length() - 1));       
      } else if (perClause.startsWith("percflow(")) {
        return new PointcutBasedPerClauseImpl(PerClauseKind.PERCFLOW,perClause.substring("percflow(".length(),perClause.length() - 1));               
      } else if (perClause.startsWith("percflowbelow(")) {
        return new PointcutBasedPerClauseImpl(PerClauseKind.PERCFLOWBELOW,perClause.substring("percflowbelow(".length(),perClause.length() - 1));
      } else if (perClause.startsWith("pertypewithin")) {
        return new TypePatternBasedPerClauseImpl(PerClauseKind.PERTYPEWITHIN,perClause.substring("pertypewithin(".length(),perClause.length() - 1));        
      } else {
        throw new IllegalStateException("Per-clause not recognized: " + perClause);
      }
    } else {
      return null;
    }
  }
  public AjType<?>[] getAjTypes() {
    Class[] classes = clazz.getClasses();
    return toAjTypeArray(classes);
  }
  public Field getDeclaredField(String name) throws NoSuchFieldException {
    Field f =  clazz.getDeclaredField(name);
    if (f.getName().startsWith(ajcMagic)) throw new NoSuchFieldException(name);
    return f;
  }
  // 这个有点意思:表示标注了@Before、@Around注解的并不算真的方法了,不会给与返回了
  public Method[] getMethods() {
    Method[] methods = clazz.getMethods();
    List<Method> filteredMethods = new ArrayList<Method>();
    for (Method method : methods) {
      if (isReallyAMethod(method)) filteredMethods.add(method);
    }
    Method[] ret = new Method[filteredMethods.size()];
    filteredMethods.toArray(ret);
    return ret;
  }
  private boolean isReallyAMethod(Method method) {
    if (method.getName().startsWith(ajcMagic)) return false;
    if (method.getAnnotations().length==0) return true;
    if (method.isAnnotationPresent(org.aspectj.lang.annotation.Pointcut.class)) return false;
    if (method.isAnnotationPresent(Before.class)) return false;
    if (method.isAnnotationPresent(After.class)) return false;
    if (method.isAnnotationPresent(AfterReturning.class)) return false;
    if (method.isAnnotationPresent(AfterThrowing.class)) return false;
    if (method.isAnnotationPresent(Around.class)) return false;
    return true;
  }
  // 拿到所有的Pointcut方法  并且保存缓存起来
  public Pointcut[] getDeclaredPointcuts() {
    if (declaredPointcuts != null) return declaredPointcuts;
    List<Pointcut> pointcuts = new ArrayList<Pointcut>();
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
      Pointcut pc = asPointcut(method);
      if (pc != null) pointcuts.add(pc);
    }
    Pointcut[] ret = new Pointcut[pointcuts.size()];
    pointcuts.toArray(ret);
    declaredPointcuts = ret;
    return ret;
  }
  // 标注有org.aspectj.lang.annotation.Pointcut这个注解的方法。  相当于解析这个注解吧,最终包装成一个PointcutImpl
  // 主义:Spring-aop也有个接口Pointcut,这里也有一个Pointcut接口  注意别弄混了
  private Pointcut asPointcut(Method method) {
    org.aspectj.lang.annotation.Pointcut pcAnn = method.getAnnotation(org.aspectj.lang.annotation.Pointcut.class);
    if (pcAnn != null) {
      String name = method.getName();
      if (name.startsWith(ajcMagic)) {
        // extract real name
        int nameStart = name.indexOf("$$");
        name = name.substring(nameStart +2,name.length());
        int nextDollar = name.indexOf("$");
        if (nextDollar != -1) name = name.substring(0,nextDollar);
      }
      return new PointcutImpl(name,pcAnn.value(),method,AjTypeSystem.getAjType(method.getDeclaringClass()),pcAnn.argNames());
    } else {
      return null;
    }
  }
  // 最终返回的对象为AdviceImpl实现类
  public Advice[] getDeclaredAdvice(AdviceKind... ofType) { ... }
  public Advice[] getAdvice(AdviceKind... ofType) { ... }
  private void initDeclaredAdvice() {
    Method[] methods = clazz.getDeclaredMethods();
    List<Advice> adviceList = new ArrayList<Advice>();
    for (Method method : methods) {
      Advice advice = asAdvice(method);
      if (advice != null) adviceList.add(advice);
    }
    declaredAdvice = new Advice[adviceList.size()];
    adviceList.toArray(declaredAdvice);
  }
  // 标注了各个注解的 做对应的处理
  private Advice asAdvice(Method method) {
    if (method.getAnnotations().length == 0) return null;
    Before beforeAnn = method.getAnnotation(Before.class);
    if (beforeAnn != null) return new AdviceImpl(method,beforeAnn.value(),AdviceKind.BEFORE);
    After afterAnn = method.getAnnotation(After.class);
    if (afterAnn != null) return new AdviceImpl(method,afterAnn.value(),AdviceKind.AFTER);
    AfterReturning afterReturningAnn = method.getAnnotation(AfterReturning.class);
    if (afterReturningAnn != null) {
      // 如果没有自己指定注解pointcut()的值,那就取值为value的值吧~~~
      String pcExpr = afterReturningAnn.pointcut();
      if (pcExpr.equals("")) pcExpr = afterReturningAnn.value();
      // 会把方法的返回值放进去、下同。。。   这就是@After和@AfterReturning的区别的原理
      // 它可议自定义自己的切点表达式咯
      return new AdviceImpl(method,pcExpr,AdviceKind.AFTER_RETURNING,afterReturningAnn.returning());
    }
    AfterThrowing afterThrowingAnn = method.getAnnotation(AfterThrowing.class);
    if (afterThrowingAnn != null) {
      String pcExpr = afterThrowingAnn.pointcut();
      if (pcExpr == null) pcExpr = afterThrowingAnn.value();
      return new AdviceImpl(method,pcExpr,AdviceKind.AFTER_THROWING,afterThrowingAnn.throwing());
    }
    Around aroundAnn = method.getAnnotation(Around.class);
    if (aroundAnn != null) return new AdviceImpl(method,aroundAnn.value(),AdviceKind.AROUND);
    return null;
  }
  // 必须不是切面才行哦~~~~
  public boolean isLocalClass() {
    return clazz.isLocalClass() && !isAspect();
  }
  public boolean isMemberClass() {
    return clazz.isMemberClass() && !isAspect();
  }
  // 内部类也是能作为切面哒  哈哈
  public boolean isMemberAspect() {
    return clazz.isMemberClass() && isAspect();
  }
  public String toString() { return getName(); }
}


总结


工欲善其事必先利其器,任何负责的框架、业务也好。都是由一部分一部分的组件组合而成的。本文主旨就是单独把组件拆出来讲解,逐个击破~~


相关文章
|
8天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
29天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
170 73
|
16天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
68 8
|
29天前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
99 5
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
54 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
60 4
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
96 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
314 1
什么是AOP面向切面编程?怎么简单理解?