0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring Boot的注解原理是什么

5jek_harmonyos ? 來源:博客園 ? 作者:kosamino ? 2021-08-27 09:24 ? 次閱讀

首先,先看SpringBoot的主配置類:

@SpringBootApplicationpublic class StartEurekaApplication

{

public static void main(String[] args)

{

SpringApplication.run(StartEurekaApplication.class, args);

}

}

點(diǎn)進(jìn)@SpringBootApplication來看,發(fā)現(xiàn)@SpringBootApplication是一個(gè)組合注解。

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

}

首先我們先來看 @SpringBootConfiguration:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Configurationpublic @interface SpringBootConfiguration {

}

可以看到這個(gè)注解除了元注解以外,就只有一個(gè)@Configuration,那也就是說這個(gè)注解相當(dāng)于@Configuration,所以這兩個(gè)注解作用是一樣的,它讓我們能夠去注冊一些額外的Bean,并且導(dǎo)入一些額外的配置。

那@Configuration還有一個(gè)作用就是把該類變成一個(gè)配置類,不需要額外的XML進(jìn)行配置。所以@SpringBootConfiguration就相當(dāng)于@Configuration。進(jìn)入@Configuration,發(fā)現(xiàn)@Configuration核心是@Component,說明Spring的配置類也是Spring的一個(gè)組件。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Componentpublic @interface Configuration {

@AliasFor

annotation = Component.class

String value() default “”;

}

繼續(xù)來看下一個(gè)@EnableAutoConfiguration,這個(gè)注解是開啟自動配置的功能。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})

public @interface EnableAutoConfiguration {

String ENABLED_OVERRIDE_PROPERTY = “spring.boot.enableautoconfiguration”;

Class《?》[] exclude() default {};

String[] excludeName() default {};

}

可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)這兩個(gè)而組成的,我們先說@AutoConfigurationPackage,他是說:讓包中的類以及子包中的類能夠被自動掃描到spring容器中。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@Import({Registrar.class})

public @interface AutoConfigurationPackage {

}

使用@Import來給Spring容器中導(dǎo)入一個(gè)組件 ,這里導(dǎo)入的是Registrar.class。來看下這個(gè)Registrar:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

Registrar() {

}

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());

}

public Set《Object》 determineImports(AnnotationMetadata metadata) {

return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));

}

}

就是通過以上這個(gè)方法獲取掃描的包路徑,可以debug查看具體的值:

那metadata是什么呢,可以看到是標(biāo)注在@SpringBootApplication注解上的DemosbApplication,也就是我們的主配置類Application:

其實(shí)就是將主配置類(即@SpringBootApplication標(biāo)注的類)的所在包及子包里面所有組件掃描加載到Spring容器。因此我們要把DemoApplication放在項(xiàng)目的最高級中(最外層目錄)。

看看注解@Import(AutoConfigurationImportSelector.class),@Import注解就是給Spring容器中導(dǎo)入一些組件,這里傳入了一個(gè)組件的選擇器:AutoConfigurationImportSelector。

可以從圖中看出AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector,ImportSelector有一個(gè)方法為:selectImports。將所有需要導(dǎo)入的組件以全類名的方式返回,這些組件就會被添加到容器中。

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry =

this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);

return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

}

}

會給容器中導(dǎo)入非常多的自動配置類(xxxAutoConfiguration);就是給容器中導(dǎo)入這個(gè)場景需要的所有組件,并配置好這些組件。

有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作。那是如何獲取到這些配置類的呢,看看下面這個(gè)方法:

protected AutoConfigurationImportSelector.AutoConfigurationEntry

getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return EMPTY_ENTRY;

} else {

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

List《String》 configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

configurations = this.removeDuplicates(configurations);

Set《String》 exclusions = this.getExclusions(annotationMetadata, attributes);

this.checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = this.filter(configurations, autoConfigurationMetadata);

this.fireAutoConfigurationImportEvents(configurations, exclusions);

return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);

}

}

我們可以看到getCandidateConfigurations()這個(gè)方法,他的作用就是引入系統(tǒng)已經(jīng)加載好的一些類,到底是那些類呢:

protected List《String》 getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List《String》 configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

Assert.notEmpty(configurations,

“No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.”);

