第一讲:BeanFactory和ApplicationContext

BeanFactory和ApplicationContext

什么是BeanFactory

  • 它是ApplicationContext的父接口
  • 它才是Spring的核心容器,主要的ApplicationContext实现都组合了它的功能

image-20231205165356277

BeanFactory能做什么?

表面上看BeanFactory的主要方法只有getBean(),实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能,都由他的实现类提供

例如:通过反射查看它的成员变量singletonObjectssingletonObjects内部包含了所有单例bean

实现这个例子前,引出一个类DefaultListableBeanFactory,主要功能是作为一个可配置的、可列表化的 Bean 工厂,用于管理和维护应用程序中的 Bean 定义和实例

DefaultListableBeanFactory继承了DefaultSingletonBeanRegistry类,而DefaultSingletonBeanRegistry类就是专门用来管理单例Bean的

image-20231205165954183

DefaultSingletonBeanRegistry其中有个成员变量也就是singletonObjects

image-20231205170156677

这样我们就找到了单例Bean存储的地方,通过反射调用即可

SpringBoot启动会有很多单例Bean注入,map中包含了所有的单例Bean,我自定义了Component1类注入到其中

@SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {//返回容器对象ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//反射获取singletonObjects属性Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");//开启可操作singletonObjects,否则无法对他进行操作,因为他是private final的singletonObjects.setAccessible(true);//BeanFactory是ConfigurableListableBeanFactory实现类的成员变量ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//Field.get(Object obj):获取指定对象obj上此field表示的字段的值Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);map.entrySet().stream().filter(e -> e.getKey().startsWith("component")).forEach(e -> {System.out.println(e.getKey() + "=" + e.getValue());});}
}

上面代码最难理解的是singletonObjects.get(beanFactory),刚开始没有理解到反射,重新翻了反射的代码发现他的作用是填充ConfigurableListableBeanFactory中的singletonObjects属性

Java源码中并没有这个属性,反复查找后发现DefaultListableBeanFactoryConfigurableListableBeanFactory的实现类,而DefaultListableBeanFactory继承了DefaultSingletonBeanRegistry,所以自然有singletonObjects属性

ApplicationContext能做什么

前面提到过,ApplicationContext是在BeanFactory基础上做了扩展

  • MessageSource:拓展国际化功能
  • ApplicationEventPublisher:事件发布与监听,实现组件之间的解耦
  • ResourcePatternResolver:通过通配符方式获取一组Resource资源
  • EnvironmentCapable:整合 Environment 环境(能通过它获取各种来源的配置信息)

image-20231205165356277

国际化

所谓国际化就是可以通过准备好的配置文件,将语言翻译成各种国家的语言

#准备以下2个配置文件
#messages_en.properties  翻译成英文
hi=hello#messages_zh.properties  翻译成中文  \u4F60\u597D对应着你好
hi=\u4F60\u597D

通过getMessage()转化

@SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//国际化System.out.println(context.getMessage("hi", null, Locale.US)); //helloSystem.out.println(context.getMessage("hi", null, Locale.CHINA)); //你好}
}

获取Resource资源

@SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//通配符方式获取一组 Resource 资源Resource[] resources = context.getResources("classpath:application.properties");for (Resource resource : resources) {System.out.println(resource); //class path resource [application.properties]}}
}

整合 Environment 环境

可以读取系统环境变量,也可以读取SpringBoot配置文件

@SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//整合 Environment 环境System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));System.out.println(context.getEnvironment().getProperty("server.port"));}
}

事件发布与监听

事件发布与监听指的是,一条线程发布特定事件,监听该事件的方法收到消息后,处理该方法,这也是异步的一种思想

创建事件

事件需要继承ApplicationEvent

public class UserRegisteredEvent extends ApplicationEvent {public UserRegisteredEvent(Object source) {super(source);}
}

监听者

@EventListener 可以在 Spring 应用中监听和响应特定类型的事件。当事件发生时,标记有 @EventListener 注解的方法将被自动调用。

@Component
public class Component1 {private static final Logger logger = LoggerFactory.getLogger(Component1.class);@EventListenerpublic void aaa(UserRegisteredEvent event) {logger.info("{}",event);}
}

事件发布

@SpringBootApplication
public class Application {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);//发布事件context.publishEvent(new UserRegisteredEvent(context));}
}

BeanFactory实现

代码准备

我们最终想要实现的是,像Spring一样,向自定义的BeanFactory注册Config对象

