Spring-AOP核心源码、原理详解前篇

news2024/11/14 23:37:56

本文主要分4部分

  1. Aop原理介绍
  2. 介绍aop相关的一些类
  3. 通过源码详解aop代理的创建过程
  4. 通过源码详解aop代理的调用过程
  5. Aop代理一些特性的使用案例

Spring AOP原理
原理比较简单,主要就是使用jdk动态代理和cglib代理来创建代理对象,通过代理对象来访问目标对象,而代理对象中融入了增强的代码,最终起到对目标对象增强的效果。

aop相关的一些类

  1. 连接点(JoinPoint)相关类
  2. 通知(Advice)相关的类
  3. 切入点(Pointcut)相关的类
  4. 切面(Advisor)相关的类
连接点(JoinPoint)相关类

JoinPoint接口
这个接口表示一个通用的运行时连接点(在AOP术语中)

import java.lang.reflect.AccessibleObject;

public interface Joinpoint {
    /**
     * 转到拦截器链中的下一个拦截器
     * @return
     * @throws Throwable
     */
    Object proceed() throws Throwable;

    /**
     * 返回保存当前连接点静态部分【的对象】,这里一般指被代理的目标对象
     * @return
     */
    Object getThis();

    /**
     * 返回此静态连接点 一般就为当前的Method(至少目前的唯一实现是MethodInvocation,所以连接点得静态部分肯定就是本方法)
     * @return
     */
    AccessibleObject getStaticPart();
}

几个重要的子接口和实现类,如下:
image.png

Invocation接口
此接口表示程序中的调用,调用是一个连接点,可以被拦截器拦截。

package org.aopalliance.intercept;

public interface Invocation extends Joinpoint {
    /**
     * 将参数作为数组对象获取,可以更改此数组中的元素值以更改参数。
     * 通常用来获取调用目标方法的参数
     */
    Object[] getArguments();
}

MethodInvocation接口
用来表示连接点中方法的调用,可以获取调用过程中的目标方法。

package org.aopalliance.intercept;
import java.lang.reflect.Method;

/**
 * 方法调用的描述,在方法调用时提供给拦截器。
 * 方法调用是一个连接点,可以被方法拦截器拦截。
 */
public interface MethodInvocation extends Invocation {
    /**
     * 返回正在被调用得方法~~~ 返回的是当前Method对象。
     * 此时,效果同父类的AccessibleObject getStaticPart() 这个方法
     */
    Method getMethod();
}

ProxyMethodInvocation接口
表示代理方法的调用


public interface ProxyMethodInvocation extends MethodInvocation {
    /**
     * 获取被调用的代理对象
     * @return
     */
    Object getProxy();

    /**
     * 克隆一个方法调用器MethodInvocation
     * @return
     */
    MethodInvocation invocableClone();

    /**
     * 克隆一个方法调用器MethodInvocation,并为方法调用器指定参数
     * @param arguments
     * @return
     */
    MethodInvocation invocableClone(Object... arguments);

    /**
     * 设置要用于此链中任何通知的后续调用的参数
     * @param arguments
     */
    void setArguments(Object... arguments);

    /**
     * 添加一些扩展用户属性,这些属性不在AOP框架内使用。它们只是作为调用对象的一部分保留,用于
     * 特殊的拦截器
     * @param key
     * @param value
     */
    void setUserAttribute(String key, @Nullable Object value);

    /**
     * 根据key获取对应的用户属性
     * @param key
     * @return
     */
    @Nullable
    Object getUserAttribute(String key);
}

通俗点理解:连接点表示方法的调用过程,内部包含了方法调用过程中的所有信息,比如被调用的方法、目标、代理对象、执行拦截器链等信息。
上面定义都是一些接口,最终有2个实现。

ReflectiveMethodInvocation
当代理对象是采用jdk动态代理创建的,通过代理对象来访问目标对象的方法的时,最终过程是由ReflectiveMethodInvocation来处理的,内部会通过递归调用方法拦截器,最终会调用到目标方法。

CglibMethodInvocation
功能和上面的类似,当代理对象是采用cglib创建的,通过代理对象来访问目标对象的方法的时,最终过程是由CglibMethodInvocation来处理的,内部会通过递归调用方法拦截器,最终会调用到目标方法。

通知相关的类

通知用来定义需要增强的逻辑。
image.png
Advice接口
通知的底层接口,只是一个标记作用

package org.aopalliance.aop;

public interface Advice {
}

BeforeAdvice接口
方法前置通知,内部空的,也就是方法前置增强

package org.springframework.aop;
import org.aopalliance.aop.Advice;

public interface BeforeAdvice extends Advice {
}

Interceptor接口
此接口表示通用拦截器

package org.aopalliance.intercept;
public interface Interceptor extends Advice {
}

MethodInterceptor接口
方法拦截器,所有的通知均需要转换为MethodInterceptor类型的,最终多个MethodInterceptor组成一个方法拦截器连

package org.aopalliance.intercept;

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
    /**
     * 拦截目标方法的执行,可以在这个方法内部实现需要增强的逻辑,以及主动调用目标方法
     * @param methodInvocation
     * @return
     * @throws Throwable
     */
    Object invoke(MethodInvocation methodInvocation) throws Throwable;
}

AfterAdvice接口
后置通知的公共标记接口,也就是后置增强接口

package org.springframework.aop;
import org.aopalliance.aop.Advice;

public interface AfterAdvice extends Advice {
}

MethodBeforeAdvice接口
方法执行前通知,需要在目标方法执行前执行一些逻辑的,可以通过这个实现。
通俗点说:需要在目标方法执行之前增强一些逻辑,可以通过这个接口来实现。before方法:在调用给定方法之前回调。

package org.springframework.aop;

import java.lang.reflect.Method;
import org.springframework.lang.Nullable;

