分布式微服务: springboot底层机制实现

springboot底层机制实现

  • 搭建SpringBoot底层机制开发环境
  • @Configuration+@Bean会发生什么,并分析机制
  • 提出问题: SpringBoot 是怎么启动Tomcat, 并可以支持访问@Controller
  • 源码分析: SpringApplication.run()
    • SpringBoot的debug流程
  • 实现SpringBoot底层机制[Tomcat启动分析 + Spring容器初始化 + Tomcat如何关联Spring容器]
    • 实现任务阶段1-创建Tomcat, 并启动
      • 🥦说明:创建Tomcat, 并启动
      • 🥦分析+代码实现
      • 🥦完成测试
    • 实现任务阶段2-创建Spring容器
      • 🥦说明:创建Spring容器
      • 🥦分析+代码实现
    • 实现任务阶段3-将Tomcat 和 Spring容器关联, 并启动Spring容器
      • 🥦说明:将Tomcat 和 Spring容器关联, 并启动Spring容器
      • 🥦分析+代码实现
      • 🥦完成测试
      • 🥦注意事项和细节
  • Lombok
    • Lombok介绍
    • Lombok常用注解
    • Lombok应用实例
      • 代码实现

在这里插入图片描述

搭建SpringBoot底层机制开发环境

1.创建 Maven 项目 zzw-springboot 参考springboot快速入门
在这里插入图片描述

2.在pom.xml引入SpringBoot父工程和web项目场景启动器

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.zzw</groupId><artifactId>quickStart</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!--导入springboot父工程-规定写法[在mybatis中讲过]--><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.5.3</version></parent><!--导入web项目场景启动器: 会自动导入和web开发相关的所有依赖[库/jar]后面还会说明spring-boot-starter-web 到底引入哪些相关依赖--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
</project>

3.创建com.zzw.springboot.MainApp.java SpringBoot引用主程序