return configurations;

}

public static List《String》 loadFactoryNames(Class《?》 factoryClass, @Nullable ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

}

會從META-INF/spring.factories中獲取資源,然后通過Properties加載資源:

private static Map《String, List《String》》 loadSpringFactories(@Nullable ClassLoader classLoader) {

MultiValueMap《String, String》 result = (MultiValueMap)cache.get(classLoader);

if (result != null) {

return result;

} else {

try {

Enumeration《URL》 urls = classLoader !=

null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”);

LinkedMultiValueMap result = new LinkedMultiValueMap();

while(urls.hasMoreElements()) {

URL url = (URL)urls.nextElement();

UrlResource resource = new UrlResource(url);

Properties properties = PropertiesLoaderUtils.loadProperties(resource);

Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {

Map.Entry《?, ?》 entry = (Map.Entry)var6.next();

String factoryClassName = ((String)entry.getKey()).trim();

String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());

int var10 = var9.length;

for(int var11 = 0; var11 《 var10; ++var11) {

String factoryName = var9[var11];

result.add(factoryClassName, factoryName.trim());

}

}

}

cache.put(classLoader, result);

return result;

} catch (IOException var13) {

throw new IllegalArgumentException(“Unable to load factories from location [META-INF/spring.factories]”, var13);

}

}

}

可以知道SpringBoot在啟動的時(shí)候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類導(dǎo)入到容器中,自動配置類就生效,幫我們進(jìn)行自動配置工作。以前我們需要自己配置的東西,自動配置類都幫我們完成了。如下圖可以發(fā)現(xiàn)Spring常見的一些類已經(jīng)自動導(dǎo)入。

接下來看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),這個(gè)注解就是掃描包,然后放入spring容器。

@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),

@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})

public @interface SpringBootApplication {}

總結(jié)下@SpringbootApplication:就是說,他已經(jīng)把很多東西準(zhǔn)備好,具體是否使用取決于我們的程序或者說配置。

接下來繼續(xù)看run方法:

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

來看下在執(zhí)行run方法到底有沒有用到哪些自動配置的東西,我們點(diǎn)進(jìn)run:

public ConfigurableApplicationContext run(String.。. args) {

//計(jì)時(shí)器

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

Collection《SpringBootExceptionReporter》 exceptionReporters = new ArrayList();

this.configureHeadlessProperty();

//監(jiān)聽器

SpringApplicationRunListeners listeners = this.getRunListeners(args);

listeners.starting();

Collection exceptionReporters;

try {

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);

this.configureIgnoreBeanInfo(environment);

Banner printedBanner = this.printBanner(environment);

//準(zhǔn)備上下文

context = this.createApplicationContext();

exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

//預(yù)刷新context

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

//刷新context

this.refreshContext(context);

//刷新之后的context

this.afterRefresh(context, applicationArguments);

stopWatch.stop();

if (this.logStartupInfo) {

(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);

}

listeners.started(context);

this.callRunners(context, applicationArguments);

} catch (Throwable var10) {

this.handleRunFailure(context, var10, exceptionReporters, listeners);

throw new IllegalStateException(var10);

}

try {

listeners.running(context);

return context;

} catch (Throwable var9) {

this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);

throw new IllegalStateException(var9);

}

}

那我們關(guān)注的就是 refreshContext(context); 刷新context,我們點(diǎn)進(jìn)來看。

private void refreshContext(ConfigurableApplicationContext context) {

refresh(context);

if (this.registerShutdownHook) {

try {

context.registerShutdownHook();

}

catch (AccessControlException ex) {

// Not allowed in some environments.

}

}

}

我們繼續(xù)點(diǎn)進(jìn)refresh(context);

protected void refresh(ApplicationContext applicationContext) {

Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);

((AbstractApplicationContext) applicationContext).refresh();

}

他會調(diào)用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點(diǎn)進(jìn)來看:

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

prepareRefresh();

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