public interface MethodBeforeAdvice extends BeforeAdvice {
    /**
     *  调用目标方法之前会先调用这个before方法
     * @param method 需要执行的目标方法
     * @param args  目标方法的参数
     * @param target    目标对象
     * @throws Throwable
     */
    void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

如同

public Object invoke(){
   // 调用MethodBeforeAdvice#before方法
    return 调用目标方法;
}

AfterReturningAdvice接口
方法执行后通知,需要在目标方法执行之后执行增强一些逻辑的,可以通过这个实现。
不过需要注意一点:目标方法正常执行后,才会回调这个接口,当目标方法有异常,那么这通知会被跳过。

package org.springframework.aop;
public interface AfterReturningAdvice extends AfterAdvice {
    /**
    * 目标方法执行之后会回调这个方法
    * method:需要执行的目标方法
    * args:目标方法的参数
    * target:目标对象
    */
    void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}

ThrowsAdvice接口
异常增强标记接口

package org.springframework.aop;

public interface ThrowsAdvice extends AfterAdvice {
}

此接口上没有任何方法,因为方法由反射调用,实现类必须实现以下形式的方法,前3个参数是可选的,最后一个参数为需要匹配的异常的类型。

void afterThrowing([Method, args, target], ThrowableSubclass);

通知包装器
负责将各种非MethodInterceptor类型的通知(Advice)包装为MethodInterceptor类型。
刚才有说过:Aop中所有的Advice最终都会转换为MethodInterceptor类型的,组成一个方法调用链,然后执行。
3个包装器类

  • MethodBeforeAdviceInterceptor
  • AfterReturningAdviceInterceptor
  • ThrowsAdviceInterceptor

MethodBeforeAdviceInterceptor类
这个类实现了 MethodInterceptor 接口,负责将 MethodBeforeAdvice 方法前置通知包装为MethodInterceptor 类型,创建这个类型的对象的时候需要传递一个MethodBeforeAdvice 类型的参数,重点是 invoke 方法。

package org.springframework.aop.framework.adapter;

import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.util.Assert;

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
    private final MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        //负责调用前置通知的方法
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //继续执行方法调用链
        return mi.proceed();
    }
}

AfterReturningAdviceInterceptor类
这个类实现了 MethodInterceptor 接口,负责将 AfterReturningAdvice 方法后置通知包装为MethodInterceptor 类型,创建这个类型的对象的时候需要传递一个AfterReturningAdvice 类型的参数,重点是 invoke 方法

package org.springframework.aop.framework.adapter;

import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.util.Assert;

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
    private final AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        //先执行方法调用链,可以获取目标方法的执行结果
        Object retVal = mi.proceed();
        //执行后置通知
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        //返回结果
        return retVal;
    }
}

ThrowsAdviceInterceptor类
这个类实现了 MethodInterceptor 接口,负责将 ThrowsAdvice 异常通知包装为 MethodInterceptor类型,创建这个类型的对象的时候需要传递一个 Object 类型的参数,通常这个参数是 ThrowsAdvice类型的,重点是 invoke 方法


package org.springframework.aop.framework.adapter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.AfterAdvice;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
    private static final String AFTER_THROWING = "afterThrowing";
    private static final Log logger = LogFactory.getLog(org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.class);
    private final Object throwsAdvice;
    private final Map<Class<?>, Method> exceptionHandlerMap = new HashMap<>();

    public ThrowsAdviceInterceptor(Object throwsAdvice) {
        Assert.notNull(throwsAdvice, "Advice must not be null");
        this.throwsAdvice = throwsAdvice;
        //获取异常通知中定义的所有方法(public、默认的、protected、private)
        Method[] methods = throwsAdvice.getClass().getMethods();
        int length = methods.length;
        //轮询methods
        for (Method method : methods) {
            //方法名称为afterThrowing && 方法参数为1或者4
            if (method.getName().equals(AFTER_THROWING) && (method.getParameterCount() == 1 || method.getParameterCount() == 4)) {
                //获取方法的最后一个参数类型
                Class<?> throwableParam = method.getParameterTypes()[method.getParameterCount() - 1];
                //判断方法参数类型是不是Throwable类型的
                if (Throwable.class.isAssignableFrom(throwableParam)) {
                    //缓存异常处理方法到map中(异常类型->异常处理方法)
                    this.exceptionHandlerMap.put(throwableParam, method);
                }
            }
        }
        //如果exceptionHandlerMap是空,抛出异常,所以最少要有一个异常处理方法
        if (this.exceptionHandlerMap.isEmpty()) {
            throw new IllegalArgumentException("At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
        }
    }

    /**
     * @return 获取异常通知中自定义的处理异常方法的数量
     */
    public int getHandlerMethodCount() {
        return this.exceptionHandlerMap.size();
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            //调用通知链
            return mi.proceed();
        } catch (Throwable e) {
            //获取异常通知中自定义的处理异常的方法
            Method handlerMethod = this.getExceptionHandler(e);
            //当处理的方法不为空
            if (handlerMethod != null) {
                this.invokeHandlerMethod(mi, e, handlerMethod);
            }
            //继续向外抛出异常
            throw e; //@1
        }
    }

    /**
     * @param exception 获取throwsAdvice中处理exception参数指定的异常的方法
     * @return
     */
    @Nullable
    private Method getExceptionHandler(Throwable exception) {
        //获取异常类型
        Class<?> exceptionClass = exception.getClass();

        //从缓存中获取异常类型对应的方法
        Method handler = this.exceptionHandlerMap.get(exceptionClass);
        //来一个循环,查询处理方法,循环条件:方法为空 && 异常类型!=Throwable
        while (handler == null && exceptionClass != Throwable.class) {
            //获取异常的父类型
            exceptionClass = exceptionClass.getSuperclass();
            //从缓存中查找异常对应的处理方法
            handler = this.exceptionHandlerMap.get(exceptionClass);
        }
        //将查找结果返回
        return handler;
    }

    /**
     *  通过反射调用异常通知中的异常方法
     * @param mi
     * @param ex
     * @param method
     * @throws Throwable
     */
    private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
        //构建方法请求参数
        Object[] handlerArgs;
        //若只有1个参数,参数为:异常对象
        if (method.getParameterCount() == 1) {
            handlerArgs = new Object[]{ex};
        } else {
            //4个参数(方法、方法请求参数、目标对象、异常对象)
            handlerArgs = new Object[]{mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
        }

        try {
            //通过反射调用异常通知中的方法
            method.invoke(this.throwsAdvice, handlerArgs);
        } catch (InvocationTargetException tx) {
            throw tx.getTargetException();
        }
    }
}

