当前位置: 首页 > news >正文

源码分析Spring @Configuration注解如何巧夺天空,偷梁换柱。

前言

回想起五年前的一次面试,面试官问@Configuration注解和@Component注解有什么区别?记得当时的回答是:

  • 相同点:@Configuration注解继承于@Component注解,都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean的配置信息。

  • 不同点:Component注解为通用组件类模式注解,Configuration注解为配置类模式注解,主要是在做代码分层上的有差别(当然也是从字面意思上理解)。

很显然不是面试官想要到答案,最后还是挂了。回去看了一下注解本身定,Configuration继承于Component,多了个proxyBeanMethods 属性,注释中提到在运行中可以生成子类进行增强,但是类类型必须不是final的,当proxyBeanMethods配置为false的时候不会进行增强。当时也就草率的下了定义,Configuration可以选择是否通过生成代理类进行增强。

进阶

多余的属性是proxyBeanMethods,字面的意思是代理Bean的方法,代理了个什么东西?是如何增强?带着问题写了一段测试代码:

@Component
public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext annotationConfigApplicationContext =new AnnotationConfigApplicationContext();annotationConfigApplicationContext.register(Demo.class);annotationConfigApplicationContext.refresh();Test test = annotationConfigApplicationContext.getBean(Test.class);System.out.println("spring ioc容器中管理的person对象:" + annotationConfigApplicationContext.getBean(Person.class));for (int i = 0; i < 2; i++) {System.out.println("通过bean作用的方法创建对象:" + test.createUser());}for (int i = 0; i < 2; i++) {System.out.println("通过没用bean作用的方法创建对象:" + test.createUserNoMethodBean());}annotationConfigApplicationContext.close();
}@Configurationpublic static class Test {@Beanpublic Person createUser() {return createUserNoMethodBean();}public Person createUserNoMethodBean(){Person person = new Person();person.setName(UUID.randomUUID().toString());return person;}}public static class Person {private String name;//get set 省略@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}}
}

代码输出

spring ioc容器中管理的person对象:Person{name='2a8c8844-c979-4784-942b-af4299d32866'}
通过bean作用的方法创建对象:Person{name='2a8c8844-c979-4784-942b-af4299d32866'}
通过bean作用的方法创建对象:Person{name='2a8c8844-c979-4784-942b-af4299d32866'}
通过没用bean作用的方法创建对象:Person{name='67707608-79bf-44c0-a83a-9ebaa7c17bb1'}
通过没用bean作用的方法创建对象:Person{name='27028430-9f82-4379-993a-6edd884ce145'}

我们调用Test对象创建用户方法,带有bean注解的返回的是同一个对象,并且与注入到spring ioc容器中的person对象是同一个,而且没有通过@Bean注解作用的方法真正执行了。不得不说好神奇。

猜想

我们先做一个大胆的猜想~ 注解Configuration注解proxyBeanMethods默认为true,也就是说默认会进行代理增强。调用通过bean注解的方法时会进行拦截,并且会舍弃调用真正的目标方法。其中会对带有bean的方法进行代理,对不带有bean的方法进行过滤。拦截带有bean方法返回对象时会从spring ioc容器中进行依赖查找并返回该对象。
如图流程图

  • 带有bean方法不需要进行执行目标方法,也就是我们的原始方法;
  • 我们需要注入我们的BeanFactory对象,来完成我们带有Bean方法依赖查找;
  • 需要对我们的方法进行过滤,需要对特定方法进行回调。
源码

由上一章我们熟悉了Spring ioc容器解析注册的流程,ConfigurationClassPostProcessor.class类比较重要,前半部分为BeanDefinitionRegistry逻辑,后半部为配置类的增强。
接下来开始查看对给定BeanFactoryPostProcessor的处理。跟踪源码到ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(beanfactory);

/*** Prepare the Configuration classes for servicing bean requests at runtime* by replacing them with CGLIB-enhanced subclasses.*///通过 CGLIB 增强的子类来代替配置类来为 bean 请求提供支持
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

继续跟踪到ConfigurationClassPostProcessor#enhanceConfigurationClasses(beanFactory)方法中,