try {

// Allows post-processing of the bean factory in context subclasses.

postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.

invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.

registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.

initMessageSource();

// Initialize event multicaster for this context.

initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.

onRefresh();

// Check for listener beans and register them.

registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.

finishRefresh();

}catch (BeansException ex) {

if (logger.isWarnEnabled()) {

logger.warn(“Exception encountered during context initialization - ” +

“cancelling refresh attempt: ” + ex);

}

// Destroy already created singletons to avoid dangling resources.

destroyBeans();

// Reset ‘a(chǎn)ctive’ flag.

cancelRefresh(ex);

// Propagate exception to caller.

throw ex;

}finally {

// Reset common introspection caches in Spring‘s core, since we

// might not ever need metadata for singleton beans anymore.。.

resetCommonCaches();

}

}

}

由此可知,就是一個(gè)spring的bean的加載過程。繼續(xù)來看一個(gè)方法叫做 onRefresh():

protected void onRefresh() throws BeansException {

// For subclasses: do nothing by default.

}

他在這里并沒有直接實(shí)現(xiàn),但是我們找他的具體實(shí)現(xiàn):

比如Tomcat跟web有關(guān),我們可以看到有個(gè)ServletWebServerApplicationContext:

@Overrideprotected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException(“Unable to start web server”, ex);

}

}

可以看到有一個(gè)createWebServer();方法他是創(chuàng)建web容器的,而Tomcat不就是web容器,那是如何創(chuàng)建的呢,我們繼續(xù)看:

private void createWebServer() {

WebServer webServer = this.webServer;

ServletContext servletContext = getServletContext();

if (webServer == null && servletContext == null) {

ServletWebServerFactory factory = getWebServerFactory();

this.webServer = factory.getWebServer(getSelfInitializer());

}

else if (servletContext != null) {

try {

getSelfInitializer().onStartup(servletContext);

}

catch (ServletException ex) {

throw new ApplicationContextException(“Cannot initialize servlet context”,

ex);

}

}

initPropertySources();

}

factory.getWebServer(getSelfInitializer());他是通過工廠的方式創(chuàng)建的。

public interface ServletWebServerFactory {

WebServer getWebServer(ServletContextInitializer.。. initializers);

}

可以看到 它是一個(gè)接口,為什么會是接口。因?yàn)槲覀儾恢故荰omcat一種web容器。

我們看到還有Jetty,那我們來看TomcatServletWebServerFactory:

@Overridepublic WebServer getWebServer(ServletContextInitializer.。. initializers) {

Tomcat tomcat = new Tomcat();

File baseDir = (this.baseDirectory != null) ? this.baseDirectory

: createTempDir(“tomcat”);

tomcat.setBaseDir(baseDir.getAbsolutePath());

Connector connector = new Connector(this.protocol);

tomcat.getService().addConnector(connector);

customizeConnector(connector);

tomcat.setConnector(connector);

tomcat.getHost().setAutoDeploy(false);

configureEngine(tomcat.getEngine());

for (Connector additionalConnector : this.additionalTomcatConnectors) {

tomcat.getService().addConnector(additionalConnector);

}

prepareContext(tomcat.getHost(), initializers);

return getTomcatWebServer(tomcat);

}

那這塊代碼,就是我們要尋找的內(nèi)置Tomcat,在這個(gè)過程當(dāng)中,我們可以看到創(chuàng)建Tomcat的一個(gè)流程。

如果不明白的話, 我們在用另一種方式來理解下,大家要應(yīng)該都知道stater舉點(diǎn)例子。

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-data-redis《/artifactId》《/dependency》《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-freemarker《/artifactId》《/dependency》

首先自定義一個(gè)stater。

《parent》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-parent《/artifactId》

《version》2.1.4.RELEASE《/version》

《relativePath/》《/parent》《groupId》com.zgw《/groupId》《artifactId》gw-spring-boot-starter《/artifactId》《version》1.0-SNAPSHOT《/version》《dependencies》

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-autoconfigure《/artifactId》

《/dependency》《/dependencies》

我們先來看maven配置寫入版本號,如果自定義一個(gè)stater的話必須依賴spring-boot-autoconfigure這個(gè)包,我們先看下項(xiàng)目目錄。

public class GwServiceImpl implements GwService{

@Autowired

GwProperties properties;

@Override

public void Hello()

{

String name=properties.getName();

System.out.println(name+“說:你們好啊”);

}

}

我們做的就是通過配置文件來定制name這個(gè)是具體實(shí)現(xiàn)。

