EasySpring-Boot(一) 手写Springboot 适合新手的小项目 启动类注解 看这个就够了 源代码 内嵌Tomcat 自动配置 自动专配多模态自动配置类

0 EasySpring-Boot (一)

项目简介:
EasySpring-Boot是一个简易版的Spring Boot框架的复现,旨在帮助开发者更好地理解Spring Boot框架的核心原理和功能。通过实现基本的依赖注入、自动配置和Web功能,EasySpring-Boot展示了一个简单的应用程序框架的搭建过程。
在EasySpring-Boot中,实现了一个简单的应用上下文(MyApplicationContext)来管理Bean的注册和依赖注入,以及一个简单的配置类(MyConfiguration)来定义Bean和自动配置。通过这个简易版的Spring Boot框架,开发者可以更深入地了解Spring框架的工作原理,并在此基础上进行扩展和定制化开发。
EasySpring-Boot项目旨在帮助初学者和开发者更好地理解Spring Boot框架的实现原理,同时也可以作为学习和实践Spring框架的一个简单示例项目。源代码会在后续发出

1. 项目准备

1.1 原始项目依赖引入

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.23</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.3.23</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.3.23</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.3.23</version>
</dependency>

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

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

1.1.1 依赖解析

  1. org.springframework:spring-context:5.3.23:Spring框架的核心容器,提供IoC(控制反转)和DI(依赖注入)功能,包括BeanFactory、ApplicationContext等。
  2. org.springframework:spring-web:5.3.23:Spring Web模块,提供构建Web应用程序的基本功能和特性,如Web框架、RESTful服务等。
  3. org.springframework:spring-webmvc:5.3.23:Spring MVC模块,提供基于MVC(模型-视图-控制器)架构的Web应用程序开发支持,用于构建Web应用程序的控制器和视图。
  4. org.springframework:spring-aop:5.3.23:Spring AOP模块,提供面向切面编程的支持,用于实现横切关注点的模块化,如事务管理、日志记录等。
  5. javax.servlet:javax.servlet-api:3.1.0:Java Servlet API,用于支持开发基于Servlet的Web应用程序,定义了Servlet容器和Servlet规范。
  6. org.apache.tomcat.embed:tomcat-embed-core:9.0.65:Tomcat嵌入式核心,用于在应用程序中嵌入Tomcat容器,方便开发和测试Web应用程序。

1.1.2 依赖实现

当使用这些依赖时,可以通过以下方式来展示每个依赖的具体用途:

  1. org.springframework:spring-context:5.3.23:
    • 例子:在Spring应用程序中创建并管理Bean
    • 代码示例:
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}
  1. org.springframework:spring-web:5.3.23:
    • 例子:创建一个简单的Spring Web应用程序
    • 代码示例:
@Controller
public class MyController {
    @RequestMapping("/")
    public String home() {
        return "index";
    }
}
  1. org.springframework:spring-webmvc:5.3.23:
    • 例子:使用Spring MVC构建一个基于MVC架构的Web应用程序
    • 代码示例:
@Controller
public class MyController {
    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello, Spring MVC!");
        return "hello";
    }
}
  1. org.springframework:spring-aop:5.3.23:
    • 例子:使用Spring AOP实现日志记录
    • 代码示例:
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}
  1. javax.servlet:javax.servlet-api:3.1.0:
    • 例子:创建一个Servlet处理HTTP请求
    • 代码示例:
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("Hello, Servlet!");
    }
}
  1. org.apache.tomcat.embed:tomcat-embed-core:9.0.65:
    • 例子:在Spring Boot应用中嵌入Tomcat容器
    • 代码示例:无需特定代码示例,Spring Boot会自动嵌入Tomcat容器并运行应用程序。

1.2 项目结构

image.pngimage.png
这里user模块已经装配了我们模拟的EasySpringBoot依赖

<artifactId>user</artifactId>

<properties>
  <maven.compiler.source>11</maven.compiler.source>
  <maven.compiler.target>11</maven.compiler.target>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
  <dependency>
    <groupId>com.bruan</groupId>
    <artifactId>EasySpringBoot</artifactId>
    <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>

2. 手写基础SpringBoot

创建SpringBootApplication注解 启动注解

首先需要模仿SpringBoot创建SpringBootApplication,我们给他一个自己的注解名称,我称之为:BrBootApplication

创建注解