public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);AnnotationMetadata annotationMetadata = null;MethodMetadata methodMetadata = null;if (beanDef instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;annotationMetadata = annotatedBeanDefinition.getMetadata();methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();}if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {// Configuration class (full or lite) or a configuration-derived @Bean method// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration// or component class without @Bean methods.AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;if (!abd.hasBeanClass()) {boolean liteConfigurationCandidateWithoutBeanMethods =(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));if (!liteConfigurationCandidateWithoutBeanMethods) {try {abd.resolveBeanClass(this.beanClassLoader);}catch (Throwable ex) {throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);}}}}//判断BeanDefinition的configurationClass是否为full,然后加入集合后续进行特殊处理if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {if (!(beanDef instanceof AbstractBeanDefinition)) {throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +beanName + "' since it is not stored in an AbstractBeanDefinition subclass");}else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {logger.info("Cannot enhance @Configuration bean definition '" + beanName +"' since its singleton instance has been created too early. The typical cause " +"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +"return type: Consider declaring such methods as 'static'.");}configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);}}if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {// nothing to enhance -> return immediatelyenhanceConfigClasses.end();return;}ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();//遍历进行cglib增强子类for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {AbstractBeanDefinition beanDef = entry.getValue();// If a @Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);// Set enhanced subclass of the user-specified bean classClass<?> configClass = beanDef.getBeanClass();Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);if (configClass != enhancedClass) {if (logger.isTraceEnabled()) {logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));}beanDef.setBeanClass(enhancedClass);}}enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();

ConfigurationClassEnhancer类中,我们看到增强的具体实现:


/*** Creates a new CGLIB {@link Enhancer} instance.*///创建cglib的实例。
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {Enhancer enhancer = new Enhancer();//为增强类设置父类enhancer.setSuperclass(configSuperClass);//为增强类设置接口,该接口继承BeanFactoryAware,在实例化非lazy对象处理中接口回调阶段initializeBean    进行(BeanNameAware  ClassLoaderAware BeanFactoryAware 回调),我们能通过BeanFactoryAware  获取我们beanfactory类。enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});enhancer.setUseFactory(false);//设置beanfactory字段。方便进行依赖查找enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));//具体我们可以看到此处配置的filter 也就是特殊方法才会执行回调,否则调用父类目标方法enhancer.setCallbackFilter(CALLBACK_FILTER);enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());return enhancer;
}

CALLBACK_FILTER:并不是所有方法进行拦截, 首先需要拦截的是内部调用 @Bean 注解的方法时,进行ioc依赖查找返回;其次是依赖查找依赖的beanfactory字段的赋值;其他方法不进行拦截(当然也可以拦截,直接在调用父类的方法proxy.invokeSuper(obj, args));这样多实现不如不实现,没用!!!。

因为我们多个拦截器,所以我们需要进行组合,选出符合条件的拦截器下标。

/*** A {@link CallbackFilter} that works by interrogating {@link Callback Callbacks} in the order* that they are defined via {@link ConditionalCallback}.*/private static class ConditionalCallbackFilter implements CallbackFilter {private final Callback[] callbacks;private final Class<?>[] callbackTypes;//初始化的数据为固定顺序public ConditionalCallbackFilter(Callback[] callbacks) {this.callbacks = callbacks;this.callbackTypes = new Class<?>[callbacks.length];for (int i = 0; i < callbacks.length; i++) {this.callbackTypes[i] = callbacks[i].getClass();}}@Overridepublic int accept(Method method) {//遍历callbacks,首先判断是否符合for (int i = 0; i < this.callbacks.length; i++) {Callback callback = this.callbacks[i];if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {return i;}}throw new IllegalStateException("No callback available for method " + method.getName());}public Class<?>[] getCallbackTypes() {return this.callbackTypes;}}// The callbacks to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS = new Callback[] {new BeanMethodInterceptor(),new BeanFactoryAwareMethodInterceptor(),NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

CALLBACKS 根据ConditionalCallbackFilter#accept(method)``方法逻辑,首先会判断是是继承了ConditionalCallback,然后调用isMatch(method)的方法;组合条件中 NoOp.INSTANCE非ConditionalCallback子类与BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor互斥;BeanMethodInterceptorBeanFactoryAwareMethodInterceptor`互斥。

BeanMethodInterceptor#isMatch(method);源码

@Override
public boolean isMatch(Method candidateMethod) {return (candidateMethod.getDeclaringClass() != Object.class &&!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}

接下来创建我们目标类的子类,注册子类的回调:


/**
*为派生子类的设置回调。
*/
private Class<?> createClass(Enhancer enhancer) {Class<?> subclass = enhancer.createClass();// Registering callbacks statically (as opposed to thread-local)// is critical for usage in an OSGi environment (SPR-5932)...//CALLBACKS为Enhancer.registerStaticCallbacks(subclass, CALLBACKS);return subclass;
}

CALLBACKS 为数组,

  • BeanMethodInterceptor:对于配置类中内部 @Bean 注解的方法的调用将会被拦截器拦截。拦截器的逻辑是判断声明的 Spring bean 在容器中是否已经存在,如果存在则直接返回容器中的 Spring bean。否则真正的配置类的方法创建 Spring bean 实例,避免了多例的出现。主要是解析获取我们beanfactory字段(该字段受益于CALLBACKS第二个元素的赋值),进行依赖查找。
private ConfigurableBeanFactory getBeanFactory(Object enhancedConfigInstance) {//解析BEAN_FACTORY_FIELD beanfactory字段~Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);Assert.state(field != null, "Unable to find generated bean factory field");Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);Assert.state(beanFactory != null, "BeanFactory has not been injected into @Configuration class");Assert.state(beanFactory instanceof ConfigurableBeanFactory,"Injected BeanFactory is not a ConfigurableBeanFactory");return (ConfigurableBeanFactory) beanFactory;
}
  • BeanFactoryAwareMethodInterceptor:为我们代理类新增的BEAN_FACTORY_FIELD字段进行赋值。源码如下:
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//查找我们的BEAN_FACTORY_FIELD字段Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);Assert.state(field != null, "Unable to find generated BeanFactory field");//为我们字段进行赋值,args[0]->因为我们BeanFactoryAware接口回调方法为 void setBeanFactory(BeanFactory beanFactory) throws BeansException;只有一个参数~field.set(obj, args[0]);// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?// If so, call its setBeanFactory() method. If not, just exit.//如果父类实现了BeanFactoryAware接口,改方法直接调用父类方法。if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {return proxy.invokeSuper(obj, args);}return null;
}
  • NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。
    配置中当然满足内部bean方法调用时走BeanMethodInterceptor;调用setBeanFactory(BeanFactory beanFactory)时走BeanFactoryAwareMethodInterceptor;其他方法走NoOp.INSTANCE。
    回到我们ConfigurationClassPostProcessor#enhanceConfigurationClasses(beanFactory)方法中,可以看到BeanDefinition的beanClass属性被赋值我们生成增强的代理子类,
    最后来到我们bean的实例化处理方法DefaultListableBeanFactory#preInstantiateSingletons。至此@Configuration的实现原理和我们猜想大致相同。

相关文章:

源码分析Spring @Configuration注解如何巧夺天空,偷梁换柱。

前言 回想起五年前的一次面试&#xff0c;面试官问Configuration注解和Component注解有什么区别&#xff1f;记得当时的回答是&#xff1a; 相同点&#xff1a;Configuration注解继承于Component注解&#xff0c;都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean…...

vector的使用及模拟实现

目录 一.vector的介绍及使用 1.vector的介绍 2.vector的使用 1.vector的定义 2.vector iterator的使用 3. vector 空间增长问题 4.vector 增删查改 3.vector 迭代器失效问题&#xff08;重点&#xff09; 1. 会引起其底层空间改变的操作 2.指定位置元素的删除操作--erase 3. Li…...

“华为杯”研究生数学建模竞赛2007年-【华为杯】A题:基于自助法和核密度估计的膳食暴露评估模型(附获奖论文)

赛题描述 我国是一个拥有13亿人口的发展中国家,每天都在消费大量的各种食品,这批食品是由成千上万的食品加工厂、不可计数的小作坊、几亿农民生产出来的,并且经过较多的中间环节和长途运输后才为广大群众所消费,加之近年来我国经济发展迅速而环境治理没有能够完全跟上,以…...

刷题(第三周)

目录 [CISCN2021 Quals]upload [羊城杯 2020]EasySer [网鼎杯 2020 青龙组]notes [SWPU2019]Web4 [Black Watch 入群题]Web [HFCTF2020]BabyUpload [CISCN2021 Quals]upload 打开界面以后&#xff0c;发现直接给出了源码 <?php if (!isset($_GET["ctf"]))…...

新C++(14):移动语义与右值引用

当你在学习语言的时候&#xff0c;是否经常听到过一种说法,""左边的叫做左值&#xff0c;""右边的叫做右值。这句话对吗&#xff1f;从某种意义上来说&#xff0c;这句话只是说对了一部分。---前言一、什么是左右值?通常认为:左值是一个表示数据的表达式(…...

TCP相关概念

目录 一.滑动窗口 1.1概念 1.2滑动窗口存在的意义 1.3 滑动窗口的大小变化 1.4丢包问题 二.拥塞控制 三.延迟应答 四.捎带应答 五.面向字节流 六.粘包问题 七.TIME_WAIT状态 八.listen第2个参数 九.TCP总结 一.滑动窗口 1.1概念 概念&#xff1a;双方在进行通信时&a…...

MySQL锁篇

MySQL锁篇 一、一条update语句 我们的故事继续发展&#xff0c;我们还是使用t这个表&#xff1a; CREATE TABLE t (id INT PRIMARY KEY,c VARCHAR(100) ) EngineInnoDB CHARSETutf8;现在表里的数据就是这样的&#xff1a; mysql> SELECT * FROM t; —------- | id | c | —…...

SWF (Simple Workflow Service)简介

Amazon Simple Workflow Service (Amazon SWF) 提供了给应用程序异步、分布式处理的流程工具。 SWF可以用在媒体处理、网站应用程序后端、商业流程、数据分析和一系列定义好的任务上。 举个例子&#xff0c;下图表明了一个电商网站的工作流程&#xff0c;其中涉及了程序执行的…...

java(Class 常用方法 获取Class对象六种方式 动态和静态加载 类加载流程)

ClassClass常用方法获取Class对象六种方式哪些类型有Class对象动态和静态加载类加载流程加载阶段连接阶段连接阶段-验证连接阶段-准备连接阶段-解析初始化阶段获取类结构信息Class常用方法 第一步&#xff1a;创建一个实体类 public class Car {public String brand "宝…...

【数据结构】线性表和顺序表

Yan-英杰的主页 悟已往之不谏 知来者之可追 目录 1.线性表 2.顺序表 2.1 静态顺序表 2.2 动态顺序表 2.3移除元素 1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线…...

Ubuntu数据库安装(mysql)

##1.下载mysql-apt-config_0.8.22-1_all.deb并且安装 wget https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb##2.更新apt-updata sudo apt update##3.如果出现如下图情况执行以下命令 [外链图片转存失败,源站可…...

MyBatis-Plus的入门学习

MyBatis-Plus入门学习简介特性快速开始MyBatis-Plus的注解详解Tableld主键生成策略1、数据库自动增长 AUTO2、UUID3、Redis生成id4、MP主键自动生成TableNameTableField自动填充测试方法&#xff1a;update乐观锁select查所有根据id查多个id批量查询简单条件查询&#xff08;通…...

华为OD机试题 - 内存池(JavaScript)

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:内存池题目输入输出示例一输入输出说明Code解题思路版权说明华为…...

数据库索引原理

数据库索引的作用是做数据的快速检索&#xff0c;而快速检索实现的本质是数据结构。像二叉树、红黑树、AVL树、B树、B树、哈希等数据结构都可以实现索引&#xff0c;但其中B树效率最高。MySQL数据库索引使用的是B树。二叉树&#xff1a;二叉树中&#xff0c;左子树比根节点小&a…...

字符函数和字符串函数详解(1)

目录前言strlen函数strlensizeofstrcpy函数strcat函数strcmp函数总结前言 最近要调整状态&#xff0c;写的文章质量不佳让大家失望&#xff0c;我现在也在反思我在做什么&#xff0c;我会什么&#xff0c;我学了什么。等我想明白的那天&#xff0c;我一定能跟大家顶峰相见的&a…...

【数据分析:工具篇】NumPy(1)NumPy介绍

【数据分析&#xff1a;工具篇】NumPy&#xff08;1&#xff09;NumPy介绍NumPy介绍NumPy的特点数组的基本操作创建数组索引和切片数组运算NumPy介绍 NumPy&#xff08;Numerical Python&#xff09;是Python的一个开源的科学计算库&#xff0c;它主要用于处理大规模的多维数组…...

mysql时区问题

设置mysql容器时间与服务器时间一致 问题背景&#xff1a; 今天测试发现一个问题&#xff0c;时间不一致&#xff0c;当工单入库时&#xff0c;其创建时间和更新时间应该是一样的&#xff0c;即使不一样最多只会错几秒的时间&#xff1b;实际上两个时间相差的大概8小时&#…...

磨金石教育摄影技能干货分享|高邮湖上观花海

江苏高邮&#xff0c;说到这里所有人能想到的&#xff0c;就是那烟波浩渺的高邮湖。高邮在旅游方面并不出名&#xff0c;但是这里的自然人文景观绝对不输于其他地方。高邮不止有浩瀚的湖泊&#xff0c;春天的油菜花海同样壮观。春日的午后&#xff0c;与家人相约游玩&#xff0…...

mysql navicat忘记密码

mysql忘记密码是常用的事情&#xff0c;那么如何解决它呢&#xff1f;1、首先将MySQL的服务关闭&#xff0c;两种方法&#xff1a;&#xff08;1&#xff09;打开命令行cmd输入net stop mysql命令即可关闭MySQL服务。&#xff08;2&#xff09;打开任务管理器&#xff0c;找到服…...

Git的下载、安装、配置、使用、卸载

前言 我是跟着狂神老师学的。该博客仅用于笔记所用。 下面是老师的B站和笔记 B站&#xff1a;https://www.bilibili.com/video/BV1FE411P7B3?p1&vd_source9266cf72b1f398b63abe0aefe358d7d6 笔记&#xff1a;https://mp.weixin.qq.com/s/Bf7uVhGiu47uOELjmC5uXQ 一、准备工…...

【博客631】监控网卡与进程网络IO使用情况

监控进程的网络IO使用情况 1、vnstat 由于 vnstat 依赖于内核提供的信息&#xff0c;因此执行以下命令来验证内核是否提供了 vnStat 所期望的所有信息&#xff1a; # vnstat --testkernel This test will take about 60 seconds. Everything is ok.不带任何参数的 vnstat 将…...

【Leetcode】【简单】35. 搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2 示例 2: 输入:…...

sql面试题

mysql优化 优化准则&#xff1a; 建表时&#xff1a;合理选择字段的类型&#xff0c;单表字段数量 sql查询尽量单表操作&#xff0c;避免复杂操作&#xff0c;复杂的多表通过java代码实现 构建复合索引优化&#xff0c;索引尽量可以覆盖主要业务查询 sql避免索引失效 避免大…...

SQL 进阶刷题笔记

SQL 进阶刷题笔记 一、MySQL 进阶 这里主要是 MySQL 刷题相关笔记&#xff0c;方便后面温习和查阅&#xff0c;希望可以帮到大家&#xff01;&#xff01;&#xff01; 题1 请计算每张SQL类别试卷发布后&#xff0c;当天5级以上的用户作答的人数uv和平均分avg_score&#xff0…...

[网鼎杯 2020 朱雀组]Think Java

SqlDict.java ,其中sql语句处存在sql注入漏洞 package .sqldict;import cn.abc.core.sqldict.Row; import cn.abc.core.sqldict.Table; import java...

AIR32F103(十) 在无系统环境和FreeRTOS环境集成LVGL

目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告AIR32F103(二) Linux环境和LibOpenCM3项目模板AIR32F103(三) Linux环境基于标准外设库的项目模板AIR32F103(四) 27倍频216MHz,CoreMark跑分测试AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码AIR32F103(六) ADC,I2S…...

SpringBoot接口 - 如何统一异常处理

SpringBoot接口如何对异常进行统一封装&#xff0c;并统一返回呢&#xff1f;以上文的参数校验为例&#xff0c;如何优雅的将参数校验的错误信息统一处理并封装返回呢&#xff1f;为什么要优雅的处理异常如果我们不统一的处理异常&#xff0c;经常会在controller层有大量的异常…...

如何使用Python进行数据可视化

数据可视化是一种将数据呈现为图形或图表的技术&#xff0c;它有助于理解和发现数据中的模式和趋势。Python是一种流行的编程语言&#xff0c;有很多库可以帮助我们进行数据可视化。在本文中&#xff0c;我们将介绍使用Python进行数据可视化的基本步骤。 第一步&#xff1a;导…...

vue -- 自定义指令钩子函数补充 自定义过滤器filter参数

自定义指令补充 自定义指令通过钩子函数的形式来实现自定义的功能 这里是几个常用的钩子函数以及它的方法&#xff1a; bind&#xff1a;只调用一次&#xff0c;指令第一次绑定到元素时调用&#xff0c;在这里可以进行一次性的初始化设置。 inserted&#xff1a;被绑定元素插…...

Qt不会操作?Qt原理不知道? | Qt详细讲解

文章目录Qt界面开发必备知识UI界面与控件类型介绍Qt设计器原理控件类型的介绍信号与槽机制处理常用控件创建与设置常见展示型控件创建与设置常见动作型控件创建与设置常见输入型控件创建与设置常见列表控件创建于设置Qt中对象树的介绍项目源码结构刨析.pro.hmain.cpp.cppQt界面…...

成都哪家做网站公司好/如何网上免费打广告

1、Ajax基本步骤&#xff1a; 生成xmlHttpRequest对象 xmlHttp new ActiveXObject("Microsoft.XMLHTTP"); 以上是简单写法&#xff0c;实际应用中要根据浏览器进行扩展 设置异步方式 xmlHttp.open("POST",url,true); 或 xmlHttp.open("GET",ur…...

wordpress积分插件中文免费下载/seo教学培训

以我自己的职业经历来说&#xff0c;我情愿自己去针对一项重现写代码&#xff0c;也不愿意去承接别人的代码。我想&#xff0c;这是大部分码农的心声吧。这其中的原因有很多种&#xff0c;但让人糟心的代码的原因排第一位的肯定是追溯其的逻辑特别的困难&#xff0c;那排第二绝…...

app网页设计网站/长尾关键词爱站

python数据类型数字 字符串 列表 元祖 字典1.为什么要编程 编程的目的是解放人力&#xff0c;这就需要人通过编写程序的方式计算机代替人去自动干活 2.什么是编程语言 编程语言就是人与计算机之间沟通的介质 3.编程语言的分类&#xff1a; 机器语言--》汇编语言--》高级语言 1.…...

网站网站建设设计/互联网营销有哪些方式

IDEA自定义代码模板&#xff0c;让开发更快更快乐IDEA中有个Live Template选项&#xff0c;就是用来自定义代码模板&#xff0c;来提高编码效率。1、创建模板&#xff0c;并做基本的变量配置&#xff0c;例如&#xff1a;Service() $INTER$Impl $INTER${Logger log Logger.get…...

国外用什么做网站/长尾词挖掘免费工具

HTML &#xff08;HyperText Makeup Language&#xff09;是超文本标记语言。 1.HTML结构 <html> <head> <title>标题</title> </head> <body> 主体部分 </body> </html> 另&#xff1a;HTML注释&#xff1a;<!--html--&g…...

建设银行官方网站诚聘英才频道/越秀seo搜索引擎优化

前言在我们访问网站的过程中&#xff0c;有时候会遇到您的连接不是私密连接。正常操作是点击高级&#xff0c;然后我们在继续访问&#xff0c;那么在selenium中&#xff0c;我们需要一步步定位&#xff1f;过程比较繁琐&#xff0c;最主要的是&#xff0c;有时候只有第一次才会…...