0102按依赖注册bean-手写springboot-springboot系列

1 前言

前一篇我们简单模拟了springboot的壳,但是有很多其功能并没有实现。

场景描述:之前我们使用的web容器为硬编码绑定的tomcat,但是现在我们服务现在想要根据我们的依赖,决定使用tomcat,或者jetty。比如我们依赖添加tomcat,web容器使用tomcat;我添加jetty依赖,我们web容器使用jetty。

2 结构设计

2.1 web容器体系

第一步:web容器体系设计,我们需要在我们自定义springboot中设计一个顶层接口WebServer,两个实现类TomcatWebServer和JettyWebServer。

WebServer接口代码2.1-1如下:

package com.gaogzhen.springboot.server;

/**
 * web容器顶层接口
 * @author gaogzhen
 */
public interface WebServer {

    /**
     * 启动容器
     */
    public void start();
}

目前我们只是简单模拟,没有关于容器的逻辑业务处理。

TomcatWebServer和JettyWebServer代码2.1-2如下所示

package com.gaogzhen.springboot.server;
/**
 * tomcat web server
 */
public class TomcatWebServer implements WebServer{
    @Override
    public void start() {
        System.out.println("tomcat web server");
    }
}

package com.gaogzhen.springboot.server;

/**
 * jetty web server
 */
public class JettyWebServer implements WebServer{
    @Override
    public void start() {
        System.out.println("jetty web server");
    }
}

2.2 spring容器管理Bean

第二步:自动配置容器管理TomcatWebServer和JettyWebServer类型的bean。

自动配置类WebServerAutoConfiguration代码2.2-1如下所示:

package com.gaogzhen.springboot.server;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

    @Bean
    public TomcatWebServer tomcatWebServer() {
        return new TomcatWebServer();
    }

    @Bean
    public JettyWebServer jettyWebServer() {
        return new JettyWebServer();
    }
}

我们自定义sprigboot需要同时依赖tomcat和jetty,而我们应用服务需要按依赖来配置web server。首先应用服务依赖自定义springboot,那么就需要解决依赖传递问题。

2 maven 依赖传递

我们应用服务只需要一个web server 就可以,即tomcat或者jetty选一个就可以,默认使用tomcat。我们自定义springboot 模块pom.xml做如下配置:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>9.0.60</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.2.28.v20190418</version>
    <optional>true</optional>
</dependency>
  • tomcat本项目生效,会依赖传递到使用我们自定义springboot的项目
  • jetty自定义sprinboot模块生效,不会向下传递

关于maven的传递依赖可自行查阅相关文档或者文末参考[2]

3 ConditionalOnClass

3.1 注解相关科普

第三步:依赖配置好了,但是我们上面的自动装配类里面2个bean目前说默认都会被pring容器管理,我们需要实现按需创建,那么就需要借助条件注解@ConditionalOnClass。

@Conditional是Spring框架中的一个注解,它允许你根据指定的条件来有选择性地注册Bean或配置类。

@Conditional添加到Bean或配置类上,只有在满足指定条件的情况下才会注册或加载它。如果条件不满足,Spring将跳过注册或加载该Bean或配置类。

@ConditionalOnClass是Spring Boot中常用的一个@Conditional注解,它用于基于特定的类是否存在于classpath中来有条件地加载Bean或配置类。

当某个类存在于classpath中时,该注解修饰的Bean或配置类才会被加载。否则,它将不会被加载。

3.2 自定义注解

自定义@ConditionalOnClass注解,代码3.2-1如下所示:

package com.gaogzhen.springboot.server;

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;


@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MyCondition.class)
public @interface ConditionalOnClass {
    String value();
}

MyCondition类代码3.2-2如下所示;

package com.gaogzhen.springboot.server;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;

/**
 *
 */
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取注解@ConditionalOnClass注解value值
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
        String className = (String) attributes.get("value");

        // 根据是否成功加载返回ture或者false
        try {
            context.getClassLoader().loadClass(className);
            return true;
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            return false;
        }
    }
}
  • 当扫描到类上有我们自定义注解@ConditionalOnClass,执行@Conditional指定的MyCondition类中的matches()方法逻辑;
  • 通过类加载器判断是否加载成功来决定是否注册该Bean,即有相关依赖注册该bean,没有想过依赖不注册该bean。

修改我们的WebServerAutoConfiguration配置类代码3.2-1如下;

@Configuration
public class WebServerAutoConfiguration {

    @Bean
    @ConditionalOnClass("org.apache.catalina.startup.Tomcat")
    public TomcatWebServer tomcatWebServer() {
        return new TomcatWebServer();
    }

    @Bean
    @ConditionalOnClass("org.eclipse.jetty.server.Server")
    public JettyWebServer jettyWebServer() {
        return new JettyWebServer();
    }
}
  • org.apache.catalina.startup.Tomcat是tomcat依赖中相关类
  • org.eclipse.jetty.server.Server是jetty依赖中相关的类

问题来了?我们的class是否可以指定依赖jar包中其他类呢?

  • 当然可以,但是选择尽量见名知意不会和其他依赖重名,即选择核心类

4 服务测试

其他测试代码同上一篇,因为我们没有功能没完善,启动类上需要加上@Import注解引入我们的自动配置类,代码如下:

@SpringBootApplicationG2zh
@Import(WebServerAutoConfiguration.class)
public class MyApplication {
    public static void main(String[] args) {
        SpringApplicationG2zh.run(MyApplication.class, args);
    }
}

user服务引入自定义springboot,就是使用默认的tomcat;

<dependencies>
    <dependency>
        <groupId>com.gaogzhen</groupId>
        <artifactId>springboot</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

启动服务,控制台输出:

tomcat web server

测试jetty服务器,user pom.xml如下配置:

<dependencies>
    <dependency>
        <groupId>com.gaogzhen</groupId>
        <artifactId>springboot</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.2.28.v20190418</version>
    </dependency>
</dependencies>

user服务启动测试,结果如下:

jetty web server

结语

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/springboot-custom

参考:

[1]Springboot视频教程[CP/OL].P118-135.

[2]Maven依赖传递,排除依赖和可选依赖[CP/OL].

[3]ChatGPT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值