一起学SF框架系列7.4-spring-AOP-AOP代理创建

news2024/9/29 19:15:16

AOP的BeanDefinition加载后,Spring提供了自动代理机制,让容器自动根据目标bean生成AOP代理bean,本文讲述具体如何实现。

基本机制

Spring的启动过程中,在bean实例化前后、初始化前后均提供了外部介入处理机制(详见“一起学SF框架系列5.3-spring-Beans-bean与Spring容器的交互方式”)。AOP正是通过其中BeanPostProcessor类来自动完成这项工作。下面是自动代理创建类关系图:
在这里插入图片描述
从上图可看出,自动创建代理类均继承于AbstractAutoProxyCreator,而AbstractAutoProxyCreator实现了接口SmartInstantiationAwareBeanPostProcessor,该接口继承于BeanPostProcessor。

启动入口

在bean创建过程中,自动代理创建类可以有四个介入时机:
1、实例化前(SmartInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation)
2、实例化后(SmartInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation)
3、初始化前(BeanPostProcessor.postProcessBeforeInitialization)
4、初始化后(BeanPostProcessor.postProcessAfterInitialization)
实际代码跟踪,AbstractAutoProxyCreator实现了如下两个接口:
实例化前:postProcessBeforeInstantiation
初始化后:postProcessAfterInitialization
跟踪启动过程,真正入口在AbstractAutoProxyCreator.postProcessAfterInitialization。这更符合实际:因为应用开发时,目标对象bean是不需要知道AOP任何内容的,有AOP能正常工作,无AOP也能正常工作,因此目标对象bean在初始化后再转换成代理对象就更恰当。

源码跟踪

AbstractAutoProxyCreator.postProcessAfterInitialization(@Nullable Object bean, String beanName)

创建bean的代理对象。

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 为给定的bean类和bean名称构建一个缓存键
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// bean没有对应的代理,则创建代理
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

AbstractAutoProxyCreator.wrapIfNecessary(Object bean, String beanName, Object cacheKey)

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 如果目标bean已存在(在bean实例化前创建的),直接返回
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 目标bean不是advisedBean,直接返回
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 如果是基础类(是不能被代理的)或者 应该跳过的bean(“跳过”意思是给定的bean不应被此后处理器进行自动代理),直接返回
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		/* 创建代理类 */
		// 获取所有适用于当前Bean的Advisors 注1
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		/* 存在适用于当前Bean的Advisors */
		if (specificInterceptors != DO_NOT_PROXY) {
			// 标记该bean是被代理类的bean
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 创建代理bean  注2
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 标记bean的代理类型
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		// 标记该bean非被代理类的bean
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

注1/ 注2方法过程均比较复杂,见下面专门章节。

获取所有适用于当前Bean的Advisors

AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource)

	// 过渡类
	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

	// 查找适用于bean所有符合条件的Advisors
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 查找BeanFactory所有Advisors
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 找适用于bean的Advisors
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			// 排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
	// 查找BeanFactory所有Advisors
	protected List<Advisor> findCandidateAdvisors() {
		Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
		// 返回所有Advisors
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}

	// 找适用于bean的Advisors
	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
		// 设置代理创建容器当前处理的beanName
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			// 适配查找
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			// 清理代理创建容器当前处理的beanName
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()

// 查找BeanFactory所有Advisors

	public List<Advisor> findAdvisorBeans() {
		// 获取所有已缓存的AdvisorBeanNames
		String[] advisorNames = this.cachedAdvisorBeanNames;
		/* 缓存没有 */
		if (advisorNames == null) {
			// 在beanFactory中查找所有Advisor类
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			// 结果放到缓存
			this.cachedAdvisorBeanNames = advisorNames;
		}
		// 不存在Advisor类
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}

		List<Advisor> advisors = new ArrayList<>();
		/* 逐一判断每个advisor是否适合当前bean */
		for (String name : advisorNames) {
			if (isEligibleBean(name)) {  // isEligibleBean返回的总是true
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					// 当前bean正被创建中,就跳过
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
						// 获取每个advisor的bean实例(当成普通bean获取实例),加入到集合中
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						Throwable rootCause = ex.getMostSpecificCause();
						if (rootCause instanceof BeanCurrentlyInCreationException) {
							BeanCreationException bce = (BeanCreationException) rootCause;
							String bceBeanName = bce.getBeanName();
							if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
							// 如果异常是因为bean正在创建中引起的,则continue处理下一个advisor
								if (logger.isTraceEnabled()) {
									logger.trace("Skipping advisor '" + name +
											"' with dependency on currently created bean: " + ex.getMessage());
								}
								continue;
							}
						}
						throw ex;
					}
				}
			}
		}
		return advisors;
	}

注意:advisor类都是prototype类型,因此每次获取都需要创建