image.png

  1. @Target(ElementType.TYPE):这个注解表示BrBootApplication注解可以应用在类上。即,这个自定义注解可以用来标记类。
  2. @Retention(RetentionPolicy.RUNTIME):这个注解表示BrBootApplication注解在运行时保留,这意味着这个自定义注解可以在运行时通过反射获取到。
  3. @Documented:这个注解表示BrBootApplication注解应该被 javadoc工具记录。当使用 javadoc 工具生成文档时,会包含这个注解的信息。
  4. @Inherited:这个注解表示BrBootApplication注解可以被子类继承。如果一个类使用了BrBootApplication注解,而这个类的子类没有使用其他的类级别的注解,那么子类也会继承这个注解。

这段代码定义了一个自定义注解BrBootApplication,该注解可以应用在类上,在运行时保留,并且可以被 javadoc 工具记录。同时,如果一个类使用了BrBootApplication注解,它的子类也会继承这个注解。这样的自定义注解可以用来标记特定的类,并在需要时通过反射获取注解信息。

user类使用该注解

不管如何,虽然该启动注解还没完善,但是我们已经可以使用该注解了,如下图:
image.png

模拟SpringBootApplication主类

运行逻辑

真正的SpringBoot框架已经帮我们实现了一个类,在启动类中,我们其实就是调用该类的Run方法,如下:
image.png
这段代码是一个典型的Spring Boot 应用程序的入口类,每部分的作用如下:

  1. **@SpringBootApplication:**这是一个Spring Boot提供的注解,它整合了多个注解,包括@Configuration、@EnableAutoConfiguration和@ComponentScan。这个注解的作用是标识这个类是Spring Boot应用程序的入口点,并且会自动扫描当前包及其子包中的组件。
  2. **public class TliasApplication:**这是定义的一个Java类,类名为TliasApplication,这个类是整个应用程序的入口点。
  3. **public static void main(String[] args):**这是Java程序的入口方法,当应用程序启动时,会首先执行这个方法。args参数是命令行参数,可以在启动应用程序时传入。
  4. **SpringApplication.run(TliasApplication.class, args):**这是Spring Boot提供的静态方法,用于启动Spring Boot应用程序。它接受两个参数,第一个参数是应用程序的主类(即包含@SpringBootApplication注解的类),第二个参数是命令行参数。调用这个方法会启动Spring Boot应用程序,并自动配置应用程序所需的环境。

综上所述,这段代码定义了一个Spring Boot应用程序的入口类,通过@SpringBootApplication注解标识这是一个Spring Boot应用程序,并在main方法中调用SpringApplication.run方法启动应用程序。这个类的目的是启动整个应用程序的运行。

模拟运行逻辑

这里我们定义一个类BRSpringbootApplication用于模拟主类,实现各种方法
image.png
现在我们可以使用该方法了,尽管逻辑还没写
image.png

run方法作用

思考一下执行完run() 会出现什么结果呢?
SpringApplication.run(TliasApplication.class, args) 方法后,会触发Spring Boot应用程序的启动过程。具体来说,会发生以下几个重要的步骤:

  1. Spring Boot应用程序会启动内嵌的Tomcat服务器(默认情况下)或者其他内嵌的Servlet容器。
  2. Spring Boot会自动扫描并加载应用程序中的所有组件(被@ComponentScan注解标识的类)。
  3. Spring Boot会自动配置应用程序的环境,包括加载配置文件、处理依赖注入、启用AOP等。
  4. Spring Boot会根据类路径下的依赖项自动配置应用程序,例如自动配置数据库连接、Web MVC等。
  5. 最终,应用程序会成功启动并监听指定的端口,等待处理HTTP请求或其他事件。

SpringBootApplication启动Tomcat

模拟运行Tomcat

public class BrSpringbootApplication {
    /**
     * 模拟run方法
     * @param clazz
     */
    public static void run(Class clazz){
        //启动Tomcat
        startTomcat();

        
    }

    private static void startTomcat() {
        //使用Java代码启动
        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");

        StandardHost host = new StandardHost();
        host.setName("localhost");
        
        String contextPath = "";
        StandardContext context = new StandardContext();
        
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());
        
        host.addChild(context);
        engine.addChild(host);
        
        service.setContainer(engine);
        service.addConnector(connector);
        
        try {
            tomcat.start();
        }catch (LifecycleException e){
            e.printStackTrace();
        }
    }

}