@Configuration
static class Config{@Beanpublic Bean1 bean1(){return new Bean1();}@Beanpublic Bean2 bean2(){return new Bean2();}
}static class Bean1{private static final Logger logger = LoggerFactory.getLogger(Bean1.class);@Autowiredprivate Bean2 bean2;public Bean1() {logger.info("构造 Bean1()");}public Bean2 getBean2(){return bean2;}
}static class Bean2{private static final Logger logger = LoggerFactory.getLogger(Bean2.class);public Bean2() {logger.info("构造 Bean2()");}
}

具体实现

BeanFactory可以通过registerBeanDefinition注册一个BeanDefinition 对象

public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//定义一个bean (class,scope,初始化,销毁) scope:单例还是多例AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();beanFactory.registerBeanDefinition("config",beanDefinition);//查看BeanFactory中的beanfor (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}
}

执行以上代码,控制台输出config,证明Config已经注册到BeanFactory

但是发现Bean1Bean2并没有注册进去,因为BeanFactory并不会主动调用BeanFactory的后置处理器

现在我们需要为BeanFactory添加一些常用的后置处理器,注意这里仅仅是添加

AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

通过观察控制台输出,发现多了一些BeanFactory中多了些Bean

image-20231207113233914

简单介绍一下目前需要了解几个关键的处理器

  • internalConfigurationAnnotationProcessor:用于处理@Configuration注解,它会解析@Configuration注解并处理其中的@Bean注解,将被注解的方法返回的对象注册为Bean。
  • internalAutowiredAnnotationProcessor:用于处理@Autowired注解,它会解析@Autowired注解并自动装配依赖对象。它会在Bean初始化之前进行处理,确保依赖对象正确注入。
  • internalCommonAnnotationProcessor:用于处理通用注解,如@Resource@PostConstruct@PreDestroy等。它会在Bean初始化之前进行处理,执行相应的初始化和销毁方法。

接下来需要让BeanFactory处理器生效,其中internalConfigurationAnnotationProcessor属于BeanFactory后置处理器,internalAutowiredAnnotationProcessorinternalCommonAnnotationProcessor属于Bean的后置处理器,生效方式不一样

//BeanFactory后置处理器的主要功能,补充了一些bean的定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});//bean后置处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowird,@Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {beanFactory.addBeanPostProcessor(beanPostProcessor);
});

观察控制台,发现Bean1Bean2也被出册到了BeanFactory中,并且可以实例化

image-20231207115043414

目前到这看似我们的目标已经完成了,实际上会发现Bean的构造方法是在用到的时候调用的,属于懒汉式,对于单例Bean来说,希望他在注册的时候就已经实例化

//在容器初始化完成后,提前实例化所有的单例Bean
beanFactory.preInstantiateSingletons();

观察控制台发现,构造方法在容器初始化的时候就已经执行了

image-20231207132430162

总结

  • BeanFactory不会主动调用BeanFactory后置处理器
  • BeanFactory不会主动调用Bean后置处理器
  • BeanFactory不会主动初始化单例

补充说明

Bean的后置处理器会有排序的逻辑

举个例子,当一个接口有两个实现类时,这个接口通过依赖注入的形式注入,同时标注了@Autowired以及@Resource,哪个注解会生效,取决于Bean的哪个后置处理器先添加到BeanFactory

ApplicationContext实现

这里我们主要了解几个常用的ApplicationContext实现

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext
  • AnnotationConfigApplicationContext
  • AnnotationConfigServletWebServerApplicationContext

基于XML配置文件注册Bean

目前无论是Spring还是SpringBoot都很少用XML的方式,更推荐使用配置类来注册Bean,所以这里了解即可

前置代码

需要注册的Bean

static class Bean1{
}static class Bean2{private Bean1 bean1;public Bean1 getBean1() {return bean1;}public void setBean1(Bean1 bean1) {this.bean1 = bean1;}
}

读取的XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bean1" class="com.yellowstar.spring.demo1.TestApplicationContext$Bean1"/><bean id="bean2" class="com.yellowstar.spring.demo1.TestApplicationContext$Bean2"><property name="bean1" ref="bean1"/></bean>
</beans>
ClassPathXmlApplicationContext

ClassPathXmlApplicationContext的作用是基于classpath下 xml 格式的配置文件来注册Bean

private static void testClassPathXmlApplicationContext(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(context.getBean(Bean2.class).getBean1());
}

