分布式微服务: 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对象
     */
    @Bean
    public 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容器
    @Bean
    public 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 {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {

        System.out.println("startup ...");

        //加载Spring web application configuration => 容器
        //自己实现的Spring容器叫做 ZzwSpringApplicationContext
        AnnotationConfigWebApplicationContext 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启动时,加载 dispatcherServlet
        registration.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的源代码

@Component
@ToString //编译时, 生成toString方法, 默认情况下, 会生成一个无参构造器
@ConfigurationProperties(prefix = "furn01")
public class Furn {
    private Integer id;
    private String name;
    private BigDecimal price;
}

在这里插入图片描述

3.@Data注解

@Component
@ConfigurationProperties(prefix = "furn01")
/**
 * 说明:
 * 1. Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
 * 2. @Data 注解等价使用了 如下注解
 * @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode
 * 3. @RequiredArgsConstructor, 单独地说一下
 */
@Data
public class Furn {
    private Integer id;
    private String name;
    private BigDecimal price;
}

编译后

在这里插入图片描述

4.@RequiredArgsConstructor详解
在我们写controller或者Service层的时候,需要注入很多的mapper接口或者另外的service接口,这时候就会写很多的@Autowired注解,代码看起来很乱
lombok提供了一个注解:

@RequiredArgsConstructor(onConstructor = @_(@Autowired))
写在类上可以代替@Autowired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解

在这里插入图片描述

5.@NoArgsConstructor详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在向量数据库中存储多模态数据,通过文字搜索图片

在向量数据中存储多模态数据&#xff0c;通过文字搜索图片&#xff0c;Chroma 支持文字和图片&#xff0c;通过 OpenClip 模型对文字以及图片做 Embedding。本文通过 Chroma 实现一个文字搜索图片的功能。 OpenClip CLIP&#xff08;Contrastive Language-Image Pretraining&…

课设--学生成绩管理系统(一)

欢迎来到 Papicatch的博客 文章目录 &#x1f349;技术核心 &#x1f349;引言 &#x1f348;标识 &#x1f348;背景 &#x1f348;项目概述 &#x1f348; 文档概述 &#x1f349;可行性分析的前提 &#x1f348;项目的要求 &#x1f348;项目的目标 &#x1f348;…

Java入门4: 泛型和集合

Java入门4: 泛型和集合 MangoGO 芒狗狗 目录 4 泛型和集合4.1 泛型4.2 Collection4.3 List4.4 ArrayList4.5 Map4.6 HashMap4.7 Set 和 HashSet4.8 Collections参考代码4 泛型和集合 Java 使用集合来组织和管理对象,本节我们重点讲解泛型和集合。主要介绍 Collection、List、A…

C#医院体检系统源码 PEIS源码 系统核心功能、特点、类型、设备对接-PACS放射科设备对接:DR、CT、MRI、钼靶。

C#医院体检系统源码 PEIS源码 系统核心功能、特点、类型、设备对接-PACS放射科设备对接:DR、CT、MRI、钼靶。 体检系统是为体检中心、医院体检科等体检机构专门开发的全流程管理系统。该系统通过软件实现检测仪器数据的自动提取&#xff0c;内置多级医生工作台&#xff0c;旨在…

远程连接服务器的工具?

远程连接服务器工具是现代工作环境中不可或缺的工具之一。它允许用户通过网络远程访问和控制远程服务器&#xff0c;为用户提供了更加便捷和高效的工作方式。无论是远程办公、远程维护还是云计算&#xff0c;远程连接服务器工具都发挥着重要的作用。 在众多远程连接服务器工具…

LabVIEW RT在非NI硬件上的应用与分析

LabVIEW RT&#xff08;实时操作系统&#xff09;可运行在非NI&#xff08;National Instruments&#xff09;硬件上&#xff0c;如研华工控机&#xff0c;但需要满足特定硬件要求。本文从硬件要求、开发和运行差异、可靠性、稳定性、优势和成本等多角度详细分析在非NI硬件上运…

【Mac】Luminar Neo for mac(图像编辑软件)软件介绍及同类型软件比较

Luminar Neo软件介绍 Luminar Neo 是一款由 Skylum 开发的功能强大的照片编辑软件&#xff0c;专为摄影师和摄影爱好者设计。它适用于 Mac 和 Windows 平台&#xff0c;提供了一系列先进的编辑工具和功能&#xff0c;使用户能够轻松提升和优化他们的照片。以下是 Luminar Neo …

沸点 | LDBC与SIGMOD联合研讨,推动图数据库创新与标准化

当地时间6月9日&#xff0c;国际基准官方平台关联数据基准委员会&#xff08;LDBC&#xff0c;Linked Data Benchmark Council&#xff09;与SIGMOD 2024&#xff08;是全球最具国际影响力的数据管理、数据处理和数据存储领域的学术顶会之一&#xff0c;ACM SIGMOD/Big Data in…

非关系型数据库NoSQL数据层解决方案 之 redis springboot整合与读写操作 2024详解以及window版redis5.0.14下载百度网盘

redis下载安装以及基本使用 下载地址 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;0410 一个名对应一个数值 内存级 在内存里进行操作 准备启动 我们现在就有一个redis客户端的服务器了 我们再启动一个cmd 操作redis数据库 redis里面的基本数据类型有五种 …

用Canvas绘制2D平面近大远小的马路斑马线

用Canvas绘制2D平面近大远小的马路斑马线 设置canvas和上下文&#xff1a; 首先&#xff0c;你需要创建一个元素&#xff0c;并获取其2D渲染上下文。 绘制斑马线&#xff1a; 使用fillRect或strokeRect方法绘制斑马线。你可以通过循环和计算来绘制多条具有不同宽度和间隔的…

LeetCode20.有效的括号

题目描述 分析 我们刚上来的思路可能是&#xff1a;找出这三种括号的个数 如果都是偶数 说明匹配 但是这里还有一个顺序问题 比如 " )( "这样是不匹配的&#xff01; 所以这种思路不可取&#xff01; 我们想 如果遇到左括号&#xff0c;把他读到一个顺序表中&#…

等级考试3-2021年3月题

作业&#xff1a; #include <iostream> using namespace std; int chonghe(int,int,int,int); int main(){int a[1000],b[1000];int n,ma0;cin>>n;for(int i0;i<n;i){cin>>a[i]>>b[i];}for(int i0;i<n;i){for(int ji1;j<n;j){mamax(ma,chongh…

Python酷库之旅-比翼双飞情侣库(10)

目录 一、xlrd库的由来 二、xlrd库优缺点 1、优点 1-1、支持多种Excel文件格式 1-2、高效性 1-3、开源性 1-4、简单易用 1-5、良好的兼容性 2、缺点 2-1、对.xlsx格式支持有限 2-2、功能相对单一 2-3、更新和维护频率低 2-4、依赖外部资源 三、xlrd库的版本说明 …

4.8.2 利用Spark SQL计算总分与平均分

姓名语文数学英语物理化学陈燕文8998807665张晓峰9078928456李太白8793677892洪小琳9867879076 1. 准备数据 创建本地成绩文件&#xff1a;scores.txt&#xff0c;包含学生成绩数据。上传到 HDFS&#xff1a; 创建目录&#xff1a;hdfs dfs -mkdir -p /scoresumavg/input上传文…

板凳------56.Linux/Unix 系统编程手册(下) -- SOCKET 介绍

56.1.概述 socket 是一种IPC方法&#xff0c;允许位于同一主机或使用网络连接起来的不同主机上的应用程序之间交换数据。 UNIX 允许位于同一主机系统上的应用程序之间通信 Internet domain IPv4 and IPV6 // socket 通信方式 1.各个应用程序创建一个socket&#xff0c;socket是…

块级元素与行内元素详解

在网页设计与开发中&#xff0c;元素根据其在页面布局中的表现可分为两大类&#xff1a;块级元素&#xff08;Block-level Elements&#xff09;和行内元素&#xff08;Inline Elements&#xff09;。理解它们的特性和使用规则对于构建结构清晰、布局合理的网页至关重要。 块级…

【因果推断python】38_预测模型1

目录 工业界中的机器学习 之前的部分涵盖了因果推理的核心。那里的技术是众所周知和成熟的。他们经受住了时间的考验。第一部分建立了我们可以依赖的坚实基础。用更专业的术语来说&#xff0c;第一部分侧重于定义什么是因果推理&#xff0c;哪些偏差会阻止相关性成为因果关系&…

高考分数线一分一段统计汇总——使用SQL窗口函数

高考分数线一分一段统计汇总——使用SQL窗口函数 select 总分数&#xff0c; 一分一段人数&#xff0c; sum(一分一段人数) over( order by 总分数 desc) as 累计排名 from( select 总分数&#xff0c; count(考生号) as 一分一段人数 from &#xff08; select 考生号…

网络编程(四)

一、使用wireshark抓包分析协议头 &#xff08;一&#xff09;wireshark常用的过滤语句 tcp.port <想要查看的端口号> ip.src <想要查看的源IP地址> ip.dest <想要查看的目的IP地址> ip.addr <想要查看的IP地址>&#xff08;二&#xff09;抓包分…

【Java】解决Java报错:InterruptedException in Multi-threaded Applications

文章目录 引言一、InterruptedException的定义与概述1. 什么是InterruptedException&#xff1f;2. InterruptedException的常见触发场景3. 示例代码 二、解决方案1. 正确处理InterruptedException2. 合理使用中断机制3. 使用更高层次的并发工具 三、最佳实践1. 避免吞掉Interr…