AopUtils.findAdvisorsThatCanApply(List candidateAdvisors, Class<?> clazz)

从Advisors侯选择中选择适合beanClass的Advisors

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		// 后选择为空
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}

		// 选择结果集合
		List<Advisor> eligibleAdvisors = new ArrayList<>();

		/* 处理引介增强 declare-parents*/
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				eligibleAdvisors.add(candidate);
			}
		}
		
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor) {
				// 上一步已处理
				continue;
			}
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

	// advisor是否适合class --过渡类
	public static boolean canApply(Advisor advisor, Class<?> targetClass) {
		return canApply(advisor, targetClass, false);
	}
	// advisor是否适合class
	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			// 引介增强器判断是否合适(同切点判断方式不一样)
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor pca) {
			// 切点判断是否合适
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// 没有切点的话,假定是合适的 (why?)
			return true;
		}
	}
	// 用切点判断advisor是否适合class
	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		// 判断切点表达式是否匹配目标类
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		// 判断方法是否匹配
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		// 判断是否引介增强匹配 注1
		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		/* 对目标类和所有实现的接口类的方法进行匹配判断 */
		Set<Class<?>> classes = new LinkedHashSet<>();
		// 如果目标类不是代理类,加入待处理类
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		// 获取目标类实现的所有接口(包括由祖先类实现的)
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
		// 逐一对目标类或实现接口类的方法进行切点匹配判断
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

注1:切点匹配主要是通过表达式同类或方法进行匹配判断,不再进一步跟踪。相关类主要是AspectJExpressionPointcut,相关类图如下:
在这里插入图片描述

创建代理bean

AbstractAutoProxyCreator.createProxyClass(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource)

创建代理bean

	//过渡类
	private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {
		return (Class<?>) buildProxy(beanClass, beanName, specificInterceptors, targetSource, true);
	}

	//创建代理bean
	private Object buildProxy(Class<?> beanClass, @Nullable String beanName,			@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
		// 在ConfigurableListableBeanFactory中设置目标类为原始类
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
		
		// 创建代理工厂
		ProxyFactory proxyFactory = new ProxyFactory();
		// 获取当前类的ProxyConfig属性 (两者都祖先类都有ProxyConfig)
		proxyFactory.copyFrom(this);
		// 判断当前代理工厂(来自上一步的copy属性)是基于TargetClass代理还是基于接口代理
		if (proxyFactory.isProxyTargetClass()) {
			// 代理工厂是基于TargetClass代理
			if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
				// bean是处理类或lambdas类
				for (Class<?> ifc : beanClass.getInterfaces()) {
					// 把接口方法加入到代理工厂
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// 代理工厂是基于接口代理
			// 确定bean是目标类而不是接口
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// bean的接口加到代理工厂
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		// 封装所有Advisors
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		// 把Advisors加到代理工行
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// 如果bean类不是用重写类类加载器加载,就使用原始类加载器
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
		// 创建代理类返回
		return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
	}

ProxyFactory.getProxyClass(@Nullable ClassLoader classLoader)

	public Object getProxyClass(@Nullable ClassLoader classLoader) {
		// createAopProxy()创建AOP代理器,再由代理器生成代理类(getProxyClass(classLoader))
		return createAopProxy().getProxyClass(classLoader);
	}

	// createAopProxy()-创建AOP代理器的实现过程
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}
	// createAopProxy(this)-创建AOP代理器的实现过程 注1
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// optimize 默认false,为true表示可启用CGLIB动态代理器
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			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.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
				// 接口类、代理目标类或Lambda类用JDK的动态代理器
				return new JdkDynamicAopProxy(config);
			}
			// 用CGLIB动态代理器
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			// 接口类、代理目标类或Lambda类用JDK的动态代理器
			return new JdkDynamicAopProxy(config);
		}
	}

注1:JDK的动态代理器或CGLIB动态代理器的类图关系:
在这里插入图片描述

JDK动态代理器-JdkDynamicAopProxy生成代理类

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

	/* 下面方法来自:java.lang.reflect.Proxy (均是jdk本身的,同spring无关)*/
	/** 新生成指定接口的代理实例,该实例将方法调用分派给指定的调用处理程序
	@param loader 定义代理类的类加载器
	@param interfaces 要实现的代理类的接口列表
	@param h InvocationHandler是方法调用处理器,就是在invoke方法时进行代理封装
	*/
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
		// h 不能为空                                          
        Objects.requireNonNull(h);

		// 获取调用本方法的方法的调用类
        @SuppressWarnings("removal")
        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        //查找或生成指定的代理类的构造器
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

		// 构建代理类新实例并返回
        return newProxyInstance(caller, cons, h);
    }
    //查找或生成指定的代理类的构造器
    private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces)
    {
        // optimization for single interface
        // 只有一个接口需做代理
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
            	// 检查是否允许被代理
                checkProxyAccess(caller, loader, intf);
            }
            return proxyCache.sub(intf).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        } else {
        // 多个接口需做代理
            //克隆拿到接口
            final Class<?>[] intfsArray = interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }
            final List<Class<?>> intfs = Arrays.asList(intfsArray);
            return proxyCache.sub(intfs).computeIfAbsent(
                loader,
                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
            );
        }
    }   
    // 生成新实例 
    private static Object newProxyInstance(Class<?> caller, 
                                           Constructor<?> cons,
                                           InvocationHandler h) {
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (caller != null) {
                checkNewProxyPermission(caller, cons.getDeclaringClass());
            }
			// 用构建器生成新实例
            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);
            }
        }
    }