/*** @SpringBootApplication: 表示这是一个springboot引用/项目*/
@SpringBootApplication
public class MainApp {public static void main(String[] args) {//启动springboot应用程序/项目//提示问题:当我们执行run方法时,怎么就启动了我们的内置的Tomcat?//在分析run方法底层机制的基础上, 我们自己尝试实现ApplicationContext ioc =SpringApplication.run(MainApp.class, args);}
}

4.启动项目 ok, 大家注意Tomcat也启动了.[这里请同学们先思考, 是如何实现的?]
在这里插入图片描述

@Configuration+@Bean会发生什么,并分析机制

1.创建D:\idea_project\zzw_springboot\zzw-springboot\src\main\java\com\zzw\springboot\bean\Dog.java

public class Dog {}

2.创建com.zzw.springboot.config

/*** @Configuration:指明当前类是一个配置类;充当Spring容器/Spring配置文件* 如果该配置类, 在springboot扫描的包/子包下, 会被注入到Spring容器中* 在该类中, 可以通过@Bean 来注入其它的组件*/
@Configuration
public class Config {/*** 1.通过@Bean的方式, 将new出来的Bean对象, 放入到Spring容器* 2.该bean在Spring容器的name/id 默认就是 方法名* 3.通过方法名, 可以得到注入到容器中的dog对象*/@Beanpublic Dog dog() {return new Dog();}
}

3.修改com.zzw.springboot.MainApp.java, 看看容器中是否已经注入了 dog 实例

debug

在这里插入图片描述

ioc容器中已经注入了 dog 实例

在这里插入图片描述

@Configuration注解标注的配置类也会被实例化并被注入到Spring容器.

在这里插入图片描述

4.底层机制分析: 仍然是 我们实现Spring容器那一套机制 IO/文件扫描+注解+反射+集合+映射. 提示: 回去复习一下 实现Spring底层机制.

(1)快捷键查看ioc->singletonObjects->table列表 有多少元素? table右键->Customize Data Views->Enable alternative view for… 但是勾选上会少很多东西
在这里插入图片描述
这里是按照索引排序,无法按照字母排序。

在这里插入图片描述

(2)debug时元素按照字母排列

在这里插入图片描述
在这里插入图片描述

提出问题: SpringBoot 是怎么启动Tomcat, 并可以支持访问@Controller

1.创建com.zzw.springboot.controller.HiController
RestController解释, 手动实现Tomcat底层机制

/*** 1.因为 HiController 被 @RestController 标注, 所以就是一个控制器* 2.HiController 在扫描包/子包下,就会被注入到Spring容器*/
@RestController
public class HiController {@RequestMapping("/hi")public String hi() {return "hi zzw HiController";}
}

2.完成测试, 浏览器输入 http://localhost:8080/hi
在这里插入图片描述

3.提出问题: SpringBoot 是怎么内嵌 Tomcat, 并启动Tomcat的? => 追踪源码

源码分析: SpringApplication.run()

1.Debug SpringApplication.run(MainApp.class, args); 看看SpringBoot 是如何启动Tomcat的.

2.我们的Debug目标: 紧抓一条线, 就是看到 tomcat 被启动的代码, 比如 tomcat.start().

3.源码如下

@SpringBootApplication
public class MainApp {public static void main(String[] args) {//启动springboot应用程序/项目//提示问题:当我们执行run方法时,怎么就启动了我们的内置的Tomcat?//在分析run方法底层机制的基础上, 我们自己尝试实现ApplicationContext ioc =SpringApplication.run(MainApp.class, args);/***  这里我们开始Debug SpringApplication.run();*  1.SpringApplication.java*   public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {*      return run(new Class[]{primarySource}, args);*  }*  2.SpringApplication.java: 创建返回 ConfigurableApplicationContext(这是个接口)对象* public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {*      return (new SpringApplication(primarySources)).run(args);*  }*  3.SpringApplication.java* public ConfigurableApplicationContext run(String... args) {*     StopWatch stopWatch = new StopWatch();*     stopWatch.start();*     DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();*     ConfigurableApplicationContext context = null;*     this.configureHeadlessProperty();*     SpringApplicationRunListeners listeners = this.getRunListeners(args);*     listeners.starting(bootstrapContext, this.mainApplicationClass);**     try {*         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);*         ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);*         this.configureIgnoreBeanInfo(environment);*         Banner printedBanner = this.printBanner(environment);*         context = this.createApplicationContext();//严重分析: 创建容器*         context.setApplicationStartup(this.applicationStartup);*         this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);*         this.refreshContext(context);//严重分析: 刷新应用程序上下文, 比如 初始化默认设置/注入相关bean/启动tomcat*         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, listeners);*         throw new IllegalStateException(var10);*     }**     try {*         listeners.running(context);*         return context;*     } catch (Throwable var9) {*         this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);*         throw new IllegalStateException(var9);*     }* }* 4.SpringApplication.java: 容器类型很多, 会根据你的this.webApplicationType创建对应的容器* this.webApplicationType 默认是 Servlet, 也就是web容器, 可以处理servlet* protected ConfigurableApplicationContext createApplicationContext() {*     return this.applicationContextFactory.create(this.webApplicationType);* }* 5.ApplicationContextFactory.java*  ApplicationContextFactory DEFAULT = (webApplicationType) -> {*      try {*          switch(webApplicationType) {*          case SERVLET://默认进入到这个分支, 返回AnnotationConfigServletWebServerApplicationContext容器*              return new AnnotationConfigServletWebServerApplicationContext();*          case REACTIVE:*              return new AnnotationConfigReactiveWebServerApplicationContext();*          default:*              return new AnnotationConfigApplicationContext();*          }*      } catch (Exception var2) {*          throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);*      }*  };*  6.SpringApplication.java*  private void refreshContext(ConfigurableApplicationContext context) {*      if (this.registerShutdownHook) {*          shutdownHook.registerApplicationContext(context);*      }**      this.refresh(context);//严重分析, 真正执行相关任务*  }*  7.SpringApplication.java*  protected void refresh(ConfigurableApplicationContext applicationContext) {*     applicationContext.refresh();* }* 8.ServletWebServerApplicationContext.java* public final void refresh() throws BeansException, IllegalStateException {*     try {*         super.refresh();//分析这个方法*     } catch (RuntimeException var3) {*         WebServer webServer = this.webServer;*         if (webServer != null) {*             webServer.stop();*         }**         throw var3;*     }* }* 9.AbstractApplicationContext.java* @Override* 	public void refresh() throws BeansException, IllegalStateException {* 		synchronized (this.startupShutdownMonitor) {* 			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");** 			// 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);** 				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");* 				// Invoke factory processors registered as beans in the context.* 				invokeBeanFactoryPostProcessors(beanFactory);** 				// Register bean processors that intercept bean creation.* 				registerBeanPostProcessors(beanFactory);* 				beanPostProcess.end();** 				// 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 'active' 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();* 				contextRefresh.end();* 			}* 		}* 	}* 	10.ServletWebServerApplicationContext.java*  protected void onRefresh() {*     super.onRefresh();**     try {*         this.createWebServer();//看到胜利的曙光. 创建webserver 可以理解成会创建指定web服务-Tomcat*     } catch (Throwable var2) {*         throw new ApplicationContextException("Unable to start web server", var2);*     }* }* 11.ServletWebServerApplicationContext.java*  private void createWebServer() {*      WebServer webServer = this.webServer;*      ServletContext servletContext = this.getServletContext();*      if (webServer == null && servletContext == null) {*          StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");*          ServletWebServerFactory factory = this.getWebServerFactory();*          createWebServer.tag("factory", factory.getClass().toString());*          this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});//严重分析, 使用TomcatServletWebServerFactory 创建一个TomcatWebServer*          createWebServer.end();*          this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));*          this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));*      } else if (servletContext != null) {*          try {*              this.getSelfInitializer().onStartup(servletContext);*          } catch (ServletException var5) {*              throw new ApplicationContextException("Cannot initialize servlet context", var5);*          }*      }**      this.initPropertySources();*  }*  12.TomcatServletWebServerFactory.java: 会创建Tomcat, 并启动*  public WebServer getWebServer(ServletContextInitializer... initializers) {*     if (this.disableMBeanRegistry) {*         Registry.disableRegistry();*     }**     Tomcat tomcat = new Tomcat();//创建了Tomcat对象*     File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");*     //这里做了一系列配置*     tomcat.setBaseDir(baseDir.getAbsolutePath());*     Connector connector = new Connector(this.protocol);*     connector.setThrowOnFailure(true);*     tomcat.getService().addConnector(connector);*     this.customizeConnector(connector);*     tomcat.setConnector(connector);*     tomcat.getHost().setAutoDeploy(false);*     this.configureEngine(tomcat.getEngine());*     Iterator var5 = this.additionalTomcatConnectors.iterator();**     while(var5.hasNext()) {*         Connector additionalConnector = (Connector)var5.next();*         tomcat.getService().addConnector(additionalConnector);*     }**     this.prepareContext(tomcat.getHost(), initializers);*     return this.getTomcatWebServer(tomcat);//严重分析该方法.* }* 13.TomcatServletWebServerFactory.java* protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {*     return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());* }* 14.TomcatServletWebServerFactory.java* public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {*     this.monitor = new Object();*     this.serviceConnectors = new HashMap();*     Assert.notNull(tomcat, "Tomcat Server must not be null");*     this.tomcat = tomcat;*     this.autoStart = autoStart;*     this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;*     this.initialize();//分析这个方法* }* 15.TomcatServletWebServerFactory.java*  private void initialize() throws WebServerException {*     logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));*     Object var1 = this.monitor;*     synchronized(this.monitor) {*         try {*             this.addInstanceIdToEngineName();*             Context context = this.findContext();*             context.addLifecycleListener((event) -> {*                 if (context.equals(event.getSource()) && "start".equals(event.getType())) {*                     this.removeServiceConnectors();*                 }**             });*             this.tomcat.start();//启动Tomcat*             this.rethrowDeferredStartupExceptions();**             try {*                 ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());*             } catch (NamingException var5) {*                 ;*             }**             this.startDaemonAwaitThread();*         } catch (Exception var6) {*             this.stopSilently();*             this.destroySilently();*             throw new WebServerException("Unable to start embedded Tomcat", var6);*         }**     }* }*/System.out.println("ok.");}
}

SpringBoot的debug流程

在这里插入图片描述

F7, Step Into
在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

webApplicationType类型可以在配置文件中修改

在这里插入图片描述

F7, Step Into

在这里插入图片描述

Shift+F8, Step Out

在这里插入图片描述

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

看看父类的类型

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

在这里插入图片描述

F7, Step Into

在这里插入图片描述

F7, Step Into

在这里插入图片描述

放行 Resume Problem

在这里插入图片描述

F7, Step Into

在这里插入图片描述

放行 Resume Problem
在这里插入图片描述

对容器进行估值

在这里插入图片描述

实现SpringBoot底层机制[Tomcat启动分析 + Spring容器初始化 + Tomcat如何关联Spring容器]

实现任务阶段1-创建Tomcat, 并启动

🥦说明:创建Tomcat, 并启动

🥦分析+代码实现

1.修改E:\idea_project\zzw_springboot\zzw-springboot\pom.xml

<!--导入web项目场景启动器: 会自动导入和web开发相关的所有依赖[库/jar]
后面还会说明spring-boot-starter-web 到底引入哪些相关依赖-->
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!--因为我们自己要创建Tomcat对象, 并启动.因此我们先排除 内嵌的 spring-boot-starter-tomcat--><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!--我们指定tomcat版本, 引入tomcat依赖/库1.使用指定的tomcat 8.5.75, 请小伙伴也引入这个版本2.如果我们引入自己指定的tomcat, 一定要记住把前面spring-boot-starter-tomcat排除3.如果你不排除, 会出现 GenericServlet Not Found 的错误提示.--><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>8.5.75</version></dependency>
</dependencies>

2.创建src/main/java/com/zzw/zzwspringboot/ZzwSpringApplication.java

public class ZzwSpringApplication {//这里我们会创建Tomcat对象, 并关联Spring容器, 并启动public static void run() {try {//创建Tomcat对象Tomcat tomcat = new Tomcat();//设置9090端口tomcat.setPort(9090);//启动, 就会在9090端口监听tomcat.start();//等待请求接入System.out.println("====9090 等待请求接入====");tomcat.getServer().await();} catch (Exception e) {throw new RuntimeException(e);}}
}

3.主程序src/main/java/com/zzw/zzwspringboot/ZzwMainApp.java

//主程序
public class ZzwMainApp {public static void main(String[] args) {//启动ZzwSpringBoot项目/程序ZzwSpringApplication.run();}
}

🥦完成测试

1.运行效果
在这里插入图片描述

2.浏览器请求 http://localhost:9090/, 这时没有返回信息, 因为还没有写Controller.
在这里插入图片描述

3.管理员权限运行cmd窗口, 输入netstat -anb. 证明9090端口真的在监听.

在这里插入图片描述

实现任务阶段2-创建Spring容器

🥦说明:创建Spring容器

🥦分析+代码实现

1.新建src/main/java/com/zzw/zzwspringboot/bean/Monster.java, 做一个测试Bean

public class Monster {
}

2.新建src/main/java/com/zzw/zzwspringboot/controller/ZzwHiController.java, 作为Controller

@RestController
public class ZzwHiController {@RequestMapping("/hi")public String hi() {return "hi ZzwHiController";}
}

3.新建src/main/java/com/zzw/zzwspringboot/config/ZzwConfig.java, 作为Spring的配置文件

/*** ZzwConfig 配置类 作为Spring的配置文件* 这里有一个问题, 容器怎么知道要扫描哪些包 ?=> 一会代码体现** 在配置类中可以指定要扫描的包: @ComponentScan("com.zzw.zzwspringboot")*/
@Configuration
@ComponentScan("com.zzw.zzwspringboot")
public class ZzwConfig {//注入Bean - monster 对象到Spring容器@Beanpublic Monster monster() {return new Monster();}
}

4.新建src/main/java/com/zzw/zzwspringboot/ZzwWebApplicationInitializer.java

/*** 解读* 1.创建我们的Spring容器, Initializer-初始化器* 2.加载/关联Spring容器的配置-按照注解的方式* 3.完成Spring容器配置的bean的创建, 依赖注入* 4.创建前端控制器 DispatcherServlet, 并让其持有Spring容器* 5.当 DispatcherServlet 持有容器, 就可以进行分发映射, 回忆实现SpringMVC底层机制* 6.这里onStartup 是Tomcat调用, 并把ServletContext 对象传入*/
public class ZzwWebApplicationInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {System.out.println("startup ...");//加载Spring web application configuration => 容器//自己实现的Spring容器叫做 ZzwSpringApplicationContextAnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();//在ac中, 注册 ZzwConfig.class配置类ac.register(ZzwConfig.class);//刷新ac.refresh();//完成bean的创建和配置//1.创建注册非常重要的前端控制器 DispatcherServlet//2.让DispatcherServlet 持有容器//3.这样就可以进行映射分发, 回忆一下我们自己实现的SpringMVC的机制DispatcherServlet dispatcherServlet = new DispatcherServlet(ac);//返回了ServletRegistration.Dynamic 对象ServletRegistration.Dynamic registration= servletContext.addServlet("app", dispatcherServlet);//当tomcat启动时,加载 dispatcherServletregistration.setLoadOnStartup(1);//拦截请求, 并进行分发处理//这里再提示一下 / 和 /* 的区别registration.addMapping("/");}
}

实现任务阶段3-将Tomcat 和 Spring容器关联, 并启动Spring容器

🥦说明:将Tomcat 和 Spring容器关联, 并启动Spring容器

🥦分析+代码实现

1.修改src/main/java/com/zzw/zzwspringboot/ZzwSpringApplication.java

public class ZzwSpringApplication {//这里我们会创建Tomcat对象, 并关联Spring容器, 并启动public static void run() {try {//创建Tomcat对象Tomcat tomcat = new Tomcat();//1. 让tomcat可以将请求转发到spring web容器, 因此需要进行关联//2. "/zzwboot" 就是我们项目的 application context, 就是我们原来配置tomcat时, 指定的 application context//3. "E:\idea_project\zzw_springboot\zzw-springboot" 指定项目的目录tomcat.addWebapp("/zzwboot", "E:\\idea_project\\zzw_springboot\\zzw-springboot");//设置9090端口tomcat.setPort(9090);//启动, 就会在9090端口监听tomcat.start();//等待请求接入System.out.println("====9090 等待请求接入====");tomcat.getServer().await();} catch (Exception e) {throw new RuntimeException(e);}}
}

2.debug运行一下, 这时会报错, 解决方案

在这里插入图片描述

refresh()负责注入初始化相关bean, 在未执行refresh方法前, spring容器是没有bean的

在这里插入图片描述

初始化后, 容器中有了bean文件

在这里插入图片描述

🥦完成测试

1.拿掉断点, 运行程序

2.浏览器请求 http://localhost:9090/zzwboot/hi
在这里插入图片描述

🥦注意事项和细节

1.如果启动包异常, 如下:
严重: Servlet [jsp] in web application [/zzwboot] threw load() exception
java.lang.ClassNotFoundException: org.apache.jasper.servlet.JspServlet

2.解决方案: 引入对应版本的jasper包即可, 修改E:\idea_project\zzw_springboot\zzw-springboot\pom.xml

<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jasper</artifactId><version>8.5.75</version>
</dependency>

Lombok

Lombok介绍

●Lombok介绍
1.简化JavaBean开发, 可以使用Lombok的注解让代码更加简洁. mybatis中学习过

2.Java项目中, 很多没有技术含量又必须存在的代码, POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些代码既没有技术含量,又影响着代码的美观,Lombok应运而生。

SpringBootIDEA官方支持
1.IDEA 2020已经内置了Lombok插件
2.SpringBoot 2.1.x之后的版本也在Starter中内置了Lombok依赖

Lombok常用注解

在这里插入图片描述

Lombok应用实例

代码实现

●需求说明
回到我们的 quickstart 项目, 使用Lombok简化Furn.java代码, 让代码简洁高效

1.在pom.xml中引入lombok
引入lombok, 版本在spring-boot-dependencies-2.5.3.pom已经指定了, 把光标放在lombok, ctrl+b 可以看到. springboot依赖管理

在这里插入图片描述

2.修改Furn.java, 使用Lombok注解简化代码. 提示: 可以通过idea自带的反编译功能, 看Furn.class的源代码


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

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

相关文章

【数据分析】推断统计学及Python实现

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 Python 初阶 Python–语言基础与由来介绍 Python–…

【SpringBoot】SpringBoot:实现文件上传和下载功能

文章目录 引言项目初始化添加依赖 配置文件存储位置实现文件上传功能创建文件上传控制器创建上传页面 实现文件下载功能创建文件下载控制器 安全性和最佳实践文件大小限制文件类型验证文件名和路径验证文件下载时的安全性 测试与部署示例&#xff1a;编写单元测试 部署结论 引言…

墨香戏韵,重塑经典

创意名称 墨香戏韵&#xff0c;重塑经典|基于AIGC对戏剧创新 创意概述 京剧作为中国传统戏曲之一&#xff0c;源远流长&#xff0c;承载了丰富的文化内涵和艺术特色。水墨画则是中国传统绘画的瑰宝&#xff0c;以其独特的墨色表达和极简的形式赢得了广泛的赞誉。我们的项目将…

docker-compose部署FastDFS分布式文件系统

文章目录 一、技术选型二、fastDFS组成部分三、docker-compose文件四、客户端nginx配置五、存储器spring Boot集成参考文献 一、技术选型 还有一个更好的google FS&#xff08;但是他不开源&#xff0c;我也没找到社区版一类的可以不要钱使用的&#xff09;。 最后考虑到我们存…

【字符串函数2】

5. strncpy 函数的使用和模拟实现 选择性拷贝 char * strncpy ( char * destination, const char * source, size_t num ); 1.拷贝num个字符从源字符串到目标空间。 2.如果源字符串的⻓度⼩于num&#xff0c;则拷⻉完源字符串之后&#xff0c;在⽬标的后边 追加0 &#…

语法04 C++ 标准输入语句

标准输入 使用格式&#xff1a;cin >> 输入的意思就是把一个值放到变量里面去&#xff0c;也就是变量的赋值&#xff0c;这个值是由我们自己输入的。 (注意:输入变量前要先定义&#xff0c;输入完之后要按Enter键。) 输入多个变量&#xff0c;与输出类似&#xff0c;…

unity数独游戏

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class MainMenuPanel : MonoBehaviour {public Button btnPlay; // 开始按钮public Slider sldDifficulty; // 难度滑动条private void Awake(){/…

springer 在线投稿编译踩坑

springer投稿&#xff0c;在线编译踩坑总结 注意&#xff1a; 有的期刊需要双栏&#xff0c;而预定义的模板中可能为单栏&#xff0c;需要增加iicol选项。 例如&#xff1a; \documentclass[sn-mathphys-num]{sn-jnl}% —>\documentclass[sn-mathphys-num, iicol]{sn-jnl}…

Matlab的Simulink系统仿真(simulink调用m函数)

这几天要用Simulink做一个小东西&#xff0c;所以在网上现学现卖&#xff0c;加油&#xff01; 起初的入门是看这篇文章MATLAB 之 Simulink 操作基础和系统仿真模型的建立_matlab仿真模型搭建-CSDN博客 写的很不错 后面我想在simulink中调用m文件 在 Simulink 中调用 MATLA…

远程问诊软件哪款好?选欣九康诊疗系统

近几年国家相继推出了支持发展“互联网医疗”的政策&#xff0c;如今随着相关政策的不断落实推进&#xff0c;市场上涌现出了一大批在线咨询、电子处方和远程问诊的医疗平台&#xff0c;而在面对种类如此繁多的医疗平台究竟选择哪款更好便成了医疗机构非常头疼的事情&#xff0…

数据中心布线管理:预标记线缆与移动扫描技术的融合

随着信息技术的飞速发展&#xff0c;数据中心布线管理面临着前所未有的挑战。传统的布线管理方式已无法满足现代数据中心高效、准确和可靠的需求。在这样一个背景下&#xff0c;预标记线缆与移动扫描技术的结合&#xff0c;为数据中心布线管理带来了革命性的解决方案。 布线管理…

港理工最新综述:基于LLM的text-to-SQL调查(方法实验数据全面梳理)1

【摘要】文本到SQL旨在将自然语言问题转换为可执行的SQL语句,这对用户提问理解、数据库模式理解和SQL生成都是一个长期存在的挑战。传统的文本到SQL系统包括人工工程和深度神经网络。随后,预训练语言模型(PLMs)被开发并用于文本到SQL任务,取得了可喜的成绩。随着现代数据库变得…

B站画质补完计划(3):智能修复让宝藏视频重焕新生

1 老片存在什么画质问题&#xff1f; B站作为一个拥有浓厚人文属性的平台社区&#xff0c;聚集了诸如《雍正王朝》、《三国演义》等经典影视剧集&#xff0c;同时也吸引了大量用户欣赏、品鉴这些人文经典 。但美中不足的是&#xff0c;由于拍摄年代久远、拍摄设备落后、数据多次…

神舟电脑文件误删怎么办?这些恢复方法助你轻松解决

神舟电脑文件误删怎么办&#xff1f;在信息爆炸的时代&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的重要工具。然而&#xff0c;有时我们会因为一些不小心的操作&#xff0c;误删了电脑中的重要文件&#xff0c;尤其是在使用神舟电脑这类高性能设备时&#xff0c;文…

怎么做出用于收集信息的二维码?采集信息的表单二维码制作技巧

现在二维码在日常生活中的应用越来越广泛&#xff0c;经常被用来展示多种内容展示&#xff0c;比如视频、图片、文件、音频、文本等内容&#xff0c;都可以通过生成二维码的方式来为其他人提供相关信息的展示。二维码除了有展示内容的用途外&#xff0c;现在很多人也会生成表单…

短剧分销小程序:影视产业链中的新兴力量

一、引言 在数字化浪潮的推动下&#xff0c;影视产业正迎来一场深刻的变革。短剧分销小程序作为这场变革中的新兴力量&#xff0c;正以其独特的魅力和价值&#xff0c;逐渐在影视产业链中崭露头角。本文将探讨短剧分销小程序在影视产业链中的新兴地位、其带来的变革以及未来的…

【枚举】564. 寻找最近的回文数

本文涉及知识点 枚举 LeetCode564. 寻找最近的回文数 给定一个表示整数的字符串 n &#xff0c;返回与它最近的回文整数&#xff08;不包括自身&#xff09;。如果不止一个&#xff0c;返回较小的那个。 “最近的”定义为两个整数差的绝对值最小。 示例 1: 输入: n “123”…

【日记】常去的那家饭馆转让了……(399 字)

正文 晚上吃饭&#xff0c;常去的那家饭馆门市转让了&#xff0c;不做了。sad。 不久之前&#xff0c;临近下班了&#xff0c;我忘了报一个表给副行长&#xff0c;那时候系统已经进不去了&#xff0c;查不了数据。于是我随便转发了一张以前的日报表给他&#xff0c;日期都没改。…

长亭雷池部署

安雷池推荐环境 我这个网站的1804也就是18.04 Index of /ubuntu-releases/ (163.com) 然后照着ppt做就行自带ubunutu 我是默认安装不是简易安装 之前在自己的虚拟机docker上试过安装github上的雷池 现在重新安装了ubuntu 在我的ubuntu上安装一个雷池 这是长亭的学习计划 …

Elasticsearch 认证模拟题 - 19

一、题目 使用异步查询 task1 索引&#xff0c;并且 找到 miantable_name 字段值为 name8 的文档 1.1 考点 异步搜索 1.2 答案 POST task1/_async_search {"query": {"term": {"miantable_name": {"value": "name8"}}}…