这段代码是一个方法startTomcat(),用于通过Java代码启动Tomcat服务器。让我解释一下这段代码的逻辑:

  1. 创建Tomcat实例:首先创建了一个Tomcat实例,表示要启动Tomcat服务器。
  2. 获取Tomcat服务器相关组件:通过Tomcat实例获取Server、Service等组件,用于配置Tomcat服务器。
  3. 配置连接器(Connector):创建一个Connector实例,并设置端口为8081,用于监听来自客户端的请求。
  4. 配置引擎(Engine)和主机(Host):创建StandardEngine实例表示Tomcat的引擎,设置默认主机为localhost;创建StandardHost实例表示Tomcat的主机,设置主机名称为localhost。
  5. 配置上下文(Context):创建StandardContext实例表示Tomcat的上下文,设置上下文路径为""(空字符串),并添加一个生命周期监听器Tomcat.FixContextListener。
  6. 组装组件关系:将上下文添加到主机上,将主机添加到引擎上,将引擎设置为服务的容器,同时将连接器添加到服务中。
  7. 启动Tomcat服务器:调用tomcat.start()方法启动Tomcat服务器,如果启动过程中出现异常(LifecycleException),则捕获并打印异常信息。

现在调用该类的run方法我们就可以启动Tomcat了,运行user中的调用如下:
image.png

路径参数解析

其实上面的代码,我们写的Tomcat确实运行了,但是还不可以达到我们需要的参数解析的效果,即通过你在浏览器输入的**/test 然后自动定位到我们后端应该执行的Controller,应该加上这两行**

//路径参数解析
tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet(applcationContext));
context.addServletMappingDecoded("/*","dispatcher"); 

这段代码是用于配置路径参数解析的部分,让我解释一下:

  1. tomcat.addServlet(contextPath, “dispatcher”, new DispatcherServlet(applicationContext));:这行代码的作用是向Tomcat服务器的指定上下文(contextPath)中添加一个名为"dispatcher"的Servlet,并指定Servlet实例为DispatcherServlet,构造函数参数为applicationContext。这里的DispatcherServlet通常是Spring MVC框架中的前端控制器,用于接收请求并将其分发到对应的处理器。
  2. context.addServletMappingDecoded(“/“, “dispatcher”);:这行代码的作用是将上一步添加的名为"dispatcher"的Servlet映射到指定的路径模式”/”。这意味着所有经过该上下文的请求都会被该Servlet处理,实现了路径参数的解析和分发功能。

也就是说,这段代码配置了一个名为"dispatcher"的Servlet,并将其映射到处理所有路径的模式"/*",实现了路径参数的解析和分发功能。通过这样的配置,请求会被DispatcherServlet处理并交由Spring MVC框架进行进一步处理,实现了请求的路由和控制。

DispatcherServlet

DispatcherServlet是Spring MVC框架中的核心组件之一,它充当了前端控制器的角色,负责接收客户端请求、委派请求处理、渲染视图等工作。
上面我们用到了该类,现在我们需要搞清楚入参applcationContext到底是什么东西

tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet(applcationContext));

我们进入该函数:
image.png
可以看到该函数接收一个Spring容器的入参,通过该容器DispatcherServlet才能对拦截到的路径请求进行映射,将其映射到SpringMVC中的controller中,也就是可以找到的Mapping
image.png

完善代码 传入容器

只需在运行该方法时加入一行,即可
image.png
完整代码如下:

public class BrSpringbootApplication {
    /**
     * 模拟run方法
     * @param clazz
     */
    public static void run(Class clazz){
        //创建Spring容器 空容器没有配置
        AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
        //配置容器 传入配置类
        webApplicationContext.register(clazz);
        //启动容器
        webApplicationContext.refresh();

        //启动Tomcat
        startTomcat(webApplicationContext);
    }

    private static void startTomcat(WebApplicationContext webApplicationContext) {
        //使用Java代码启动
        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");

        StandardHost host = new StandardHost();
        host.setName("localhost");

        String contextPath = "";
        StandardContext 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(webApplicationContext));
        context.addServletMappingDecoded("/*","dispatcher");


        try {
            tomcat.start();
        }catch (LifecycleException e){
            e.printStackTrace();
        }
    }

}

从外部调用来看
image.png

组件扫描ComponentScan

上面说到,启动类调用 BrSpringbootApplication.run(MyApplication.class); 时,会扫描传入类中的Bean及其注解,我们主要看注解中的内容,如下:
image.png
可以看到里面有组件扫描注解,于是我们先照猫画虎,给我们自己之前的模拟注解也加上该注解@ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ComponentScan
public @interface BrBootApplication {

}