image-20231207152154004

FileSystemXmlApplicationContext

FileSystemXmlApplicationContext的作用是基于磁盘路径下 xml 格式的配置文件来创建

    private static void testFileSystemXmlApplicationContext(){//可以选择绝对路径,也可以是相对路径FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src/main/resources/b01.xml");}
实现ClassPathXmlApplicationContext

XmlBeanDefinitionReader用于从XML配置文件中读取和解析Bean的定义信息

   DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//设置配置文件读取到beanFactoryXmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("b01.xml"));

基于Java配置类注册Bean

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext的作用是基于Java配置类来注册Bean

配置类

@Configuration
static class Config{@Beanpublic Bean1 bean1(){return new Bean1();}@Beanpublic Bean2 bean2(Bean1 bean1){Bean2 bean2 = new Bean2();bean2.setBean1(bean1);return bean2;}
}

实现

private static void testAnnotationConfigApplicationContext(){AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(">==" + context.getBean(Bean2.class).getBean1());
}

这里可以注意到常用的一些后置处理器是自动帮我们加载到容器中了,而基于XML模式的方式还需要在XML另外定义才行

image-20231207153311158

AnnotationConfigServletWebServerApplicationContext

AnnotationConfigServletWebServerApplicationContext也是是基于Java配置类来注册Bean,区别不同的在于他适用于web环境

这块内容比较复杂,先贴代码在详细解释

    private static void testAnnotationConfigServletWebServerApplicationContext(){AnnotationConfigServletWebServerApplicationContext context =new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);}@Configurationstatic class WebConfig{@Beanpublic ServletWebServerFactory servletWebServerFactory(){return new TomcatServletWebServerFactory();}@Beanpublic DispatcherServlet dispatcherServlet(){return new DispatcherServlet();}@Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){// "/" 代表匹配所有请求return new DispatcherServletRegistrationBean(dispatcherServlet,"/");}@Bean("/")public Controller controller1(){return (request,response) -> {response.getWriter().print("hello");return null;};}}

上述代码的功能就像是启动了Spring服务一样,可以通过访问localhost:8080来进入controller1

image-20231207160024704

其中最重要的是WebConfig必须拥有三大组件

  • ServletWebServer:用于支持 Servlet 容器的接口。它定义了与 Servlet 容器相关的通用操作和属性,允许 Spring 应用程序与不同的 Servlet 容器(如Tomcat、Jetty等)进行交互。
  • DispatcherServlet:用于处理web请求的关键组件之一,所有请求都会经过他
  • DispatcherServletRegistrationBean:用于注册和配置 DispatcherServlet 的 Bean。它允许开发者以编程方式配置 DispatcherServlet 的各种属性,以及将其与特定的 URL 映射关联起来。

Bean的生命周期

通常情况下,Bean的生命周期分为四个阶段

  1. 构造方法阶段:在这个阶段,Spring容器实例化Bean对象,调用其构造方法来创建Bean的实例
  2. 依赖注入阶段:在Bean对象被实例化完成后,Spring容器会对Bean的属性进行依赖注入,将依赖的其他 Bean或值注入到当前 Bean中。
  3. 初始化阶段:在依赖注入完成后,Spring容器会调用Bean的初始化方法,例如使用@PostConstruct注解
  4. 销毁阶段:当容器关闭或销毁时,Spring容器会调用Bean的销毁方法,例如使用@PreDestory注解

这样讲讲方法太枯燥了,我一开始也记不住,通过下面例子可以加深印象

@SpringBootApplication
public class A03Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);context.close();}
}@Component
public class LifeCycleBean {private static final Logger logger = LoggerFactory.getLogger(LifeCycleBean.class);public LifeCycleBean() {logger.debug("构造");}@Autowiredpublic void autowire(@Value("${JAVA_HOME}") String home){logger.debug("依赖注入:{}",home);}@PostConstructpublic void init(){logger.debug("初始化");}@PreDestroypublic void destory(){logger.debug("销毁");}
}

执行代码可以观察到顺序依次为构造->依赖注入->初始化->销毁

image-20231208113234518

Bean扩展

Spring的扩展程度是非常高的,可以在Bean的各个生命周期中实现自己想要的结果

@Component
public class MyBeanPostProcessor implements DestructionAwareBeanPostProcessor, InstantiationAwareBeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");}return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");}return bean;}@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");}}
}

模版方法

