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

文章目录

    • 1 前言
    • 2 结构设计
      • 2.1 web容器体系
      • 2.2 spring容器管理Bean
    • 2 maven 依赖传递
    • 3 ConditionalOnClass
      • 3.1 注解相关科普
      • 3.2 自定义注解
    • 4 服务测试
    • 结语

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.embedgroupId>
    <artifactId>tomcat-embed-coreartifactId>
    <version>9.0.60version>
dependency>
<dependency>
    <groupId>org.eclipse.jettygroupId>
    <artifactId>jetty-serverartifactId>
    <version>9.2.28.v20190418version>
    <optional>trueoptional>
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.gaogzhengroupId>
        <artifactId>springbootartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
dependencies>

启动服务,控制台输出:

tomcat web server

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

<dependencies>
    <dependency>
        <groupId>com.gaogzhengroupId>
        <artifactId>springbootartifactId>
        <version>1.0-SNAPSHOTversion>
        <exclusions>
            <exclusion>
                <groupId>org.apache.tomcat.embedgroupId>
                <artifactId>tomcat-embed-coreartifactId>
            exclusion>
        exclusions>
    dependency>
    <dependency>
        <groupId>org.eclipse.jettygroupId>
        <artifactId>jetty-serverartifactId>
        <version>9.2.28.v20190418version>
    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

你可能感兴趣的:(#,spring全家桶,spring,boot,java,spring)