如果不指定该注解的扫描路径,那么默认扫描当前包及其子包
image.png
可以看到controller也位于当前包及其子包中,所以我们可以得到controller这个bean,便于后续进行Tomcat映射,至此启动项目,并且到浏览器输入访问端口,即可得到映射结果,如图:
image.png

手写多模态Webserver

鉴于有的用户需要不同的WebServer,我们需要给用户不同的选择,SpringBoot也考虑到了这一点,但是其并不是使用if进行判断,而是抽取出接口进行判断,那么我们要怎么做呢?

  1. 首先需要抽取出接口

image.png

  1. 导入依赖到pom文件,以Tomcat和jetty为例
<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-server</artifactId>
  <version>9.4.46.v20220331</version>
</dependency>
  1. 完善各自的实现类,以Tomcat和jetty为例

  2. 我们的启动类也要改变,不能再用之前的startTomcat()了,而是获取Webserver,采用多态的方法调用Start

/**
     * 获取Webserver
     * @param webApplicationContext
     * @return
     */
private static WebServer getWebserver(WebApplicationContext webApplicationContext) {

//判断需要哪种webserver 
//获取Bean
Map<String, WebServer> beansOfType = webApplicationContext.getBeansOfType(WebServer.class);
//判断错误情况
if (beansOfType.size() ==0){
    throw  new NullPointerException();
}
if (beansOfType.size()>1){
    throw new IllegalStateException();
}

return beansOfType.values().stream().findFirst().get();
}

这段代码是用于获取WebServer bean 的方法,让我解释一下:

  1. 首先,通过webApplicationContext.getBeansOfType(WebServer.class)方法获取所有类型为WebServer的bean,并将其存储在Map<String, WebServer> beansOfType中。
  2. 然后,通过判断beansOfType的大小,如果没有找到WebServer类型的bean,则抛出NullPointerException异常;如果找到多个WebServer类型的bean,则抛出IllegalStateException异常。
  3. 最后,通过beansOfType.values().stream().findFirst().get()来获取第一个WebServer类型的bean并返回。这里为什么要使用.stream().findFirst().get()的方式呢?
    • 使用.stream()将Map中的值转换为流(Stream)。
    • 使用.findFirst()获取流中的第一个元素。
    • 使用.get()将Optional对象中的值取出,因为findFirst()返回的是一个Optional对象,通过get()方法取出实际的值。

为什么不能直接返回 beansOfType.values()呢?其实是可以直接返回beansOfType.values(),但是要注意返回的是一个Collection而不是单个WebServer对象。如果你的业务逻辑需要获取所有WebServer对象的集合,那么直接返回beansOfType.values()是可以的。但是在这段代码中,方法的定义是返回一个单个WebServer对象,而不是集合。因此,在这种情况下,我们需要使用.stream().findFirst().get()的方式来确保我们返回的是第一个WebServer对象,而不是整个集合。
至此已经可以实现手动定义Bean来进行Webserver的选取了:

package com.bruan;


import com.bruan.webserver.impl.TomcatWebserver;
import org.springframework.context.annotation.Bean;

@BrBootApplication
public class MyApplication {

    // 通过定义该bean 表示我们要选择tomcat
    @Bean
    public TomcatWebserver tomcatWebserver() {
        return new TomcatWebserver();
    }

    public static void main(String[] args) {
        System.out.println("程序启动");
        BrSpringbootApplication.run(MyApplication.class);
    }
}

image.png
或者

@BrBootApplication
public class MyApplication {

    // 通过定义该bean 表示我们要选择jetty
    @Bean
    public jettyWebserver jettyWebserver(){
        return new jettyWebserver();
    }

    public static void main(String[] args) {
        System.out.println("程序启动");
        BrSpringbootApplication.run(MyApplication.class);
    }
}

image.png

模拟WebServer自动配置类

鉴于上述的手写bean进行模式选择太过于麻烦,并且使用我们框架的程序员不一定知道上述两个类的存在,也就不知道怎么去定义对应的Bean,总而言之,过于麻烦,所以我们考虑使用自动配置进行处理

  1. 定义自动配置类

image.png

package com.bruan.webserver.autoConfiguration;

import com.bruan.webserver.impl.TomcatWebserver;
import com.bruan.webserver.impl.jettyWebserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自动配置类
 */
@Configuration
public class WebServerAutoConfiguration {

    /**
     * 将bean定义在这里,相当于sp把我们定义好了bean
     * @return
     */
    @Bean
    public TomcatWebserver tomcatWebserver() {
        return new TomcatWebserver();
    }
    @Bean
    public jettyWebserver jettyWebserver() {
        return new jettyWebserver();
    }

}