从上面可以看出,异常通知,自定义处理异常的方法有几个特点

  1. 方法名称必须为 afterThrowing
  2. 方法参数必须1个或4个,最后一个参数是 Throwable 类型或其子类型
  3. 可以在异常处理中记录一些异常信息,这个还是比较有用的,但是注意一点目标方法抛出的异常最后还是会向外继续抛出 @1
切入点(PointCut)相关类

通知(Advice)用来指定需要增强的逻辑,但是哪些类的哪些方法中需要使用这些通知呢?这个就是通过切入点来配置的。
image.png

public interface Pointcut {

    //匹配所有对象的 Pointcut,内部的2个过滤器默认都会返回true
    Pointcut TRUE = TruePointcut.INSTANCE;

    /**
     * 类过滤器, 可以知道哪些类需要拦截
     * @return
     */
    ClassFilter getClassFilter();

    /**
     * 方法匹配器, 可以知道哪些方法需要拦截
     * @return
     */
    MethodMatcher getMethodMatcher();
}

ClassFilter接口
类过滤器

@FunctionalInterface
public interface ClassFilter {
    ClassFilter TRUE = TrueClassFilter.INSTANCE;

    boolean matches(Class<?> var1);
}

MethodMatcher接口
方法过滤器


package org.springframework.aop;
import java.lang.reflect.Method;

public interface MethodMatcher {
    
    //匹配所有方法,这个内部的2个matches方法任何时候都返回true
    org.springframework.aop.MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

    /**
     * 执行静态检查给定方法是否匹配
     * @param method 目标方法
     * @param targetClass 目标对象类型
     * @return 是否通过
     */
    boolean matches(Method method, Class<?> targetClass);

    /**
     *
     * @return 是否是动态匹配,即是否每次执行目标方法的时候都去验证一下
     */
    boolean isRuntime();

    /**
     *  动态匹配验证的方法,比第一个matches方法多了一个参数args,这个参数是调用目标方法传入的参数
     * @param method 目标方法
     * @param targetClass 目标对象类型
     * @param args 方法参数
     * @return 是否通过
     */
    boolean matches(Method method, Class<?> targetClass, Object... args);
}

其实从字面意思我们都可以看出来,isRuntime这个标记是判断要不要根据方法参数动态的拦击。所以这个是第二层判断而已。

顾问(Advisor) 也就是切面

通知定义了需要做什么,切入点定义了在哪些类的哪些方法中执行通知,那么需要将他们2个组合起来才有效啊。
顾问(Advisor)就是做这个事情的。
在spring aop中,你可以将advisor理解为切面,切面中通常有2个关键信息:

  • 需要增强的目标方法列表,这个通过切入点(Pointcut)来指定
  • 需要在目标方法中增强的逻辑,这个通过(Advice)通知来指定
package org.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * 包含AOP通知(在joinpoint处执行的操作)和确定通知适用性的过滤器(如切入点[PointCut])的基
 * 本接口。
 */
public interface Advisor {
    Advice EMPTY_ADVICE = new Advice() {
    };

    /**
     * 
     * @return 返回引用的通知
     */
    Advice getAdvice();

    boolean isPerInstance();
}

上面这个接口通常不会直接使用,这个接口有2个子接口,通常我们会和这2个子接口来打交道,下面看一下这2个子接口

PointcutAdvisor接口
通过名字就能看出来,这个和Pointcut有关,内部有个方法用来获取 Pointcut ,AOP使用到的大部分Advisor都属于这种类型的。
在目标方法中实现各种增强功能基本上都是通过PointcutAdvisor来实现的。

package org.springframework.aop;

public interface PointcutAdvisor extends Advisor {
    /**
     * 
     * @return 获取顾问中使用的切入点
     */
    Pointcut getPointcut();
}

DefaultPointcutAdvisor类
PointcutAdvisor的默认实现,这是最常用的Advisor实现,它可以用于任何Pointcut和Advice类型,代码相当简单,里面定义了2个属性:pointcut和advisor,由使用者指定。

IntroductionAdvisor接口
这个接口,估计大家比较陌生,干什么的呢?
一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。可以通过IntroductionAdvisor给目标类引入更多接口的功能,这个功能是不是非常牛逼。

下面开始2个重点工作。

  • 通过源码介绍aop中代理创建过程
  • 通过源码介绍代理方法的调用执行过程

创建代理3大步骤

  1. 创建代理所需参数配置
  2. 根据代理参数获取AopProxy对象
  3. 通过AopProxy获取代理对象

创建代理所需参数配置
创建代理所需参数配置主要是通过 AdvisedSupport 这个类来做的,看一下类图,下面一个个来介绍。
image.png

TargetClassAware接口
比较简单的一个接口,定义了一个方法,用来获取目标对象类型。
所谓目标对象:就是被代理对象。

package org.springframework.aop;
public interface TargetClassAware {
    @Nullable
     Class<?> getTargetClass();
}

ProxyConfig类
这个类比较关键了,代理配置类,内部包含了创建代理时需要配置的各种参数。

package org.springframework.aop.framework;

import java.io.Serializable;
import org.springframework.util.Assert;

/**
 * 对外提供统一的代理参数配置类,以确保所有代理创建程序具有一致的属性
 */