CGLIB代理器-CglibAopProxy生成代理类

	// 过渡类
	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		return buildProxy(classLoader, false);
	}
	
	private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
		}

		try {
			// 获取目标类
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
			// 上级类默认为自己本身
			Class<?> proxySuperClass = rootClass;
			// 类是CGLIB类(beanName字含有“$$“,注意同java嵌套类区别(含有"$");针对引入增强
			if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
				// 取上级类
				proxySuperClass = rootClass.getSuperclass();
				// 获取所有接口加入到advisor
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// Configure CGLIB Enhancer...
			// Enhancer是CGLIB封装的增强类,支持CGLIB功能处理
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader &&
						((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			// 设置增强类的上级类为代理目标类,增强类就代表了代理目标类
			enhancer.setSuperclass(proxySuperClass);
			//设置增强类的需封装的代理目标类接口
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setAttemptLoad(true);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

			//获取代理的回调方法集合
			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
	// 用enhancer创建目标类代理实例
	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null && this.constructorArgTypes != null ?
				enhancer.create(this.constructorArgTypes, this.constructorArgs) :
				enhancer.create());
	}

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

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

相关文章

三、SQLServer 数据库安装集

一、Docker 安装 Docker下安装SqlServer2019Docker 安装 SQLServer 1. 创建容器 前置准备 # 1. 创建主机映射目录 mkdir -p /root/sqlserver # 2. 修改主机映射目录权限 chown -R 10001:0 /root/sqlserver创建容器 # 1、拉取镜像。 #sudo docker pull mcr.microsoft.com/mssql/…

超越函数界限:探索JavaScript函数的无限可能

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4da; 前言 &#x1f4d8; 1. 函数的基本概念 &#x1f4df; 1.1 函数的定义和调用 &#x1f4df; 1.2 …

用加持了大模型的 Byzer-Notebook 做数据分析是什么体验

Byzer-Notebook 是专门为 SQL 而研发的一款 Web Notebook。他的第一公民是 SQL&#xff0c;而 Jupyter 则是是以 Python 为第一公民的。 随着 Byzer 引擎对大模型能力的支持日渐完善&#xff0c; Byzer-Notebook 也在不自觉中变得更加强大。我和小伙伴在聊天的过程中才发现他已…

TCP定制协议,序列化和反序列化

目录 前言 1.理解协议 2.网络版本计算器 2.1设计思路 2.2接口设计 2.3代码实现&#xff1a; 2.4编译测试 总结 前言 在之前的文章中&#xff0c;我们说TCP是面向字节流的&#xff0c;但是可能对于面向字节流这个概念&#xff0c;其实并不理解的&#xff0c;今天我们要介…

QT:绘图事件QPainter

绘图事件QPainter 绘图事件&#xff08;需要重写的函数&#xff09;&#xff1a;paintEvent 声明一个画家对象 QPainter painter(this) 指定绘图设备 画线&#xff0c;画圆&#xff0c;画矩形&#xff0c;画文字 可设置画笔&#xff0c;画刷#include <QPainter> ...... …

剑指 Offer 48. 最长不含重复字符的子字符串(C++实现)

剑指 Offer 48. 最长不含重复字符的子字符串https://leetcode.cn/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/ dp 注意&#xff1a;缩小 不含重复字符子串 时的写法 dp_1 min(i - charToIndex[s[i]], dp_0 1); int lengthOfLongestSubstring(string s…

Autosar MCAL-S32K324 CAN-FD配置及使用

文章目录 前言配置MCAL CANCAN Controller配置CAN FD波特率配置Ram block关于MailBox 代码中使用CAN FD报文发送和接收CAN FD报文接收CAN FD报文发送 总结 前言 在之前的文章中&#xff0c;介绍了标准CAN的MCAL配置&#xff0c;在此基础上&#xff0c;扩展为CAN-FD就会容易很多…

6.RocketMQ之消费索引文件ConsumeQueue

功能&#xff1a;作为CommitLog文件的索引文件。 本文着重分析为consumequeue/topic/queueId目录下的索引文件。 1.ConsumeQueueStore public class ConsumeQueueStore {protected final ConcurrentMap<String>, ConcurrentMap<Integer>, ConsumeQueueInterface…

NetSuite OIDC、SAML SSO 演示

NetSuite的SSO的策略近些年处于演进过程&#xff0c;所以原来的Inbound SSO和Outbound SSO已经退出历史舞台。前者已经废止&#xff0c;后者在24年底废止。目前的SSO策略是&#xff1a; 第三方的身份认证服务商NetSuite as OIDC Provider 前者的含义是&#xff0c;把认证服务…

数据结构 - 基本概念和术语

基础概念之间的关系大致如下&#xff1a; 一、数据、数据元素、数据项和数据对象 数据 > 数据对象 > 数据元素 > 数据项 类比数据库&#xff0c;这四个概念代表的含义如下所示&#xff1a; 数据&#xff1a;整个数据库的所有数据数据对象&#xff1a;这个数据库的…

Shell脚本五:函数和数组

文章目录 1.函数1.1Shell函数的概念1.2函数的好处1.2函数的组成1.3函数的结构1.4查看函数列表1.5删除函数1.6函数的返回值1.6.1使用原则1.6.2示例 1.7函数的作用范围1.8函数递归1.8.1示例 2.数组2.1什么是数组2.2数组的作用2.3数组名和索引2.4定义数组的方式2.5普通数组和关联数…

深入理解分布式架构,构建高效可靠系统的关键

深入探讨分布式架构的核心概念、优势、挑战以及构建过程中的关键考虑因素。 引言什么是分布式架构&#xff1f;分布式架构的重要性 分布式系统的核心概念节点和通信数据分区与复制一致性与一致性模型负载均衡与容错性 常见的分布式架构模式客户端-服务器架构微服务架构事件驱动…

对Lua的理解

在redis和nginx中都潜入了Lua环境用于快速上手开发。但如何理解Lua以及Lua与宿主环境的交互是需要掌握的。 首先是Lua本身&#xff0c;打开5.1的lua版本开始编译后最后生成一个lua的可执行文件&#xff0c;这其实就是一个包含了Lua虚拟机的终端.。所以其实在不管redis也好nginx…

2023/8/20周报

目录 摘要 论文阅读 1、标题和现存问题 2、准备知识 3、模型结构 4、实验准备 5、实验结果 深度学习 1、构建图数据 2、GCN模型 3、当前实验结果 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇时空图卷积网络:交通预测的深度学习框架的论文。文章的时空图卷积…

NOIP2014普及组,提高组 比例简化 飞扬的小鸟 答案

比例简化 说明 在社交媒体上&#xff0c;经常会看到针对某一个观点同意与否的民意调查以及结果。例如&#xff0c;对某一观点表示支持的有1498 人&#xff0c;反对的有 902人&#xff0c;那么赞同与反对的比例可以简单的记为1498:902。 不过&#xff0c;如果把调查结果就以这种…

Leetcode-每日一题【剑指 Offer 33. 二叉搜索树的后序遍历序列】

题目 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true&#xff0c;否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树&#xff1a; 5 / \ 2 6 / \ 1 3 示例 1&#xff1a; 输入: […

第4天----找出第一个只出现一次的字符(桶计数法/4种思路讲解)

题目描述 给定一个只包含小写字母的字符串&#xff0c;请你找到第一个仅出现一次的字符。如果没有&#xff0c;输出 no。 输入格式 一个字符串&#xff0c;长度小于 1100。 输出格式 输出第一个仅出现一次的字符&#xff0c;若没有则输出 no。 输入输出样例 输入 #1复制 abc 输…

鸿蒙/Android上最大的毒瘤:快应用服务

鸿蒙/Android上最大的毒瘤&#xff1a;快应用服务 2023.3.22版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、什么是快应用&#xff1f; “快应用” 是安卓厂&#xff08;华&#xff0c;米&#xff0c;O、V、魅族、努、联、加&#xf…

消息中间件的选择:RabbitMQ是一个明智的选择

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; MQ&#xff08;Message Queue&#xff09; MQ&#xff08;消息队列&#xff09;是一种用于在应用程序之间进行异步通信的技术&#xff1b;允许应用程序通过发送和接收…

[虚幻引擎] DTGlobalVariable 插件说明,蓝图全局变量访问,设置, Get, Set。

本插件可以在蓝图或者UMG中直接访问指定的全局变量值&#xff0c;方便编写。 支持Bool&#xff0c;Byte&#xff0c;Int&#xff0c;Int64&#xff0c;Float&#xff0c;Name&#xff0c;String&#xff0c;Text&#xff0c;Vector&#xff0c;Rotator&#xff0c;Transform&am…