但是会出现两个bean同时生效的问题,所以需要使用condition注解进行控制bean的生效

  1. 条件注解的接入

image.png
可以看到需要实现condition的实现类,所以我们需要定义condition类

condition类的定义

项目结构

完成两个实现类,分别实现对Tomcat与jetty的matches方法
image.png

具体实现
  • TomcatCondition

其实就是判断当前项目是否有对应的依赖 也就是是否有这个类 “org.apache.catalina.startup.Tomcat”

/**
 * tomcat的接口判断类,用于判断该bean是否生效
 */
public class TomcatCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //条件 判断当前项目是否有对应的依赖 也就是是否有这个类 "org.apache.catalina.startup.Tomcat"
        try {
            context.getClassLoader().loadClass("org.apache.catalina.startup.Tomcat");
        } catch (ClassNotFoundException e) {
            return false;
        }
        return true;
    }
}

  • 同理也可以写出 jettycondition
public class JettyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //条件 判断当前项目是否有对应的依赖 也就是是否有这个类 "org.eclipse.jetty.server.Server"
        try {
            context.getClassLoader().loadClass("org.eclipse.jetty.server.Server");
        } catch (ClassNotFoundException e) {
            return false;
        }
        return true;
    }
}

依赖问题解析

问题

对于当前而言我们已经可以通过使用不同的依赖,实现对不同webserver的控制了,但是有一个严重问题是,当前我们的项目依赖是BRsp,其中包含tomcat和jetty的依赖,那么我们到底是使用了哪个呢?
image.png

解析

只需加上optional为true,
image.png
image.png
现在就只有tomcat的依赖了

切换依赖

在终依赖那里进行依赖排除,并且加入新的webserver依赖,这样就可以切换到webserver了


<dependencies>
  <dependency>
    <groupId>com.bruan</groupId>
    <artifactId>EasySpringBoot</artifactId>
    <version>1.0-SNAPSHOT</version>
    <exclusions>
      <exclusion>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
      </exclusion>
    </exclusions>
  </dependency>

  <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.4.46.v20220331</version>
  </dependency>
</dependencies>

image.png

空指针

如果出现空指针异常,需要在user启动类中加上import注解,因为包扫描位置不对,所以我们之前的autowebconfiguration根本就没起作用
image.png

模拟sp条件注解

springboot的ConditonalOnClass注解

@ConditionalOnClass是Spring框架中的一个条件注解,用于在特定类存在于类路径上时才会生效。当指定的类存在于类路径上时,被注解的Bean或配置类才会被注册到Spring容器中。
这个注解通常用于控制Bean的加载,可以根据类路径上是否存在某个特定类来决定是否注册某个Bean。例如,可以在一个自动配置类(@Configuration注解的类)上使用@ConditionalOnClass注解来指定只有当某个特定类存在时才加载该自动配置类。
示例用法:

@Configuration
@ConditionalOnClass({SomeClass.class})
public class MyAutoConfiguration {
    // Bean definitions
}

在上面的示例中,MyAutoConfiguration这个自动配置类只有在类路径上存在SomeClass类时才会被加载。
总的来说,@ConditionalOnClass注解可以帮助开发者根据特定类的存在与否来动态地控制Bean的加载,从而实现更灵活的配置和自动化装配。

模拟实现BRConditonalOnClass注解

观察到JettyCondition和TomcatDondition很像,我们不妨进行抽象,可以抽象出代码如下:

/**
 * condition的抽像总实现类,从注解BRConditionalOnClass获取值
 */
public class BruanCondition implements Condition {


    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}

同时我们需要明确目的是实现该注解,实现输入全类名,判断数据是否存在,如果存在就生成bean,获取webserver容器image.png
那么我们需要定义一个注解


/**
 * 条件注解,优化版 可以让我们不写对应的Condition实现类
 * 直接判断当前项目是否有对应的依赖 也就是是否有这个类 "org.eclipse.jetty.Server"
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(BruanCondition.class)
public @interface BRConditionalOnClass {
    String value();
}

同时完善上述抽取的抽象类

/**
 * condition的抽像总实现类,从注解BRConditionalOnClass获取值
 */
public class BruanCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //条件 判断当前项目是否有对应的依赖 也就是是否有这个类
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BRConditionalOnClass.class.getName());
        String className = annotationAttributes.get("value").toString();
        try {
            context.getClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            return false;
        }
        return true;
    }

}

image.png

执行过程图解

image.png

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值