手写模拟SpringBoot核心流程

1、创建工程

创建一个工程,包含springboot和user两个module
在这里插入图片描述

  • springboot模块,表示springboot框架的源码实现
  • user包,表示用户业务系统,用来写业务代码来测试我们所模拟出来的SpringBoot

1.1、pom依赖

SpringBoot是基于的Spring,所以我们要依赖Spring,然后我希望我们模拟出来的SpringBoot也支持Spring MVC的那一套功能,所以也要依赖Spring MVC,包括Tomcat等,所以在SpringBoot模块中要添加以下依赖

<dependencies>
	<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.18</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.60</version>
        </dependency>
</dependencies>

在User模块只添加我们创建的SpringBoot的依赖:

<dependencies>
	<dependency>
		<groupId>org.example</groupId>
		<artifactId>springboot</artifactId>
		<version>1.0-SNAPSHOT</version>
	</dependency>
</dependencies>

定义相关Controller和Service
在这里插入图片描述

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("test")
    public String test(){
        return userService.test();
    }
}
@Service
public class UserService {

    public String test() {
        return "tacy";
    }
}

1.2、核心注解和核心类

在真正使用SpringBoot时,核心会用到SpringBoot一个类和注解:

  • @SpringBootApplication,这个注解是加在应用启动类上的,也就是main方法所在的类
  • SpringApplication,这个类中有个run()方法,用来启动SpringBoot应用的

现在也来模拟实现他们。
@TacySpringBootApplication注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
public @interface TacySpringBootApplication {
}

用来实现启动逻辑的TacySpringApplication类:

public class TacySpringApplication {

    public static void run(Class clazz) {
    }
}

**在MyApplication中来使用: **

@TacySpringBootApplication
public class UserApplication {

    public static void main(String[] args) {
        TacySpringApplication.run(UserApplication.class);
    }
}

目前只是壳子出来了,还不能跑起来

1.3、run方法

目标: 希望run方法一旦执行完,就能在浏览器中访问到UserController,那势必在run方法中要启动Tomcat,通过Tomcat就能接收到请求了。

在SpringMVC中有一个Servlet非常核心,那就是DispatcherServlet,这个DispatcherServlet需要绑定一个Spring容器,因为DispatcherServlet接收到请求后,就会从所绑定的Spring容器中找到所匹配的Controller,并执行所匹配的方法。

所以,在run方法中,我们要实现的逻辑如下:

  • 创建一个Spring容器
  • 创建Tomcat对象
  • 生成DispatcherServlet对象,并且和前面创建出来的Spring容器进行绑定
  • 将DispatcherServlet添加到Tomcat中
  • 启动Tomcat

1.4、创建Spring容器

public class TacySpringApplication {

    public static void run(Class clazz) {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(clazz);
        applicationContext.refresh();
    }
}
  • 创建的是一个AnnotationConfigWebApplicationContext容器,
  • 把run方法传入进来的class作为容器的配置类,比如在UserApplication的run方法中,我们就是把UserApplication.class传入到了run方法中,最终UserApplication就是所创建出来的Spring容器的配置类
  • 并且由于UserApplication类上有@TacySpringBootApplication注解, @TacySpringBootApplication注解的定义上又存在@ComponentScan注解,所以AnnotationConfigWebApplicationContext容器在执行refresh时,就会解析UserApplication这个配置类,从而发现定义了@ComponentScan注解,也就知道了要进行扫描,只不过扫描路径为空,而AnnotationConfigWebApplicationContext容器会处理这种情况,如果扫描路径会空,则会将UserApplication所在的包路径做为扫描路径,从而就会扫描到UserService和UserController。
  • Spring容器创建完之后,容器内部就拥有了UserService和UserController这两个Bean。

1.5、启动Tomcat

使用内嵌的tomcat, Embed-Tomcat