@Component@ConfigurationProperties(prefix = “spring.gwname”)

public class GwProperties {

String name=“zgw”;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

這個(gè)類可以通過@ConfigurationProperties讀取配置文件。

@Configuration@ConditionalOnClass(GwService.class) //掃描類

@EnableConfigurationProperties(GwProperties.class) //讓配置類生效

public class GwAutoConfiguration {

/**

* 功能描述 托管給spring

* @author zgw

* @return

*/

@Bean

@ConditionalOnMissingBean

public GwService gwService()

{

return new GwServiceImpl();

}

}

這個(gè)為配置類,為什么這么寫因?yàn)?,spring-boot的stater都是這么寫的,我們可以參照他仿寫stater,以達(dá)到自動配置的目的,然后我們在通過spring.factories也來進(jìn)行配置。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration

然后這樣一個(gè)簡單的stater就完成了,然后可以進(jìn)行maven的打包,在其他項(xiàng)目引入就可以使用。

鏈接:cnblogs.com/cmt/p/14553189.html

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    338

    瀏覽量

    14295
  • Boot
    +關(guān)注

    關(guān)注

    0

    文章

    149

    瀏覽量

    35751

原文標(biāo)題:10000 字講清楚 Spring Boot 注解原理

文章出處:【微信號:harmonyos_developer,微信公眾號:harmonyos_developer】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Spring事務(wù)實(shí)現(xiàn)原理

    這些操作。 spring事務(wù)有編程式事務(wù)和聲明式事務(wù)兩種實(shí)現(xiàn)方式。編程式事務(wù)是通過編寫代碼來管理事務(wù)的提交、回滾、以及事務(wù)的邊界。這意味著開發(fā)者需要在代碼中顯式地調(diào)用事務(wù)的開始、提交和回滾。聲明式事務(wù)是通過配置來管理事務(wù),您可以使用注解或XML配置來
    的頭像 發(fā)表于 11-08 10:10 ?392次閱讀
    <b class='flag-5'>Spring</b>事務(wù)實(shí)現(xiàn)原理

    Spring Cloud Gateway網(wǎng)關(guān)框架

    SpringCloud Gateway功能特征如下: (1) 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進(jìn)行構(gòu)建; (2) 動態(tài)路由:能夠匹配任何請求屬性;
    的頭像 發(fā)表于 08-22 09:58 ?413次閱讀
    <b class='flag-5'>Spring</b> Cloud Gateway網(wǎng)關(guān)框架

    vue+spring boot人員定位系統(tǒng)源碼,實(shí)現(xiàn)實(shí)時(shí)定位、智慧調(diào)度、軌跡追蹤

    、機(jī)具、物料上定位標(biāo)簽回傳的位置信息數(shù)據(jù),采用多維定位模式,精確定位人、機(jī)具、物料的實(shí)時(shí)位置,實(shí)現(xiàn)實(shí)時(shí)定位、物料標(biāo)簽配置、智慧調(diào)度、軌跡追蹤、工時(shí)統(tǒng)計(jì)、區(qū)域物料統(tǒng)計(jì)、電子圍欄等應(yīng)用功能。 技術(shù)架構(gòu):java+ spring boot+ v
    的頭像 發(fā)表于 08-08 14:27 ?509次閱讀
    vue+<b class='flag-5'>spring</b> <b class='flag-5'>boot</b>人員定位系統(tǒng)源碼,實(shí)現(xiàn)實(shí)時(shí)定位、智慧調(diào)度、軌跡追蹤

    使用Spring Boot 3.2虛擬線程搭建靜態(tài)文件服務(wù)器

    Spring Boot 3.2 于 2023 年 11 月大張旗鼓地發(fā)布,標(biāo)志著 Java 開發(fā)領(lǐng)域的一個(gè)關(guān)鍵時(shí)刻。這一突破性的版本引入了一系列革命性的功能。
    的頭像 發(fā)表于 01-09 09:34 ?1071次閱讀
    使用<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 3.2虛擬線程搭建靜態(tài)文件服務(wù)器

    Spring事務(wù)失效的十種常見場景

    Spring的聲明式事務(wù)功能更是提供了極其方便的事務(wù)配置方式,配合Spring Boot的自動配置,大多數(shù)Spring Boot項(xiàng)目只需要在
    的頭像 發(fā)表于 12-11 15:03 ?834次閱讀

    【飛騰派4G版免費(fèi)試用】Spring Boot和飛騰派融合構(gòu)建的農(nóng)業(yè)物聯(lián)網(wǎng)系統(tǒng)-環(huán)境搭建篇

    ntpdate-u 120.25.108.11) 7.安裝MySQL服務(wù)器(sudo apt install mysql -server) 遇到問題: 1測試中板子TF卡處發(fā)熱較為嚴(yán)重。 下期更新: Spring Boot和飛騰派融合構(gòu)建的農(nóng)業(yè)物聯(lián)網(wǎng)系統(tǒng)-實(shí)現(xiàn)篇1
    發(fā)表于 12-11 15:00

    dubbo和spring cloud區(qū)別

    包括了服務(wù)提供者、注冊中心、服務(wù)消費(fèi)者等角色。 Spring Cloud是基于Spring Boot的微服務(wù)
    的頭像 發(fā)表于 12-04 14:47 ?1548次閱讀

    如何在Spring Boot應(yīng)用程序中整合ZXing庫

    在數(shù)字化時(shí)代,二維碼已經(jīng)成為了信息交流的一種常見方式。它們被廣泛用于各種應(yīng)用,從產(chǎn)品標(biāo)簽到活動傳單,以及電子支付。本文將向您展示如何在Spring Boot應(yīng)用程序中整合ZXing庫,以創(chuàng)建和解析QR碼。
    的頭像 發(fā)表于 12-03 17:39 ?1048次閱讀

    SpringBoot核心注解由幾個(gè)注解組成

    Spring Boot 是一個(gè)開源的 JavaEE 開發(fā)框架,它以簡化 Spring 框架的配置與開發(fā)為目標(biāo),旨在讓開發(fā)者能快速構(gòu)建獨(dú)立、生產(chǎn)級別的應(yīng)用程序。Spring
    的頭像 發(fā)表于 12-03 15:09 ?685次閱讀

    springboot自動裝配原理簡述

    Spring Boot是針對Spring框架的一種快速開發(fā)工具,它通過自動裝配(Auto-Configuration)機(jī)制簡化了Spring應(yīng)用程序的配置和部署。本文將詳細(xì)介紹
    的頭像 發(fā)表于 12-03 14:57 ?1685次閱讀

    Spring Boot 3.2支持虛擬線程和原生鏡像

    Spring Boot 3.2 前幾日發(fā)布,讓我們用 Java 21、GraalVM 和虛擬線程來嘗試一下。
    的頭像 發(fā)表于 11-30 16:22 ?671次閱讀

    springboot核心注解

    Spring Boot 是基于 Spring 框架的開源框架,它可以幫助開發(fā)者快速構(gòu)建、部署和運(yùn)行獨(dú)立的、生產(chǎn)級的 Spring 應(yīng)用程序。Spri
    的頭像 發(fā)表于 11-23 09:23 ?481次閱讀

    springmvc常用5種注解

    SpringMVC是一種基于Java的Web框架,使用注解可以更加方便靈活地開發(fā)和管理控制器,實(shí)現(xiàn)請求的映射和處理。在SpringMVC中,有許多常用的注解,本文將詳細(xì)介紹其中的五種注解,并且詳細(xì)
    的頭像 發(fā)表于 11-22 16:51 ?766次閱讀

    spring分布式框架有哪些

    Spring分布式框架。 Spring Cloud Spring Cloud是基于Spring Boot的分布式開發(fā)工具包。它提供了多個(gè)子
    的頭像 發(fā)表于 11-16 10:58 ?735次閱讀

    Spring布能用來搭建基礎(chǔ)架構(gòu)嗎

    Spring Boot 是一個(gè)用于簡化 Spring 應(yīng)用程序開發(fā)的框架,它利用 Spring 框架的強(qiáng)大功能,使得基礎(chǔ)架構(gòu)的搭建變得更加簡單、輕量級、易于維護(hù)。在本文中,我們將詳細(xì)討
    的頭像 發(fā)表于 11-16 10:56 ?362次閱讀