public class ProxyConfig implements Serializable {
    private static final long serialVersionUID = -8409359707199703185L;
    //标记是否直接对目标类进行代理,而不是通过接口产生代理
    private boolean proxyTargetClass = false;
    /**
     * 标记是否对代理进行优化。启动优化通常意味着在代理对象被创建后,增强的修改将不会生效,因此
     * 默认值为false,
     * todo 如果exposeProxy设置为true,即使optimize为true也会被忽略。
     */
    private boolean optimize = false;
    //标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象
    //可以被转换为Advised类型
    boolean opaque = false;
    /**
     * 标记代理对象是否应该被aop框架通过AopContext以ThreadLocal的形式暴露出去。
     * 当一个代理对象需要调用它自己的另外一个代理方法时,这个属性将非常有用。默认是是false,以
     * 避免不必要的拦截。
     */
    boolean exposeProxy = false;
    /**
     * 标记该配置是否需要被冻结,如果被冻结,将不可以修改增强的配置。
     * 当我们不希望调用方修改转换成Advised对象之后的代理对象时,这个配置将非常有用。
     */
    private boolean frozen = false;

    //...省略了属性的get set方法

    
}

Advised接口
这个接口中定义了操作Aop代理配置的各种方法(比如指定被代理的目标对象、添加通知、添加顾问等等)。
所有由spring aop创建的代理对象默认都会实现这个接口。


package org.springframework.aop.framework;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;

public interface Advised extends TargetClassAware {
    /**
     *
     * @return 返回配置是否已冻结,被冻结之后,无法修改已创建好的代理对象中的通知
     */
    boolean isFrozen();

    /**
     * 是否对目标类直接创建代理,而不是对接口创建代理,通俗点讲:
     * @return 如果是通过cglib创建代理,此方法返回true,否则返回false
     */
    boolean isProxyTargetClass();

    /**
     *
     * @return 获取配置中需要代理的接口列表
     */
    Class<?>[] getProxiedInterfaces();

    /**
     *
     * @param intf
     * @return 判断某个接口是否被代理
     */
    boolean isInterfaceProxied(Class<?> intf);

    /**
     * 设置被代理的目标源,创建代理的时候,通常需要传入被代理的对象,最终被代理的对象会被包装为
     * TargetSource类型的
     * @param targetSource
     */
    void setTargetSource(TargetSource targetSource);

    TargetSource getTargetSource();

    void setExposeProxy(boolean var1);

    /**
     * 设置是否需要将代理暴露在ThreadLocal中,这样可以在线程中获取到被代理对象,这个配置挺有
     * 用的,稍后会举例说明使用场景
     * @return isExposeProxy
     */
    boolean isExposeProxy();

    /**
     *  设置此代理配置是否经过预筛选,以便它只包含适用的顾问(匹配此代理的目标类)。
     *  默认设置是“假”。如果已经对advisor进行了预先筛选,则将其设置为“true”
     *  这意味着在为代理调用构建实际的advisor链时可以跳过ClassFilter检查。
     * @param preFiltered
     */
    void setPreFiltered(boolean preFiltered);

    boolean isPreFiltered();

    //返回代理配置中干掉所有Advisor列表
    Advisor[] getAdvisors();

    //添加一个Advisor
    void addAdvisor(Advisor advisor) throws AopConfigException;

    //指定的位置添加一个Advisor
    void addAdvisor(int index, Advisor advisor) throws AopConfigException;

    //移除一个Advisor
    boolean removeAdvisor(Advisor advisor);

    //移除指定位置的Advisor
    void removeAdvisor(int index) throws AopConfigException;

    //查找某个Advisor的位置
    int indexOf(Advisor advisor);

    //对advisor列表中的a替换为b
    boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

    //添加一个通知
    void addAdvice(Advice advice) throws AopConfigException;

    //向指定的位置添加一个通知
    void addAdvice(int index, Advice advice) throws AopConfigException;

    //移除一个通知
    boolean removeAdvice(Advice advice);

    //获取通知的位置
    int indexOf(Advice advice);

    //将代理配置转换为字符串,这个方便排错和调试使用的
    String toProxyConfigString();
}

AdvisedSupport类
这个类是个重点,AOP代理配置管理器的基类,继承了 ProxyConfig 并且实现了 Advised 接口,创建 aop代理之前,所有需要配置的信息都是通过这个类来操作的。
比如:设置是否为目标类创建代理、设置目标对象、配置通知列表等等

import lombok.Getter;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AdvisorChainFactory;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.framework.DefaultAdvisorChainFactory;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 *
 * 上面几个类有几个结论,这里说一下。
 * 1. 配置中添加的Advice对象最终都会被转换为DefaultPointcutAdvisor对象,此时
 * DefaultPointcutAdvisor未指定pointcut,大家可以去看一下DefaultPointcutAdvisor中pointcut有
 * 个默认值,默认会匹配任意类的任意方法。
 * 2. 当配置被冻结的时候,即frozen为true的时,此时配置中的Advisor列表是不允许修改的。
 * 3. 上面的 getInterceptorsAndDynamicInterceptionAdvice 方法,通过代理调用目标方法的时
 * 候,最后需要通过方法和目标类的类型,从当前配置中会获取匹配的方法拦截器列表,获取方法拦
 * 截器列表是由 AdvisorChainFactory 负责的。
 * getInterceptorsAndDynamicInterceptionAdvice 会在调用代理的方法时会执行,稍后在执行
 * 阶段会详解。
 * 4. 目标方法和其关联的方法拦截器列表会被缓存在 methodCache 中,当顾问列表有变化的时候,
 * methodCache 缓存会被清除。
 * @description: 自己的代理类
 *
 * @author: stone
 * @date: Created by 2021/6/3 16:55
 * @version: 1.0.0
 * @pakeage: com.shiguiwu.springmybatis.spring.aop.principle
 */
public class AdvisedSupport extends ProxyConfig implements Advised {


