Spring 中的 ProxyFactory 创建代理对象

news2024/11/24 17:16:27

一、jdk 动态代理 和 cglib动态代理 简单介绍 

        1.jdk动态代理

public interface AService {
    public String serviceA(String param);
    public String serviceAA(String param);
}
public interface BService {
    public String serviceB(String param);
    public String serviceBB(String param);
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkCustomInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        if(method.equals(AService.class.getMethod("serviceA" , String.class))){
            // 在这里可以写 当我们把代理当作 AService 的实现子类,调用 serviceA 方法时
            // 我们要执行的代码,这里我们姑且就简单打印,并返回一个值
            System.out.println("JdkCustomInvocationHandler 开始处理 com.fll.start.dynamic_proxy.jdk.AService.serviceA 方法");
            return "JdkCustomInvocationHandler 处理 com.fll.start.dynamic_proxy.jdk.BService.serviceA 的结果";
        }else if(method.equals(BService.class.getMethod("serviceB", String.class))){
            // 在这里可以写 当我们把代理当作 BService 的实现子类,调用 serviceB 方法时
            // 我们要执行的代码,这里我们姑且就简单打印,并返回一个值
            System.out.println("JdkCustomInvocationHandler 开始处理 com.fll.start.dynamic_proxy.jdk.BService.serviceB 方法");
            return "JdkCustomInvocationHandler 处理 com.fll.start.dynamic_proxy.jdk.BService.serviceB 的结果";
        }else {
            return "暂时对该方法没有处理逻辑";
        }

    }
}

这里总结下jdk动态代理的特点:
        1.可以不需要实现类,直接对接口进行代理
        2.创建代理对象时返回的类型时Object,但是可以将其强转为任何一个它所代理的接口类型
因为生成的代理对象是它所代理的所有接口的实现类
        3.当我们将代理对象强转为它所代理的接口类型进行方法调用时,所有的调用都会回调到InvocationHandler 对象的 invoke 方法,在回调 invoke 方法的参数中有我们调用的方法对象 Method method,和调用时所传递的所有参数Object[] objects,Object o就是代理对象本身
        4.关于这几个参数的注意点
                1.不能在 invoke 方法中将代理对象强转为它所代理的某一个接口,然后调用其方法,这          样会形成递归调用,造成栈内存溢出
                2.如果没有所代理的接口的真正实现类,不可以通过反射的方法调用该方法,因为通过            反射的方式,Method.invoke() 方法进行调用时,需要传递真正实现了该接口的实现类的一个          对象,这里的Object o对象虽然也是接口的实现类的对象,但是不能传递 o ,如果过传递                o,还是相当于调用了代理对象的方法,也会形成递归调用。
                3.要想让代理对象调用不同的方法时,分别执行我们想要的逻辑,只能在 invoke 方法的          回调中通过判断 Method method 对象的不同,执行不同业务逻辑
        5.这样的的代理方式没啥实际意义,只是把对所有接口方法的调用全部回调到了 InvocationHandler.invoke() 方法中,在invoke中进行分别实现,会造成代码冗长杂乱

 接下来常规用法