模板方法是一种常用的设计模式,在之前对BeanFactory添加后处理器时,比较好奇他这个处理器是如何加载进去的,现在使用模板方法可以试着自己动手实现

先来看一下基础代码,MyBeanFactory是自定义的一个BeanFactory,其中getBean()可以获取Bean

我们的想法是,在getBean()时,添加后处理器,来解析@Autowired或者@Resource

    public static void main(String[] args) {MyBeanFactory beanFactory = new MyBeanFactory();beanFactory.getBean();}static class MyBeanFactory{public Object getBean(){Object bean = new Object();//依赖注入解析return bean;}}

方案一

比较简单的方法就是直接在getBean()中实现,但是这样并不利于扩展,假如需要后续解析其他注解,那么就得修改getBean()方法

方案二

参照之前DefaultListableBeanFactory的做法,我们并没有修改内部方法,而是调用DefaultListableBeanFactory其他方法来添加后处理器

通过一个集合来加载所有后处理器,这样在需要添加后处理器的时候就可以在外部直接调用addBeanPostProcessor()方法,不需要动到MyBeanFactory这个最底层的类

    public static void main(String[] args) {MyBeanFactory beanFactory = new MyBeanFactory();beanFactory.addBeanPostProcessor(() -> System.out.println("解析 @Autowired"));beanFactory.addBeanPostProcessor(() -> System.out.println("解析 @Resource"));beanFactory.getBean();}static class MyBeanFactory{public Object getBean(){Object bean = new Object();//执行后置处理器for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {beanPostProcessor.inject();}return bean;}private static List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){beanPostProcessorList.add(beanPostProcessor);}}interface BeanPostProcessor{/*** 依赖注入阶段扩展*/void inject();}

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

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

相关文章

路由器IP地址及直连实验

实验目的&#xff1a; &#xff08;1&#xff09;理解IP地址&#xff1b; &#xff08;2&#xff09;掌握路由器端口IP地址的配置方法&#xff1b; &#xff08;3&#xff09;理解路由器的直连网络。 实验器材&#xff1a; Cisco packet 实验内容&#xff1a; 实验步骤&a…

【ArcGIS微课1000例】0083:地震灾害图件制作之土壤类型分布图

本文基于1:400万矢量土壤图,制作甘肃积石山6.2级地震100km范围内土壤类型分布图。 文章目录 一、土壤分布图预览二、数据集来源及简介三、土壤分布图制作一、土壤分布图预览 二、数据集来源及简介 1. 数据来源 数据集为1:400万中国土壤图,1:400万中国土壤图(2000)由中国科…

大创项目推荐 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

模式识别与机器学习-集成学习

集成学习 集成学习思想过拟合与欠拟合判断方法 K折交叉验证BootstrapBagging随机森林的特点和工作原理&#xff1a; BoostingAdaBoost工作原理&#xff1a;AdaBoost的特点和优点&#xff1a;AdaBoost的缺点&#xff1a; Gradient Boosting工作原理&#xff1a;Gradient Boostin…

android 13.0 Launcher3长按app弹窗设置为圆角背景功能实现一

1.前言 在13.0的系统ROM定制化开发中,在进行一些Launcher3的定制化开发中,在使用app的弹窗的功能时,会弹出应用信息和微件之类的内容,所以在定制需求中,需要默认设置为圆角背景,接下来就来分析下相关功能的实现 如图: 2.Launcher3长按app弹窗设置为圆角背景功能实现的核…

【MYSQL】-函数

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

AcWing 1076. 迷宫问题(最短路模型)

题目链接 活动 - AcWing本课程系统讲解常用算法与数据结构的应用方式与技巧。https://www.acwing.com/problem/content/description/1078/ 来源 《信息学奥赛一本通》, kuangbin专题 , POJ3984 代码 #include <cstring> #include <iostream> #include <alg…

【网络安全常用术语解读】SCAP详解

本文主要介绍什么是SCAP&#xff0c;SCAP的产生背景是怎样的&#xff0c;SCAP有什么用途&#xff0c;有哪些组件&#xff0c;各个组件的用途是什么&#xff1f; SCAP产生背景 由于计算机和网络技术的快速发展&#xff0c;越来越多的软件和系统被应用到企业和机构中&#xff0c…

迭代归并:归并排序非递归实现解析

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《数据结构&算法》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 归并排序的思想上我们已经全部介绍完了&#xff0c;但是同时也面临和快速排序一样的问题那就是递…

通信原理课设(gec6818) 007:语音识别

目录 1、去科大讯飞官网下载对应的sdk 2、科大讯飞文件夹的意思 3、配置ARM的录音环境 4、编程实现语音识别 我们的需求是将一个语音文件从客户端传到服务器&#xff0c;因此我们最好是选用tcp 现在市面上面常用的语音识别解决方案为&#xff1a;科大讯飞c和百度c 离…

龙芯3A5000上安装使用QQ

原文链接&#xff1a;龙芯3A5000上安装使用QQ hello&#xff0c;大家好啊&#xff01;今天我要给大家带来的是在龙芯3A5000处理器上安装使用QQ的文章。近期&#xff0c;腾讯发布了最新版本的QQ&#xff0c;值得一提的是&#xff0c;这一版本增加了对Linux系统下龙芯架构的支持。…

KG+LLM(一)KnowGPT: Black-Box Knowledge Injection for Large Language Models

论文链接&#xff1a;2023.12-https://arxiv.org/pdf/2312.06185.pdf 1.Background & Motivation 目前生成式的语言模型&#xff0c;如ChatGPT等在通用领域获得了巨大的成功&#xff0c;但在专业领域&#xff0c;由于缺乏相关事实性知识&#xff0c;LLM往往会产生不准确的…

项目记录:利用Redis实现缓存以提升查询效率

一、概述 当我们查询所有数据时&#xff0c;如果缓存中没有&#xff0c;则去数据库查询&#xff0c;如果有&#xff0c;直接查缓存的数据就行。注意定期更新缓存数据。 二、主体代码 private static final String ROOM_SCHEDULES_HASH "RoomSchedules";Overridepu…

HTML---JavaScript基础

文章目录 目录 文章目录 本章目标 一.JavaScript基础 概述 特点 JavaScript 基本机构 语法 网页中引用JavaScript的方式 二. JavaScript核心语法 变量 ​编辑 数据类型 数组 练习 本章目标 掌握JavaScript的组成掌握JavaScript的基本语法会定义和使用函数会使用工具进行…

引领手游技术潮流:武汉灰京文化的卓越技术创新与市场推广支持

在数字娱乐领域&#xff0c;手游行业正蓬勃发展&#xff0c;为数以亿计的玩家提供了丰富的娱乐选择。武汉灰京文化&#xff0c;作为该领域的佼佼者&#xff0c;以其强大的技术创新和全面的市场推广支持&#xff0c;为合作伙伴的成功铺平了道路&#xff0c;不仅提升了游戏质量&a…

TV端Web页面性能优化实践

01 背景 随着互联网技术的持续创新和电视行业的高速发展&#xff0c;通过电视观看在线视频已经逐渐成为大众的重要娱乐方式。奇异果App作为在TV设备上用户活跃度最高的应用之一&#xff0c;为广大用户提供了丰富的内容播放服务&#xff0c;除此之外&#xff0c;同样有会员运营、…

【Kubernetes】什么是 kubectl ?

什么是 kubectl &#xff1f; 1.什么是 kubectl &#xff1f;2.Kubernetes 内部结构3.Kubernetes API 的作用 1.什么是 kubectl &#xff1f; 在学习如何更有效地使用 kubectl 之前&#xff0c;您应该对它是什么以及它如何工作有一个基本的了解。从用户的角度来看&#xff0c;…

Javaweb-servlet

一、servlet入门 1.Servlet介绍 (1)什么是Servlet Servlet是Server Applet的简称&#xff0c;是用Java编写的是运行在 Web 服务器上的程序&#xff0c;它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet&#…

CGAL的三角网格曲面脊线和脐点的近似计算(需要微分几何学的知识)

脊线&#xff08;Ridges&#xff09;&#xff1a;在光滑曲面上&#xff0c;脊线是一种特殊的曲线。沿着这条曲线&#xff0c;曲面的一个主曲率在其曲率线上达到极值&#xff08;最大或最小&#xff09;。这意味着脊线是那些曲率发生突变的区域&#xff0c;它们在形状感知、物体…

Android Studio下载gradle失败

1、打开Android Studio设置Gradle的地方&#xff0c;点击左上角的File->Settings查看gradle存放路径 C:\Users\Administrator.gradle\wrapper\dists\gradle-5.4.1-all\3221gyojl5jsh0helicew7rwx 2、找到正在下载的gradle版本&#xff0c;Android Studio取消下载gradle&…