    public static final TargetSource EMPTY_TARGET_SOURCE =
            EmptyTargetSource.INSTANCE;
    public TargetSource targetSource = EMPTY_TARGET_SOURCE;
    /**
     * 建议器是否已经针对特定的目标类进行筛选
     */
    private boolean preFiltered = false;
    /**
     * 调用链工厂,用来获取目标方法的调用链
     */
    @Getter
    public AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
    /**
     * 方法调用链缓存:以方法为键,以顾问链表为值的缓存。
     */
    private transient Map<MethodCacheKey, List<Object>> methodCache;
    //代理对象需要实现的接口列表。保存在列表中以保持注册的顺序,以创建具有指定接口顺序的JDK代理。
    private List<Class<?>> interfaces = new ArrayList<>();
    //配置的顾问列表。所有添加的Advise对象都会被包装为Advisor对象
    private List<Advisor> advisors = new ArrayList<>();
    //数组更新了对advisor列表的更改,这更容易在内部操作。
    private Advisor[] advisorArray = new Advisor[0];

    //无参构造方法
    public AdvisedSupport() {
        this.methodCache = new ConcurrentHashMap<>(32);
    }

    //有参构造方法,参数为:代理需要实现的接口列表
    public AdvisedSupport(Class<?>... interfaces) {
        this();
        this.setInterfaces(interfaces);

    }

    //设置目标对象
    public void setTarget(Object target) {
        this.setTargetSource(new SingletonTargetSource(target));

    }