public class AServiceImpl implements AService{
    @Override
    public String serviceA(String param){
        System.out.println("AServiceImpl 开始执行 serviceA,param=" + param);
        return "AServiceImpl.serviceA 处理的结果";
    }
    @Override
    public String serviceAA(String param) {
        System.out.println("AServiceImpl 开始执行 serviceAA,param=" + param);
        return "AServiceImpl.serviceAA 处理的结果";
    }
}
public class BServiceImpl implements BService{
    @Override
    public String serviceB(String param){
        System.out.println("BServiceImpl 开始执行 serviceB,param=" + param);
        return "BServiceImpl.serviceB 处理的结果";
    }
    @Override
    public String serviceBB(String param) {
        System.out.println("BServiceImpl 开始执行 serviceBB,param=" + param);
        return "BServiceImpl.serviceBB 处理的结果";
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkCustomInvocationHandler1 implements InvocationHandler {

    private AService aService;
    private BService bService;

    public JdkCustomInvocationHandler1(AService aService , BService bService){
        this.aService = aService;
        this.bService = bService;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        if(method.equals(AService.class.getMethod("serviceA" , String.class))){
            System.out.println(String.format("执行%s前置加强逻辑" , aService.getClass() +"."+ method.getName()));
            Object retVal = method.invoke(this.aService , objects);
            System.out.println(String.format("执行%s后置加强逻辑" , aService.getClass() +"."+ method.getName()));
            return retVal + "(也可以被代理对象再加工)";
        }else if(method.equals(BService.class.getMethod("serviceB", String.class))){
            System.out.println(String.format("执行%s前置加强逻辑" , bService.getClass() +"."+ method.getName()));
            Object retVal = method.invoke(this.bService , objects);
            System.out.println(String.format("执行%s后置加强逻辑" , bService.getClass() +"."+ method.getName()));
            return retVal + "(也可以被代理对象再加工)";
        }else {
            return "暂时对该方法没有处理逻辑";
        }

    }
}
public class JdkDynamicProxyTest1 {


    public static void main(String[] args) throws IOException {

        ClassLoader classLoader = JdkDynamicProxyTest1.class.getClassLoader();
        Class[] interfaces = {AService.class, BService.class};

        AService aService = new AServiceImpl();
        BService bService = new BServiceImpl();
        JdkCustomInvocationHandler1 jdkCustomInvocationHandler
                = new JdkCustomInvocationHandler1(aService , bService);

        AService aServiceProxy = (AService) Proxy.newProxyInstance(classLoader, interfaces, jdkCustomInvocationHandler);
        String test = aServiceProxy.serviceA("AService");
        System.out.println(test);

        System.out.println();

        BService bServiceProxy = (BService) Proxy.newProxyInstance(classLoader , interfaces , jdkCustomInvocationHandler);
        String test1 = bServiceProxy.serviceB("BService");
        System.out.println(test1);


    }

}

运行结果:
执行class com.fll.start.dynamic_proxy.jdk.AServiceImpl.serviceA前置加强逻辑
AServiceImpl 开始执行 serviceA,param=AService
执行class com.fll.start.dynamic_proxy.jdk.AServiceImpl.serviceA后置加强逻辑
AServiceImpl.serviceA 处理的结果(也可以被代理对象再加工)

执行class com.fll.start.dynamic_proxy.jdk.BServiceImpl.serviceB前置加强逻辑
BServiceImpl 开始执行 serviceB,param=BService
执行class com.fll.start.dynamic_proxy.jdk.BServiceImpl.serviceB后置加强逻辑
BServiceImpl.serviceB 处理的结果(也可以被代理对象再加工)

          这次的特点:
                1.所代理的接口本来就有自己的实现类
                2.调用所代理的接口的方法时,最终都会在 InvocationHandler.invoke() 方法中通过反射           的方式调用到接口实现类对象的对应方法上,只不过我们可以在调用实现类方法之前或者之           后执行额外的逻辑,进行加强,也可以对实现类返回的结果进行再加工

        其实这种方式才是代理模式要达到的真正目的,本来就有实现好的功能,而且代码运行稳定,或者说一个黑盒子,我们只知道其功能和参数,这些情况下,我们想要对原本的功能或者黑盒子进行加强,但是又不想修改原来代码逻辑,所以就可以通过代理,在原来的功能之上 ,进行额外的加强处理

 通过jdk源码,看看代理对象是如何创建的
java.lang.reflect.Proxy#newProxyInstance

/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 *
 * <p>{@code Proxy.newProxyInstance} throws
 * {@code IllegalArgumentException} for the same reasons that
 * {@code Proxy.getProxyClass} does.
 *
 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * @param   h the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 * @throws  IllegalArgumentException if any of the restrictions on the
 *          parameters that may be passed to {@code getProxyClass}
 *          are violated
 * @throws  SecurityException if a security manager, <em>s</em>, is present
 *          and any of the following conditions is met:
 *          <ul>
 *          <li> the given {@code loader} is {@code null} and
 *               the caller's class loader is not {@code null} and the
 *               invocation of {@link SecurityManager#checkPermission
 *               s.checkPermission} with
 *               {@code RuntimePermission("getClassLoader")} permission
 *               denies access;</li>
 *          <li> for each proxy interface, {@code intf},
 *               the caller's class loader is not the same as or an
 *               ancestor of the class loader for {@code intf} and
 *               invocation of {@link SecurityManager#checkPackageAccess
 *               s.checkPackageAccess()} denies access to {@code intf};</li>
 *          <li> any of the given proxy interfaces is non-public and the
 *               caller class is not in the same {@linkplain Package runtime package}
 *               as the non-public interface and the invocation of
 *               {@link SecurityManager#checkPermission s.checkPermission} with
 *               {@code ReflectPermission("newProxyInPackage.{package name}")}
 *               permission denies access.</li>
 *          </ul>
 * @throws  NullPointerException if the {@code interfaces} array
 *          argument or any of its elements are {@code null}, or
 *          if the invocation handler, {@code h}, is
 *          {@code null}
 */
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)throws IllegalArgumentException{

    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /* 
     * 生成代理类的 Class 对象
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        // 这里的private static final Class<?>[] constructorParams =
        // {InvocationHandler.class };
        // 通过代理类的 Class 对象获取参数为 InvocationHandler 对象的构造器对象
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 通过代理类构造器对象创一个 代理类的对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

java.lang.reflect.Proxy#getProxyClass0 

/**
 * Generate a proxy class.  Must call the checkProxyAccess method
 * to perform permission checks before calling this.
 */
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    // 如果被指定的类加载器定义好的,实现了指定接口的代理类 Class 对象已经存在了
    // 那就简单的返回缓存中的备份,相反如果不存在,就得通过ProxyClassFactory创建
    // 代理类的 Class 对象,并放入缓存中
    return proxyClassCache.get(loader, interfaces);
}

 java.lang.reflect.WeakCache#get

/**
 * Look-up the value through the cache. This always evaluates the
 * {@code subKeyFactory} function and optionally evaluates
 * {@code valueFactory} function if there is no entry in the cache for given
 * pair of (key, subKey) or the entry has already been cleared.
 *
 * @param key       possibly null key
 * @param parameter parameter used together with key to create sub-key and
 *                  value (should not be null)
 * @return the cached value (never null)
 * @throws NullPointerException if {@code parameter} passed in or
 *                              {@code sub-key} calculated by
 *                              {@code subKeyFactory} or {@code value}
 *                              calculated by {@code valueFactory} is null.
 */
public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

    Object cacheKey = CacheKey.valueOf(key, refQueue);
	
    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

	//
    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
	// 生成代理类Class对象的主要逻辑在 subKeyFactory.apply(key, parameter)
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    while (true) {
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

 java.lang.reflect.Proxy.ProxyClassFactory#apply

/**
 * A factory function that generates, defines and returns the proxy class given
 * the ClassLoader and array of interfaces.
 */
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * Choose a name for the proxy class to generate.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * Generate the specified proxy class.
         * 生成代理类,我们看到生成代理类Class对象需要两部
         * 1.生成一个字节数组,其实等价于我们通过.class文件创建Class对象的时候,把.class文件            
         * 加载进内存,放到一个字节数组中
         * 2.通过 defineClass0() 方法把字节数组解析为一个Class对象
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

 我们看到可以通过以下代码生成一个等价于.class文件的字节数组,那我们把生成的这个字节数组输出到一个文件,是不是就相当于得到了这个代理类的 .class 文件了

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
import sun.misc.ProxyGenerator;

import java.io.IOException;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ProxyGeneratorTest {

    public static void main(String[] args) throws IOException {

        ClassLoader classLoader = JdkDynamicProxyTest1.class.getClassLoader();
        Class[] interfaces = {AService.class, BService.class};

        AService aService = new AServiceImpl();
        BService bService = new BServiceImpl();
        JdkCustomInvocationHandler1 jdkCustomInvocationHandler
                = new JdkCustomInvocationHandler1(aService , bService);

        Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, jdkCustomInvocationHandler);

        byte[] bytes = ProxyGenerator.generateProxyClass(proxyInstance.getClass().getSimpleName(), interfaces);
        Files.write(Paths.get("D:/springboot-demo/study-spring/src/main/java/com/fll/start/dynamic_proxy/jdk/" + proxyInstance.getClass().getSimpleName() + ".class") , bytes);

    }

}

 以下是使用idea将生成的.class文件反编译之后得到的源码

import com.fll.start.dynamic_proxy.jdk.AService;
import com.fll.start.dynamic_proxy.jdk.BService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements AService, BService {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m6;
    private static Method m3;
    private static Method m0;
    private static Method m5;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String serviceA(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String serviceBB(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m6, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String serviceAA(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String serviceB(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m5, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.fll.start.dynamic_proxy.jdk.AService").getMethod("serviceA", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("com.fll.start.dynamic_proxy.jdk.BService").getMethod("serviceBB", Class.forName("java.lang.String"));
            m3 = Class.forName("com.fll.start.dynamic_proxy.jdk.AService").getMethod("serviceAA", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m5 = Class.forName("com.fll.start.dynamic_proxy.jdk.BService").getMethod("serviceB", Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这里注意最好用 jdk1.8,在jdk11 中,这个方法就不是public了,测试调用的话比较麻烦 

可以看到这里的代理类 除了实现了 我们要代理的接口 AService, BService之外还结成了java.lang.reflect.Proxy,我们使用代理类的构造器创建代理对象时,所传的 构造器参数InvocationHandler 就是通过 super(InvocationHandler) 传到了java.lang.reflect.Proxy

        2.cglib动态代理

        

二、Spring中的 三个重要的概念 Advice  Advisor  Advised

        1.Advice

        2.Advisor

        3.Advised

三、ProxyFactory 类的继承结构,以及每一层中实现的功能

四、AopProxyFactory的默认实现类DefaultAopProxyFactory的介绍

五、Spring 中的 InvocationHandler --- JdkDynamicAopProxy

六、AdvisedSupport 中 getInterceptorsAndDynamicInterceptionAdvice 详解

七、AdvisorChainFactory 的默认实现类 DefaultAdvisorChainFactory

八、AdvisorAdapterRegistry 的 默认实现类 DefaultAdvisorAdapterRegistry

九、简单介绍 Interceptor

十、详细介绍 MethodInterceptor 

十一、如何通过 MethodInterceptor 几个实现类实现 Advice 的顺序调用

AdvisorChainFactory 的默认实现类 DefaultAdvisorChainFactory

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

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

相关文章

读取各种来源格式单细胞数据集构建seurat分析对象,代做生信分析

参考资料和分析注意事项 全流程的分析指导视频 演示数据集网盘文件 分析参数文件路径格式的特别提示 大家给要分析用到的文件路径或目录路径的时候&#xff0c;以D:/omics_tools/demo_data/scrnaseq/GSE189125/GSE189125_5prime_scRNAseq_seqbatchA_counts.txt.gz 这个文件为…

SQL-多表操作

前文所介绍的sql操作都是基于单表进行的&#xff0c;接下来我们来学习多表操作。 多表设计 在实际的项目开发中&#xff0c;会根据业务需求和业务模块之间的关系进行数据库表结构设计&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xf…

c++ STL线程安全使用

c STL不是线程安全的&#xff0c;因此在多线程中使用的时候&#xff0c;操作同一个容器&#xff0c;会崩溃&#xff0c;因此需要解决线程安全的问题&#xff1a; 使用实例类似于以下&#xff1a; #include <thread> #include <vector> #include "thread_safe…

Swift 实现判断链表是否存在环:快慢指针法

文章目录 前言摘要描述题解答案题解代码题解代码分析示例测试及结果时间复杂度空间复杂度总结关于我们 前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 LeetCode - #141 环形链表 不积跬步&#xff0c;无以至千里&#xff1b;不积小流…

SpringCloud实用-OpenFeign 调用三方接口

文章目录 前言正文一、项目环境二、项目结构2.1 包的含义2.2 代理的场景 三、完整代码示例3.1 定义FeignClient3.2 定义拦截器3.3 配置类3.4 okhttp配置3.5 响应体3.5.1 天行基础响应3.5.2 热点新闻响应 3.6 代理类3.6.1 代理工厂3.6.2 代理客户端3.6.3 FeignClient的建造器 四…

C++设计模式行为模式———中介者模式

文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互&#xff0c; 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…

HarmonyOS:使用ArkWeb构建页面

一、简介 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 页面加载过程中&#xff0c;若涉及网络资源获取&#xff0c;需要配置ohos.permission.INTERNET网络访问权限。 二、…

矩阵的拼接

矩阵的拼接分为横向拼接和纵向拼接 注意&#xff1a;横向拼接要求两矩阵行数相同&#xff0c;纵向拼接要求两矩阵列数相同 h o r z c a t horzcat horzcat和 v e r t c a t vertcat vertcat函数 h o r z c a t ( a , b ) horzcat(a,b) horzcat(a,b)将 a a a和 b b b横向拼接&a…

SpringCloud框架学习(第五部分:SpringCloud Alibaba入门和 nacos)

目录 十二、SpringCloud Alibaba入门简介 1. 基本介绍 2.作用 3.版本选型 十三、 SpringCloud Alibaba Nacos服务注册和配置中心 1.简介 2.各种注册中心比较 3.下载安装 4.Nacos Discovery服务注册中心 &#xff08;1&#xff09; 基于 Nacos 的服务提供者 &#xf…

Ollama vs VLLM:大模型推理性能全面测评!

最近在用本地大模型跑实验&#xff0c;一开始选择了ollama,分别部署了Qwen2.5-14B和Qwen2.5-32B&#xff0c;发现最后跑出来的实验效果很差&#xff0c;一开始一直以为prompt的问题&#xff0c;尝试了不同的prompt&#xff0c;最后效果还是一直不好。随后尝试了vllm部署Qwen2.5…

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了&#xff0c;今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进&#xff0c;包含了数千项的修改&#xff0c;今天主要和大家一起体…

LeetCode 144.二叉树的前序遍历

题目&#xff1a;给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 思路&#xff1a;根 左 右 代码&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNod…

【论文阅读】WGSR

0. 摘要 0.1. 问题提出 1.超分辨率(SR)是一个不适定逆问题&#xff0c;可行解众多。 2.超分辨率(SR)算法在可行解中寻找一个在保真度和感知质量之间取得平衡的“良好”解。 3.现有的方法重建高频细节时会产生伪影和幻觉&#xff0c;模型区分图像细节与伪影仍是难题。 0.2. …

游戏引擎学习第21天

虽然没有上一节的难但是内容也很多 关于实现和使用脚本语言 以下是详细复述&#xff1a; 许多人经常问一个问题&#xff0c;反复问过好几次&#xff0c;那就是&#xff1a;是否会在项目中实现脚本语言。这个问题的具体形式通常是&#xff1a;你们会使用脚本语言吗&#xff1…

NVR接入录像回放平台EasyCVR视频融合平台加油站监控应用场景与实际功能

在现代社会中&#xff0c;加油站作为重要的能源供应点&#xff0c;面临着安全监管与风险管理的双重挑战。为应对这些问题&#xff0c;安防监控平台EasyCVR推出了一套全面的加油站监控方案。该方案结合了智能分析网关V4的先进识别技术和EasyCVR视频监控平台的强大监控功能&#…

springboot vue工资管理系统源码和答辩PPT论文

人类现已迈入二十一世纪&#xff0c;科学技术日新月异&#xff0c;经济、资讯等各方面都有了非常大的进步&#xff0c;尤其是资讯与网络技术的飞速发展&#xff0c;对政治、经济、军事、文化等各方面都有了极大的影响。 利用电脑网络的这些便利&#xff0c;发展一套工资管理系统…

部署实战(二)--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

RabbitMQ4:work模型

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

SpringMVC——简介及入门

SpringMVC简介 看到SpringMVC这个名字&#xff0c;我们会发现其中包含Spring&#xff0c;那么SpringMVC和Spring之间有怎样的关系呢&#xff1f; SpringMVC隶属于Spring&#xff0c;是Spring技术中的一部分。 那么SpringMVC是用来做什么的呢&#xff1f; 回想web阶段&#x…

鸿蒙开发-文件与分享

文件分类 按所有者&#xff1a; 应用文件&#xff1a;所有者为应用&#xff0c;包括应用安装文件、应用资源文件、应用缓存文件等。 用户文件&#xff1a;所有者是登录到该终端设备的用户&#xff0c;包括用户私有的图片、视频、音频、文档等。 系统文件&#xff1a;与应用和…