您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

手写Spring Config,最终一战,来瞅瞅撒

时间:2022-09-25 11:53:17  来源:网易号  作者:Java开发技术分享

配置分析

为什么要提供配置的方式呢,之前的内容中我们测试的时候都是通过代码来进行的:

GeneralBeanDefinition bd = new GeneralBeanDefinition();bd.setBeanClass(Lad.class);List args = new ArrayList<>();args.add("sunwukong");args.add(new BeanReference("magicGril"));bd.setConstructorArgumentValues(args);bf.registerBeanDefinition("swk", bd);bd = new GeneralBeanDefinition();bd.setBeanClass(MagicGril.class);args = new ArrayList<>();args.add("bAIgujing");bd.setConstructorArgumentValues(args);bf.registerBeanDefinition("magicGril", bd);

下面看下平时使用的时候,通过配置是什么样的:

Springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.XSD">

可以看出,提供配置的方式的优点:

 

  • 实用简单,改动起来比较灵活
  • 而且不需要改动代码

 

常用的配置方式,就是XML和注解的形式,它们的工作过程如下:


 

配置的工作过程定义XML标记和注解

需要定义什么样的XML标记和注解呢?通过之前的内容知道,配置的内容就是Bean定义信息,那么Bean定义的内容就是需要配置的内容

首先来看下Bean定义接口中有哪些信息:


 

XML配置的方式,首先需要定义一个DTD或者XSD文档,来定义一套标记信息,去指定Bean定义

可以看出,bean的配置指定的内容就是Bean定义接口中的信息

注解的方式,需要定义一套注解,那么需要哪些注解呢,也是Bean定义接口中的内容:

 

  • 指定类、指定BeanName、指定scope、指定工厂方法、指定工厂Bean、指定init method、指定destroy method,这些在我们使用Spring的时候是通过@Component来实现的
  • 指定构造参数的依赖:@Autowired、@Qualifier
  • 指定属性依赖:@Value
Bean配置的解析

 

Bean配置的解析过程,需要单独的接口来实现,而不是在BeanFactory中来做,要做到单一职责原则,所以需要定义单独的接口来解析Bean配置,然后再向BeanFactory注册Bean定义

ApplicationContext接口

ApplicationContext这个接口就是用来完成Bean配置解析的,上面说到实现配置的方式有XML和注解,所以会有两个实现类来实现ApplicationContext接口


 

 

  1. XML方式的实现:
  • XML文件可能存在多个,所以这里使用了list
  • 需要完成:加载xml、解析xml、创建Bean定义、注册Bean定义的任务
  1. 注解方式的实现
  • 扫描的包也会存在多个,这里也使用list
  • 需要完成:扫描包、获取注解、创建Bean定义、注册Bean定义的任务

 

因为需要创建和注册Bean定义,所以会使用到BeanFactory和BeanDefinitionRegistry接口,那么这部分代码在子类中分别实现的话就会重复,所以抽象出来放在父类中:


 

用户在使用的使用需要知道哪些接口和类呢?

 

  1. 指定配置相关:xml、注解
  2. 获取bean相关:BeanFactory

 

那么可以使用外观模式,让用户只需要知道ApplicationContext和其子类就行了,ApplicationContext可以继承BeanFactory,继而把两个接口合在一起:


 

ApplicationContext接口:

* @className: ApplicationContext* 用来构建整个应用环境的接口,用来完成Bean的配置和解析* 1:为了减少用户对框架类接口的依赖,扩展了BeanFactory接口,* Bean的配置和Bean的获取都可以通过ApplicationContext接口来完成* 2:配置资源的方式有xml和注解,所以存在xml和注解两种子类的实现* 3. Bean配置解析首先需要加载,所以实现了配置资源Resource的加载接口ResourceLoader* @author: TRpublic interface ApplicationContext extends ResourceLoader,BeanFactory {ApplicationContext的抽象类实现* @className: abstractApplicationContext* @description: ApplicationContext的抽象类实现* @author: TRpublic abstract class AbstractApplicationContext implements ApplicationContext {/** 用组合的方式来持有BeanFactory,完成BeanFactory接口的方法 */protected BeanFactory beanFactory;public AbstractApplicationContext() {super();this.beanFactory = new PreBuildBeanFactory();public AbstractApplicationContext(BeanFactory beanFactory) {super();this.beanFactory = beanFactory;@Overridepublic Object getBean(String beanName) throws Exception {return this.beanFactory.getBean(beanName);@Overridepublic void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {this.beanFactory.registerBeanPostProcessor(beanPostProcessor);xml配置方式的ApplicationContext实现类* @className: XmlApplicationContext* @description: xml配置方式的ApplicationContext实现类* @author: TRpublic class XmlApplicationContext extends AbstractApplicationContext {注解配置方式的ApplicationContext实现类* @className: AnnotationApplicationContext* @description: 注解配置方式的ApplicationContext实现类* @author: TRpublic class AnnotationApplicationContext extends AbstractApplicationContext {配置的实现XML方式XML文件来源的处理

xml配置文件的来源会有多种,比如:


 

不同来源的XML文件,它的加载方式是不一样的,但是在解析的过程中,最后都希望获取到InputStream

这里也需要设计一套接口,对于不同来源的XML文件分别进行处理


 

InputStreamSource接口* @className: InputStreamSource* @description: 配置方式的最终统一接口* @author: TRpublic interface InputStreamSource {* 最终要获取的就是输入流* @return: JAVA.io.InputStreamInputStream getInputStream() throws IOException;Resource接口* @className: Resource* @description: 输入流的资源扩展接口* @author: TRpublic interface Resource extends InputStreamSource {//classpath形式的xml配置文件String CLASS_PATH_PREFIX = "classpath:";//系统文件形式的xml配置文件String File_SYSTEM_PREFIX = "file:";* 判断资源是否存在* @return: booleanboolean exists();* 是否可读* @return: booleanboolean isReadable();* 是否打开* @return: booleanboolean isOpen();* 获取资源文件* @return: java.io.FileFile getFile();InputStreamSource接口的实现类

FileSystemResource实现类:

* @className: FileSystemResource* @description: 系统文件类型的资源实现类* @author: TRpublic class FileSystemResource implements Resource {/** 文件资源对象 */private File file;public FileSystemResource(String fileName) {super();this.file = new File(fileName);public FileSystemResource(File file) {super();this.file = file;@Overridepublic boolean exists() {return this.file == null ? false : this.file.exists();@Overridepublic boolean isReadable() {return this.file == null ? false : this.file.canRead();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return file;@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(this.file);

ClassPathResource实现类:

* @className: ClassPathResource* @description: classpath形式的资源实现类* @author: TRpublic class ClassPathResource implements Resource {//classpath所需要的信息private String path;private Class clazz;private ClassLoader classLoader;public ClassPathResource(String path) {this(path, null );public ClassPathResource(String path, Class clazz) {this(path, clazz, null);public ClassPathResource(String path, Class clazz, ClassLoader classLoader) {super();this.path = path;this.clazz = clazz;this.classLoader = classLoader;public String getPath() {return path;public void setPath(String path) {this.path = path;public Class getClazz() {return clazz;public void setClazz(Class clazz) {this.clazz = clazz;public ClassLoader getClassLoader() {return classLoader;public void setClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;@Overridepublic boolean exists() {if (StringUtils.isNotBlank(path)) {if (this.clazz != null) {return this.clazz.getResource(path) != null;if (this.classLoader != null) {return this.classLoader.getResource(path.startsWith("/") ? path.substring(1) : path) != null;return this.getClass().getResource(path) != null;return false;@Overridepublic boolean isReadable() {return exists();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return null;@Overridepublic InputStream getInputStream() throws IOException {if (StringUtils.isNotBlank(path)) {if (this.clazz != null) {return this.clazz.getResourceAsStream(path);if (this.classLoader != null) {return this.classLoader.getResourceAsStream(path.startsWith("/") ? path.substring(1) : path);return this.getClass().getResourceAsStream(path);return null;

UrlResource实现类:

* @className: UrlResource* @description: URL形式的资源实现类* @author: TRpublic class UrlResource implements Resource {/** url的资源对象 */private URL url;public UrlResource(String url) throws IOException {this.url = new URL(url);public UrlResource(URL url) {super();this.url = url;public URL getUrl() {return url;public void setUrl(URL url) {this.url = url;@Overridepublic boolean exists() {return this.url != null;@Overridepublic boolean isReadable() {return exists();@Overridepublic boolean isOpen() {return false;@Overridepublic File getFile() {return null;@Overridepublic InputStream getInputStream() throws IOException {return null;XML资源加载器

用户给定资源时是一个字符串,上面有三种资源,那么谁去负责创建这些资源呢

这里需要定义一个资源加载器,去分辨不同的资源,然后进行加载,这部分工作是由ApplicationContext来完成的,所以ApplicationContext需要继承ResourceLoader接口

ResourceLoader接口:

* @className: ResourceLoader* 配置资源加载接口* 不同的配置方式,加载过程不一样,所以需要抽象出来一个接口应对变化的部分* 虽然加载的方式不一样,但是返回的资源结果是一样的,都是Resource* @author: TRpublic interface ResourceLoader {* 加载资源* @param location:* @return: demo.context.ResourceResource getResource(String location) throws IOException;

在这里,还需要区分用户给的字符串代表的是哪种资源,所以需要定义字符串的规则:


 

注解方式如何扫描的

扫描的包有哪些呢?

需要到指定的包目录下找出所有的类文件,而且要包含子孙包下的


 

需要定义一个资源路径的匹配行为

扫描的结果

扫描到了包下的class文件后,需要的是类名,而且扫描的是class文件,直接使用上面的FileResource即可

扫描的类ClassPathBeanDefinitionScanner

* @className: ClassPathBeanDefinitionScanner* @description: 扫描class文件* @author: TRpublic class ClassPathBeanDefinitionScanner {private static Log logger = LogFactory.getLog(ClassPathBeanDefinitionScanner.class);private BeanDefinitionRegistry registry;private BeanDefinitionReader reader;private PathMatcher pathMatcher = new AntPathMatcher();private String resourcePatter = "**/*.class";public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {super();this.registry = registry;this.reader = new AnnotationBeanDefinitionReader(registry);* 扫描包的方法* @param basePackages:* @return: voidpublic void scan(String... basePackages) throws Throwable {if (basePackages != null && basePackages.length > 0) {for (String b : basePackages) {this.reader.loadBeanDefintions(doScan(b));* 将扫描的class转为Resource* @param basePackage:* @return: demo.context.Resource[]private Resource[] doScan(String basePackage) throws IOException {// 扫描包下的类// 构造初步匹配模式串,= 给入的包串 + / + **/*.class,替换里面的.为/String pathPattern = StringUtils.replace(basePackage, ".", "/") + "/" + this.resourcePatter;if (pathPattern.charAt(0) != '/') {pathPattern = "/" + pathPattern;// 找出模式的根包路径String rootPath = this.determineRootDir(pathPattern);// 得到文件名匹配的绝对路径模式String fullPattern = this.getClass().getResource("/").toString() + pathPattern;// 根据根包理解得到根包对应的目录File rootDir = new File(this.getClass().getResource(rootPath).toString());// 存放找到的类文件的resource集合Set scanedClassFileResources = new HashSet<>();// 调用doRetrieveMatchingFiles来扫描class文件this.doRetrieveMatchingFiles(fullPattern, rootDir, scanedClassFileResources);return (Resource[]) scanedClassFileResources.toArray();private String determineRootDir(String location) {int rootDirEnd = location.length();rootDirEnd = location.indexOf('*');int zi = location.indexOf('?');if (zi != -1 && zi < rootDirEnd) {rootDirEnd = location.lastIndexOf('/', zi);if (rootDirEnd != -1) {return location.substring(0, rootDirEnd);} else {return location;* 递归找指定目录下的所有类,匹配模式的加入到结果中。* @param fullPattern* @param dir* @param result* @throws IOExceptionprotected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) throws IOException {if (logger.isTraceEnabled()) {logger.trace("Searching directory [" + dir.getAbsolutePath() + "] for files matching pattern ["+ fullPattern + "]");for (File content : listDirectory(dir)) {String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {if (!content.canRead()) {if (logger.isDebugEnabled()) {logger.debug("Skipping subdirectory [" + dir.getAbsolutePath()+ "] because the application is not allowed to read the directory");} else {doRetrieveMatchingFiles(fullPattern, content, result);if (getPathMatcher().match(fullPattern, currPath)) {result.add(new FileSystemResource(content));protected File[] listDirectory(File dir) {File[] files = dir.listFiles();if (files == null) {if (logger.isInfoEnabled()) {logger.info("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");return new File[0];Arrays.sort(files, Comparator.comparing(File::getName));return files;public BeanDefinitionRegistry getRegistry() {return registry;public void setRegistry(BeanDefinitionRegistry registry) {this.registry = registry;public BeanDefinitionReader getReader() {return reader;public void setReader(BeanDefinitionReader reader) {this.reader = reader;public PathMatcher getPathMatcher() {return pathMatcher;public void setPathMatcher(PathMatcher pathMatcher) {this.pathMatcher = pathMatcher;public String getResourcePatter() {return resourcePatter;public void setResourcePatter(String resourcePatter) {this.resourcePatter = resourcePatter;解析成Bean定义

XML和注解最终的输出结果都是Resource,在这里还需要把Resource解析成Bean定义信息才行

需要定义接口来进行解析:

BeanDefinitionReader接口:

* @className: BeanDefinitionReader* @description: 将Resource资源解析成Bean定义的接口* @author: TRpublic interface BeanDefinitionReader {* 解析单个资源* @param resource:* @return: voidvoid loadBeanDefintions(Resource resource) throws Throwable;* 解析多个资源* @param resource:* @return: voidvoid loadBeanDefintions(Resource... resource) throws Throwable;

AbstractBeanDefinitionReader抽象类:

* @className: AbstractBeanDefinitionReader* @description: TODO* @date: 2021/6/10 15:58* @author: jinpeng.sunpublic abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {/** 持有BeanDefinitionRegistry接口,以便完成注册到BeanFactory中 */protected BeanDefinitionRegistry beanDefinitionRegistry;public AbstractBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {super();this.beanDefinitionRegistry = beanDefinitionRegistry;

xml配置方式的bean定义解析器:

* @className: XmlBeanDefinitionReader* @description: xml配置方式的bean定义解析器* @author: TRpublic class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {super(beanDefinitionRegistry);@Overridepublic void loadBeanDefintions(Resource resource) throws Throwable {this.loadBeanDefintions(new Resource[] {resource});@Overridepublic void loadBeanDefintions(Resource... resource) throws Throwable {if (resource != null && resource.length > 0) {for (Resource r : resource) {this.parseXml(r);private void parseXml(Resource r) {//TODO 解析xml文档,获取bean定义,创建bean定义对象,注册到BeanDefinitionRegistry中

注解配置方式的bean定义解析器:

* @className: AnnotationBeanDefinitionReader* @description: 注解配置方式的bean定义解析器:* @author: TRpublic class AnnotationBeanDefinitionReader extends AbstractBeanDefinitionReader {public AnnotationBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {super(beanDefinitionRegistry);@Overridepublic void loadBeanDefintions(Resource resource) throws Throwable {this.loadBeanDefintions(new Resource[] {resource});@Overridepublic void loadBeanDefintions(Resource... resource) throws Throwable {if (resource != null && resource.length > 0) {for (Resource r : resource) {this.retriveAndRegistBeanDefinition(r);private void retriveAndRegistBeanDefinition(Resource resource) {if(resource != null && resource.getFile() != null) {String className = getClassNameFormFile(resource.getFile());try {Class clazz = Class.forName(className);Component component = clazz.getAnnotation(Component.class);if (component != null) {GeneralBeanDefinition beanDefinition = new GeneralBeanDefinition();beanDefinition.setBeanClass(clazz);beanDefinition.setScope(component.scope());beanDefinition.setFactoryMethodName(component.factoryMethodName());beanDefinition.setFactoryBeanName(component.factoryBeanName());beanDefinition.setInitMethodName(component.initMethodName());beanDefinition.setDestroyMethodName(component.destroyMethodName());//获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bdthis.handleConstructor(clazz, beanDefinition);//处理工厂方法参数依赖if(StringUtils.isNotBlank(beanDefinition.getFactoryMethodName())) {this.handleFactoryMethodArgs(clazz, beanDefinition);//处理属性依赖this.handlePropertyDi(clazz, beanDefinition);String beanName = "".equals(component.value()) ? component.name() : null;if (StringUtils.isBlank(beanName)) {// TODO 应用名称生成规则生成beanName;// 默认驼峰命名法beanName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, clazz.getSimpleName());// 注册bean定义this.beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);} catch (ClassNotFoundException | BeanDefinitionException e) {e.printStackTrace();private void handlePropertyDi(Class clazz, GeneralBeanDefinition bd) {// TODO Auto-generated method stubprivate void handleFactoryMethodArgs(Class clazz, GeneralBeanDefinition bd) {// TODO Auto-generated method stubprivate void handleConstructor(Class clazz, GeneralBeanDefinition bd) {//获取所有的构造方法,在构造方法上找Autowired注解,如果有的话,将这个构造方法set到bdConstructor[] constructors = clazz.getConstructors();if (constructors != null && constructors.length > 0) {for (Constructor c : constructors) {if (c.getAnnotation(Autowired.class) != null) {bd.setConstructor(c);Parameter[] ps = c.getParameters();//遍历获取参数上的注解,及创建参数依赖break;private int classPathAbsLength = AnnotationBeanDefinitionReader.class.getResource("/").toString().length();private String getClassNameFormFile(File file) {//返回绝对路径名字符串String absPath = file.getAbsolutePath();String name = absPath.substring(classPathAbsLength+1, absPath.indexOf("."));return StringUtils.replace(name, File.separator, ".");

完善XmlApplicationContext和AnnotationApplicationContext:

public class XmlApplicationContext extends AbstractApplicationContext {private List resources;private BeanDefinitionReader definitionReader;public XmlApplicationContext(String... locations) throws Throwable {super();load(locations);//资源解析成BeanDefinition,外派给BeanDefinitionReader接口来实现this.definitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.beanFactory);Resource[] resourceArray = new Resource[resources.size()];resources.toArray(resourceArray);//将解析后的BeanDefinition装载到BeanFactory中definitionReader.loadBeanDefintions(resourceArray);* 根据用户指定的配置文件位置,加载资源信息* @param locations:* @return: voidprivate void load(String[] locations) throws IOException {if (resources == null) {resources = new ArrayList();//完成加载,创建好Resourceif (locations != null && locations.length > 0) {for (String lo : locations) {Resource resource = getResource(lo);if (resource != null) {this.resources.add(resource);@Overridepublic Resource getResource(String location) throws IOException {if (StringUtils.isNotBlank(location)) {//根据字符串的前缀判断区分,class、系统文件、url三种资源的加载if (location.startsWith(Resource.CLASS_PATH_PREFIX)) {return new ClassPathResource(location.substring(Resource.CLASS_PATH_PREFIX.length()));} else if (location.startsWith(Resource.File_SYSTEM_PREFIX)) {return new FileSystemResource(location.substring(Resource.File_SYSTEM_PREFIX.length()));} else {return new UrlResource(location);return null;public class AnnotationApplicationContext extends AbstractApplicationContext {private ClassPathBeanDefinitionScanner scanner;public AnnotationApplicationContext(String... locations) throws Throwable {scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) this.beanFactory);scanner.scan(locations);@Overridepublic Resource getResource(String location) throws IOException {return null;



Tags:Spring   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  Search: Spring  点击:(55)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: Spring  点击:(39)  评论:(0)  加入收藏
Spring Boot2.0深度实践 核心原理拆解+源码分析
Spring Boot2.0深度实践:核心原理拆解与源码分析一、引言Spring Boot是一个基于Java的轻量级框架,它简化了Spring应用程序的创建过程,使得开发者能够快速搭建一个可运行的应用...【详细内容】
2024-01-15  Search: Spring  点击:(96)  评论:(0)  加入收藏
Spring Boot 3.0是什么?
Spring Boot 3.0是一款基于Java的开源框架,用于简化Spring应用程序的构建和开发过程。与之前的版本相比,Spring Boot 3.0在多个方面进行了改进和增强,使其更加易用、高效和灵活...【详细内容】
2024-01-11  Search: Spring  点击:(133)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11  Search: Spring  点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  Search: Spring  点击:(118)  评论:(0)  加入收藏
深入分析-Spring BeanDefinition构造元信息
Spring BeanDefinition元信息定义方式Bean Definition是一个包含Bean元数据的对象。它描述了如何创建Bean实例、Bean属性的值以及Bean之间的依赖关系。可以使用多种方式来定...【详细内容】
2024-01-08  Search: Spring  点击:(73)  评论:(0)  加入收藏
Spring Boot Starter的原理
Spring Boot Starter 是 Spring Boot 框架的特性之一,用于简化应用程序的依赖管理和配置。1. 概述: - Spring Boot Starter 是一种包含了一组特定功能和依赖关系的依赖项,旨在...【详细内容】
2024-01-05  Search: Spring  点击:(94)  评论:(0)  加入收藏
Spring 七种事务传播性介绍
本文主要介绍了Spring事务传播性的相关知识。Spring中定义了7种事务传播性: PROPAGATION_REQUIRED PROPAGATION_SUPPORTS PROPAGATION_MANDATORY PROPAGATION_REQUIRES_NEW ...【详细内容】
2024-01-04  Search: Spring  点击:(103)  评论:(0)  加入收藏
监控 Spring Cloud 微服务的实践方案
一、简介Spring Cloud是一个基于Spring Boot实现的微服务框架,它提供了丰富的微服务功能,如分布式配置、服务注册与发现、服务熔断、负载均衡等。为了更好地管理和监控这样复...【详细内容】
2023-12-19  Search: Spring  点击:(145)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(1)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(18)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(55)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(51)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(88)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
站内最新
站内热门
站内头条