    //todo 此方法先忽略,用来为目标类引入接口的
    private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
        advisor.validateInterfaces();
        // If the advisor passed validation, we can make the change.
        Class<?>[] ifcs = advisor.getInterfaces();
        for (Class<?> ifc : ifcs) {
            addInterface(ifc);
        }
    }

    //指定的位置添加顾问
    private void addAdvisorInternal(int pos, Advisor advisor) throws
            AopConfigException {
        Assert.notNull(advisor, "Advisor must not be null");
        if (isFrozen()) {
            throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
        }
        if (pos > this.advisors.size()) {
            throw new IllegalArgumentException(
                    "Illegal position " + pos + " in advisor list with size " +
                            this.advisors.size());
        }
        this.advisors.add(pos, advisor);
        updateAdvisorArray();
        adviceChanged();
    }

    //将advisorArray和advisors保持一致
    protected final void updateAdvisorArray() {
        this.advisorArray = this.advisors.toArray(new Advisor[0]);
    }

    //获取顾问列表
    protected final List<Advisor> getAdvisorsInternal() {
        return this.advisors;
    }

    //设置被代理的目标类
    public void setTargetClass(Class<?> targetClass) {
        this.targetSource = EmptyTargetSource.forClass(targetClass);
    }

    //获取被代理的目标类型
    @Override
    public Class<?> getTargetClass() {
        return this.targetSource.getTargetClass();
    }

    //设置代理对象需要实现的接口
    public void setInterfaces(Class<?>... interfaces) {
        Assert.notNull(interfaces, "Interfaces must not be null");
        this.interfaces.clear();
        for (Class<?> ifc : interfaces) {
            addInterface(ifc);
        }

    }

    /**
     * 为代理对象添加需要实现的接口
     *
     * @param intf
     */
    public void addInterface(Class<?> intf) {
        Assert.notNull(intf, "Interface must not be null");
        if (!intf.isInterface()) {
            throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
        } else {
            if (!this.interfaces.contains(intf)) {
                this.interfaces.add(intf);
                this.adviceChanged();
            }

        }
    }

    /**
     * 通知更改时调用,会清空当前方法调用链缓存
     */
    protected void adviceChanged() {
        this.methodCache.clear();
    }

    /**
     *   * 设置顾问链工厂,当调用目标方法的时候,需要获取这个方法上匹配的Advisor列表,
     * 获取目标方法上匹配的Advisor列表的功能就是AdvisorChainFactory来负责的
     */
    public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
        Assert.notNull(advisorChainFactory, "advisorChainFactory is must not null");
        this.advisorChainFactory = advisorChainFactory;
    }

    //移除代理对象需要实现的接口
    public boolean removeInterface(Class<?> intf) {
        return this.interfaces.remove(intf);
    }

    /**
     * 获取代理对象需要实现的接口列表
     *
     * @return
     */
    @Override
    public Class<?>[] getProxiedInterfaces() {
        return ClassUtils.toClassArray(this.interfaces);
    }

    /**
     * 判断代理对象是否需要实现某个接口
     *
     * @param aClass
     * @return
     */
    @Override
    public boolean isInterfaceProxied(Class<?> aClass) {
        for (Class<?> proxyIntf : this.interfaces) {
            if (aClass.isAssignableFrom(proxyIntf)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void setTargetSource(@Nullable TargetSource targetSource) {
        this.targetSource = (targetSource == null ? EMPTY_TARGET_SOURCE : targetSource);
    }


    //获取被代理的目标源
    @Override
    public TargetSource getTargetSource() {
        return this.targetSource;
    }


    /**
     * todo
     * 设置此代理配置是否经过预筛选,这个什么意思呢:通过目标方法调用代理的时候,
     * 需要通过匹配的方式获取这个方法上的调用链列表,查找过程需要2个步骤:
     * 第一步:类是否匹配,第二步:方法是否匹配,当这个属性为true的时候,会直接跳过第一步,这个
     * 懂了不
     */
    @Override
    public void setPreFiltered(boolean preFiltered) {
        this.preFiltered = preFiltered;
    }

    // 返回preFiltered
    @Override
    public boolean isPreFiltered() {
        return this.preFiltered;
    }


    /**
     * 获取所有的顾问列表
     *
     * @return
     */
    @Override
    public Advisor[] getAdvisors() {
        return this.advisorArray;
    }

    /**
     * 添加顾问
     *
     * @param advisor
     * @throws AopConfigException
     */
    @Override
    public void addAdvisor(Advisor advisor) throws AopConfigException {
        int pos = this.advisors.size();
        this.addAdvisor(pos, advisor);
    }

    /**
     * 指定的位置添加顾问
     *
     * @param pos
     * @param advisor
     * @throws AopConfigException
     */
    @Override
    public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
        // todo 这块先忽略,以后讲解
        if (advisor instanceof IntroductionAdvisor) {
            validateIntroductionAdvisor((IntroductionAdvisor) advisor);
        }
        addAdvisorInternal(pos, advisor);
    }

    @Override
    public boolean removeAdvisor(Advisor advisor) {
        int index = indexOf(advisor);
        if (index == -1) {
            return false;
        }
        removeAdvisor(index);
        return true;
    }

    @Override
    public void removeAdvisor(int index) throws AopConfigException {
        //当配置如果是冻结状态,是不允许对顾问进行修改的,否则会抛出异常
        if (isFrozen()) {
            throw new AopConfigException("Cannot remove Advisor: Configuration is frozen.");
        }
        if (index < 0 || index > this.advisors.size() - 1) {
            throw new AopConfigException("Advisor index " + index + " is out of bounds:" +
                    "This configuration only has " + this.advisors.size() + " advisors. ");
        }
        //移除advisors中的顾问
        Advisor advisor = this.advisors.remove(index);
        if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            // We need to remove introduction interfaces.
            for (Class<?> ifc : ia.getInterfaces()) {
                removeInterface(ifc);
            }
        }
        //更新advisorArray
        updateAdvisorArray();
        //通知已改变,内部会清除方法调用链缓存信息。
        adviceChanged();
    }

    @Override
    public int indexOf(Advisor advisor) {
        Assert.notNull(advisor, "Advisor must not be null");
        return this.advisors.indexOf(advisor);
    }

    @Override
    public boolean replaceAdvisor(Advisor replaceAdvisor, Advisor targetAdvisor) throws AopConfigException {
        Assert.notNull(replaceAdvisor, "Advisor a must not be null");
        Assert.notNull(targetAdvisor, "Advisor b must not be null");
        int index = indexOf(replaceAdvisor);
        if (index == -1) {
            return false;
        }
        removeAdvisor(index);
        addAdvisor(index, targetAdvisor);
        return true;
    }

    /**
     * 添加通知
     * @param advice
     * @throws AopConfigException
     */
    @Override
    public void addAdvice(Advice advice) throws AopConfigException {
        int pos = this.advisors.size();
        this.addAdvice(pos, advice);
    }

    /**
     * 添加指定位置的通知
     * @param index
     * @param advice
     * @throws AopConfigException
     */
    @Override
    public void addAdvice(int index, Advice advice) throws AopConfigException {
        //此处会将advice通知包装为DefaultPointcutAdvisor类型的Advisor
        addAdvisor(index, new DefaultPointcutAdvisor(advice));
    }

    /**
     * 移除通知
     * @param advice
     * @return
     */
    @Override
    public boolean removeAdvice(Advice advice) {
        int index = indexOf(advice);
        if (index == -1) {
            return false;
        }
        removeAdvisor(index);
        return true;
    }

    /**
     *
     * @param advice
     * @return
     */
    @Override
    public int indexOf(Advice advice) {
        Assert.notNull(advice, "Advice must not be null");

        for (int i = 0; i < this.advisors.size(); i++) {
            Advisor advisor = this.advisors.get(i);
            if (advisor.getAdvice() == advice) {
                return i;
            }
        }
        return -1;
    }

    //是否包含某个通知
    public boolean adviceIncluded(@Nullable Advice advice) {
        if (advice != null) {
            for (Advisor advisor : this.advisors) {
                if (advisor.getAdvice() == advice) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取某种类型的通知数量
     * @param adviceClass
     * @return
     */
    public int countAdvicesOfType(Class<?> adviceClass) {
        int count = 0;
        if (adviceClass == null) {
            return count;
        }
        long count1 = this.advisors.stream().filter(e -> adviceClass.isInstance(e.getAdvice())).count();
        count = (int) count1;
        return count;
    }

    /**
     * 基于当前配置,获取给定方法的方法调用链列表(即
     * org.aopalliance.intercept.MethodInterceptor对象列表)
     *
     * @param method
     * @param targetClass
     * @return
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        MethodCacheKey methodCacheKey = new MethodCacheKey(method);
//        List<Object> objects = this.methodCache.get(methodCacheKey);
//        if (objects == null) {
//            objects = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
//                    this, method, targetClass);
//            this.methodCache.put(methodCacheKey, objects);
//        }

        return this.methodCache.computeIfAbsent(methodCacheKey, m -> this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass));
    }

    @Override
    public String toProxyConfigString() {
        return this.toString();
    }



    /**
     * 将other中的配置信息复制到当前对象中
     * @param other
     * @param targetSource
     * @param advisors
     */
    protected void copyConfigurationFrom(MyAdvisedSupport other, TargetSource
            targetSource, List<Advisor> advisors) {
        copyFrom(other);
        this.targetSource = targetSource;
        this.advisorChainFactory = other.advisorChainFactory;
        this.interfaces = new ArrayList<>(other.interfaces);
        for (Advisor advisor : advisors) {
            if (advisor instanceof IntroductionAdvisor) {
                validateIntroductionAdvisor((IntroductionAdvisor) advisor);
            }
            Assert.notNull(advisor, "Advisor must not be null");
            this.advisors.add(advisor);
        }
        updateAdvisorArray();
        adviceChanged();
    }

    //构建此AdvisedSupport的仅配置副本,替换TargetSource。

    /**
     * 构建此AdvisedSupport的仅配置副本,替换TargetSource。
     * @return
     */
    MyAdvisedSupport getConfigurationOnlyCopy() {
        MyAdvisedSupport copy = new MyAdvisedSupport();
        copy.copyFrom(this);
        copy.targetSource = EmptyTargetSource.forClass(getTargetClass(),
                getTargetSource().isStatic());
        copy.advisorChainFactory = this.advisorChainFactory;
        copy.interfaces = this.interfaces;
        copy.advisors = this.advisors;
        copy.updateAdvisorArray();
        return copy;
    }





    private static final class MethodCacheKey implements Comparable<MethodCacheKey> {
        private final Method method;
        private final int hashCode;

        public MethodCacheKey(Method method) {
            this.method = method;
            this.hashCode = method.hashCode();
        }

        public boolean equals(@Nullable Object other) {
            return this == other || other instanceof MyAdvisedSupport.MethodCacheKey && this.method == ((MyAdvisedSupport.MethodCacheKey) other).method;
        }

        public int hashCode() {
            return this.hashCode;
        }

        public String toString() {
            return this.method.toString();
        }

        public int compareTo(MethodCacheKey other) {
            int result = this.method.getName().compareTo(other.method.getName());
            if (result == 0) {
                result = this.method.toString().compareTo(other.method.toString());
            }

            return result;
        }
    }

}

上面几个类有几个结论,这里说一下。

  1. 配置中添加的Advice对象最终都会被转换为DefaultPointcutAdvisor对象,此时
    DefaultPointcutAdvisor未指定pointcut,大家可以去看一下DefaultPointcutAdvisor中pointcut有个默认值,默认会匹配任意类的任意方法。
  2. 当配置被冻结的时候,即frozen为true的时,此时配置中的Advisor列表是不允许修改的。
  3. 上面的 getInterceptorsAndDynamicInterceptionAdvice 方法,通过代理调用目标方法的时候,最后需要通过方法和目标类的类型,从当前配置中会获取匹配的方法拦截器列表,获取方法拦截器列表是由 AdvisorChainFactory 负责的。
    getInterceptorsAndDynamicInterceptionAdvice 会在调用代理的方法时会执行,稍后在执行阶段会详解。
  4. 目标方法和其关联的方法拦截器列表会被缓存在 methodCache 中,当顾问列表有变化的时候,methodCache 缓存会被清除。

配置阶段完成之后,下面进入AopProxy获取阶段。
此阶段会根据AdvisedSupport中配置信息,判断具体是采用cglib的方式还是采用jdk动态代理的方式获取代理对象,先看一下涉及到的一些类。
image.png

AopProxy接口

package org.springframework.aop.framework;

import org.springframework.lang.Nullable;

public interface AopProxy {
    /**
     * 
     * @return 创建一个新的代理对象
     */
    Object getProxy();

    /**
     * 
     * @param classLoader
     * @return 创建一个新的代理对象
     */
    Object getProxy(@Nullable ClassLoader classLoader);
}

AopProxy的2个实现类,实现了上面定义的2个方法,稍后在代理的创建阶段详细介绍。

AopProxyFactory接口
通过名称就可以看出来,是一个工厂,负责创建AopProxy,使用的是简单工厂模式。
接口中定义了一个方法,会根据Aop的配置信息AdvisedSupport来获取AopProxy对象,主要是判断采用cglib的方式还是采用jdk动态代理的方式。

package org.springframework.aop.framework;
public interface AopProxyFactory {
    /**
    * 根据aop配置信息获取AopProxy对象
    */
    AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}

DefaultAopProxyFactory类
AopProxyFactory接口的默认实现,代码比较简单,我们来细看一下

/**
 * 默认AopProxyFactory实现,创建CGLIB代理或JDK动态代理。
 * 对于给定的AdvisedSupport实例,以下条件为真,则创建一个CGLIB代理:
 * optimize = true
 * proxyTargetClass = true
 * 未指定代理接口
 代理创建阶段
 到目前为止我们已经根据aop配置信息得到了AopProxy对象了,下面就可以调用AopProxy.getProxy方
 法获取代理对象了。
 AopProxy.createAopProxy方法返回的结果有2种情况
 JdkDynamicAopProxy:以jdk动态代理的方式创建代理
 ObjenesisCglibAopProxy:以cglib的方式创建动态代理
 项目详解这2个类的源码 。
 * 通常,指定proxyTargetClass来强制执行CGLIB代理,或者指定一个或多个接口来使用JDK动态代理。
 */
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public DefaultAopProxyFactory() {
    }

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // optimize==false && proxyTargetClass 为false && 配置中有需要代理的接口
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                /**
                 * 如果被代理的类为接口 或者 被代理的类是jdk动态代理创建代理类,则采用
                 * JdkDynamicAopProxy的方式,否则采用cglib代理的方式
                 */
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

    /**
     * 
     * @param config
     * @return 确定所提供的AdvisedSupport是否只指定了SpringProxy接口(或者根本没有指定代理接口)
     */
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
    }
}

代理创建阶段
到目前为止我们已经根据aop配置信息得到了AopProxy对象了,下面就可以调用AopProxy.getProxy方
法获取代理对象了。
AopProxy.createAopProxy方法返回的结果有2种情况

  • JdkDynamicAopProxy:以jdk动态代理的方式创建代理
  • ObjenesisCglibAopProxy:以cglib的方式创建动态代理

篇幅较长,下回分解吧,88

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2129572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

漏洞复现-泛微-E-Cology-SQL

更多漏洞分析复现&#xff0c;可前往无问社区查看http://www.wwlib.cn/index.php/artread/artid/10358.html 0x01 产品简介 泛微e-cology是一款由泛微网络科技开发的协同管理平台&#xff0c;支持人力资源、财务、行政等多功能管理和移动办公。 0x02 漏洞概述 泛微e-cology…

路由器WAN口和LAN口有什么不一样?

“ 路由器WAN口和LAN口的区别&#xff0c;WAN是广域网端口&#xff0c;LAN是本地网端口。WAN主要用于连接外部网络&#xff0c;而LAN用来连接家庭内部网络&#xff0c;两者主要会在标识上面有区别。以往大部分路由器的WAN只有一个&#xff0c;LAN口则有四个或以上&#xff0c;近…

shader 案例学习笔记之偏移

效果 代码 #ifdef GL_ES precision mediump float; #endifuniform vec2 u_resolution; uniform float u_time;vec2 brickTile(vec2 _st, float _zoom){_st * 5.;_st.x step(1., mod(_st.y,2.0)) * 0.5;return fract(_st); }float box(vec2 _st, vec2 _size){_size vec2(0.5)…

红帽9中nginx-源码编译php

什么是PHP-FPM&#xff1f; PHP-FPM(FastCGI Process Manager&#xff1a; FastCGI进程管理器)是一个实现了Fastcgi的程序&#xff0c;并且提供进程管理的功能。 进程包括master进程和worker进程。master进程只有一个&#xff0c;负责监听端口&#xff0c;接受来自web server 的…

Linux进程(2)(进程状态 - 僵尸、孤儿进程)

目录 1.进程状态 1&#xff09;直接谈论Linux的进程状态 R &#xff1a;进程运行的状态 S &#xff1a;休眠状态 &#xff08;进程在等待“资源”就绪&#xff1b;可中断睡眠&#xff09; T / t &#xff1a;让进程暂停&#xff0c;等待被进一步唤醒 D …

【springboot】整合spring security 和 JWT

目录 1. 整合spring security 1. 导入依赖 2. 配置类 3. 实体类实现UserDetails接口 4. 业务逻辑实现类实现UserDetailsService接口 5. 控制类实现登录功能 6. 测试登录功能 2. 分析源码 1. UsernamePasswordAuthenticationToken 2. A…

【c/c++】类型转换:隐式类型转换、强制类型转换

目录 前言 一、了解类型转换 二、隐式类型转换 1.适用场景 2.转换规则 三、强制类型转换 适用场景 使用规则 注意事项 前言 类型转换是编程中一个常见的现象。在我们进行编码的时候不经意间就发生了&#xff0c;但却能让整个程序运行得更加流畅。 但是这种不经意&am…

C++:STL之vector

1.vector的使用 1.1vector的定义 使用vector需要包含头文件 #include<vector> vector的构造 &#xff08;constructor&#xff09;构造函数声明接口说明vector() (重点)无参构造vector(size_type n,const value_type& val value_type())用n个val初始化并构造vecto…

Java11环境安装(Windows)

目录 1 Java11安装2 配置2.1 JavaHome配置2.2 CLASSPATH配置PATH路径配置 3 验证 1 Java11安装 从官网下载Java11安装包&#xff1a;jdk-11_windows-x64_bin.exe,安装时选择安装到D:\Java目录。 安装完目录结构如下&#xff1a; 2 配置 2.1 JavaHome配置 如下图所示配置JAV…

Ubuntu构建只读文件系统

本文介绍Ubuntu构建只读文件系统。 嵌入式系统使用过程中&#xff0c;有时会涉及到非法关机&#xff08;比如直接关机&#xff0c;或意外断电&#xff09;&#xff0c;这可能造成文件系统损坏&#xff0c;为了提高系统的可靠性&#xff0c;通常将根文件系统设置为只读&#xf…

Linux下进程间的通信--信号量

前言&#xff1a; 资源竞争&#xff1a; 资源竞争&#xff08;Race Condition&#xff09;是多线程或多进程环境中的一种常见问题&#xff0c;它发生在多个进程或线程并发访问和修改同一资源&#xff08;如内存位置、文件、数据库记录等&#xff09;时&#xff0c;而最终结果…

mysql学习教程,从入门到精通,SQL AND OR 运算符(12)

1、SQL AND & OR 运算符 在本教程中&#xff0c;您将学习如何在子句中使用ASELECT column1_name, column2_name, columnN_nameFROM table_nameWHERE condition1 AND condition2;ND&#xff06;OR运算符&#xff0c;WHERE以根据多个条件过滤记录。 1.1、根据条件选择记录 …

从代码直观理解Self-Attention和Cross-Attention的本质区别

Transformer的模型架构实际上非常简单&#xff0c;Self-Attention 和 Cross-Attention 仅仅是在 k, v上有所不同&#xff08;这里不讨论 mask&#xff09;。 论文原文&#xff1a;Attention Is All You Need 我们可以使用同一个 Attention 类来实现 Self-Attention 和 Cross-At…

day11-多线程

一、线程安全问题 线程安全问题出现的原因&#xff1f;存在多个线程在同时执行同时访问一个共享资源存在修改该共享资源 线程安全:多个线程同时修改同一个资源 取钱案例 小明和小红是一对夫妻&#xff0c;他们有一个共同的账户&#xff0c;余额是10万元 如果小明和小红同时来取…

速看!6款可以写论文的ai写作网站,这才是真正的论文神器!(含教程)

在当今信息爆炸的时代&#xff0c;AI写作工具的出现极大地提高了写作效率和质量。特别是对于需要撰写论文的学生和研究人员来说&#xff0c;这些工具提供了极大的便利。本文将重点介绍一款备受推荐的AI写作平台——千笔-AIPassPaper&#xff0c;并结合相关教程帮助用户更好地使…

【北京迅为】《STM32MP157开发板使用手册》- 第二十四章 STM32CubeIDE的初步使用

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

校园水电费管理微信小程序的设计与实现+ssm(lw+演示+源码+运行)

校园水电费管理小程序 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来…

基于SSM的学生信息管理系统(选课管理系统)的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的学生信息管理系统&#xff08;选课管理系统&#xff09;13拥有三种角色 管理员&#xff1a;学生管理、教师管理、专业管理、课程管理、审批管理、课程表管理、开课管理、教室管…

高德地图JS API加载行政区边界AMap.Polygon

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f49…

大模型LLM之SpringAI:Web+AI(二)

2.2.2、ChatModel API(聊天模型API) 聊天模型太多了,这里只写OpenAI和Ollama ChatModel和ChatClient区别:ChatClient针对的是所有模型,共用一个客户端。而ChatModel是针对各个模型实现的。 (1)OpenAI 自动配置 <dependency><groupId>org.springframework…