> 文章列表 > 0102按依赖注册bean-手写springboot-springboot系列

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

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

文章目录

    • 1 前言
    • 2 结构设计
      • 2.1 web容器体系
      • 2.2 spring容器管理Bean
    • 2 maven 依赖传递
    • 3 ConditionalOnClass
    • 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{@Overridepublic void start() {System.out.println("tomcat web server");}
}package com.gaogzhen.springboot.server;/*** jetty web server*/
public class JettyWebServer implements WebServer{@Overridepublic 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 {@Beanpublic TomcatWebServer tomcatWebServer() {return new TomcatWebServer();}@Beanpublic 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 {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取注解@ConditionalOnClass注解value值Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());String className = (String) attributes.get("value");// 根据是否成功加载返回ture或者falsetry {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