public static void startTomcat(WebApplicationContext applicationContext){
	
	Tomcat tomcat = new Tomcat();
	
	Server server = tomcat.getServer();
	Service service = server.findService("Tomcat");
	
	Connector connector = new Connector();
	connector.setPort(8081);
	
	Engine engine = new StandardEngine();
	engine.setDefaultHost("localhost");
	
	Host host = new StandardHost();
	host.setName("localhost");
	
	String contextPath = "";
	Context context = new StandardContext();
	context.setPath(contextPath);
	context.addLifecycleListener(new Tomcat.FixContextListener());
	
	host.addChild(context);
	engine.addChild(host);
	
	service.setContainer(engine);
	service.addConnector(connector);
	
	tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext));
	context.addServletMappingDecoded("/*", "dispatcher");
	
	try {
		tomcat.start();
	} catch (LifecycleException e) {
		e.printStackTrace();
	}
	
}
  • 配置了Tomcat绑定的端口为8081,
  • 向当前Tomcat中添加了DispatcherServlet,并设置了一个Mapping关系,最后启动
  • 在构造DispatcherServlet对象时,传入了一个ApplicationContext对象,也就是一个Spring容器,前文说的,DispatcherServlet对象和一个Spring容器进行绑定。
  • 在run方法中,调用startTomcat:
    public static void run(Class clazz) {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(clazz);
        applicationContext.refresh();

        startTomcat(applicationContext);
    }

现在运行Application,就能正常的启动项目,并能接收请求
在这里插入图片描述
在浏览器上访问:http://localhost:8081/test
在这里插入图片描述

1.6、实现Tomcat和Jetty的切换

上面已经实现了一个简单的SpringBoot,接下来继续扩充,实现以下需求:

  • 如果项目中Tomcat的依赖,就启动Tomcat
  • 如果项目中Jetty的依赖,就启动Jetty
  • 如果两者都没有就报错
  • 如果两者都有也报错

需求目标: 希望SpringBoot自动帮我实现,对于程序员用户而言,只要在Pom文件中添加相关依赖就可以了,想用Tomcat就加Tomcat依赖,想用Jetty就加Jetty依赖

定义一个接口,WebServer, 在这个接口中定义一个start方法

public interface WebServer {

    public void start();
}

再针对Tomcat和Jetty提供2个实现类

public class TomcatWebServer implements WebServer{

    @Override
    public void start() {
        System.out.println("启动Tomcat");
    }
}
public class TomcatWebServer implements WebServer{

    @Override
    public void start() {
        System.out.println("启动Jetty");
    }
}

在TacySpringApplication中的run方法中,去获取对应的WebServer,然后启动对应的webServer,代码为:

public static void run(Class clazz){
	AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
	applicationContext.register(clazz);
	applicationContext.refresh();
	
	WebServer webServer = getWebServer(applicationContext);
	webServer.start();
	
}

public static WebServer getWebServer(ApplicationContext applicationContext){
	return null;
}

1.7、模拟实现条件注解

实现一个条件注解@TacyConditionalOnClass

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(TacyOnClassCondition.class)
public @interface TacyConditionalOnClass {
    String value() default "";
}

真正实现条件逻辑的是@Conditional(TacyOnClassCondition.class)中的TacyOnClassCondition

public class TacyOnClassCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(TacyConditionalOnClass.class.getName());
        String className = (String) annotationAttributes.get("value");

        try {
            context.getClassLoader().loadClass(className);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
  • 拿到@TacyConditionalOnClass中的value属性
  • 用类加载器进行加载
  • 加载到了所指定的类,就符合条件
SpringBoot手写简易流程可以通过以下几个步骤来实现: 1. 创建一个新的Java项目,并导入所需的依赖。这些依赖可以包括SpringBoot核心模块、Web模块以及其他你需要的模块。 2. 创建一个主应用程序类,通常命名为`MyApplication`。这个类需要使用`@SpringBootApplication`注解标记,以指示它是SpringBoot应用程序的入口。 3. 在主应用程序类中,可以定义一些需要的配置和组件。例如,你可以使用`@Configuration`注解创建一个配置类,使用`@Bean`注解创建一些Bean实例。 4. 编写业务代码。在这个例子中,你可以创建一个`User`类来表示用户,并且定义一些操作用户的方法。 5. 启动应用程序。可以通过在主应用程序类中添加一个`main`方法,并在该方法中调用`SpringApplication.run(MyApplication.class, args)`来启动应用程序。 6. 测试应用程序。可以编写一些测试类,使用JUnit或其他测试框架来测试你编写的业务代码。 这样,你就可以手写一个简易的SpringBoot应用程序。通过编写配置、组件和业务代码,并启动应用程序来测试它。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [学习SpringBoot源码之手写一个简易版SpringBoot](https://blog.csdn.net/xjx666666/article/details/128205845)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值