1.SpringBoot的优越性
Spring是目前世界上主流的JavaWeb开发框架,它可以帮助我们更快速构建Java项目,在Spring家族中提供了很多优秀的框架,而所有的框架都是基于一个基础框架的SpringFrameWork(也就是Spring框架),不过,直接基于Spring框架进行开发是一个比较繁琐的过程,一方面,在pom.xml中进行依赖配置比较繁琐,在开发过程中,我们需要手动去配置大量依赖并保证其不起冲突,另一方面,我们在Spring框架的开发过程中,需要在Spring的配置文件中做大量配置,这是一个非常繁琐且成本较高的过程。
由此,我们的SpringBoot框架应运而生,它使得我们能够把更多精力放到业务本身,而不是把时间花费在框架本身的配置使用上。SpringBoot的优越性体现在其底层提供的两个重要功能:起步依赖和自动配置,起步依赖可以大大简化在pom.xml文件中的配置,自动配置可以大大简化框架在使用过程中Bean的声明和配置。
2.起步依赖
SpringBoot提供了大量封装好的起步依赖,比如,spring-boot-start-web是Web开发的起步依赖,在此依赖中集成了json,web,tomcat等依赖,我们只需要引入这个起步依赖,其他的依赖都会自动通过Maven的依赖传递进入项目。
编辑
3. 自动装配
SpringBoot的自动装配就是当Spring容器启动后,一些配置类,Bean就会自动装配到IOC容器中,无需我们手动声明,从而简化开发,省去繁琐的配置操作。
3.1 实现自动配置的方案
方案一:
示例如下:
在common模块下封装JWT的Bean:
编辑
在web模块下如果想使用JwtUtil的Bean,则需要在Web启动类上添加注解:
@ComponentScan("com.zl.common") 或者 @SpringBootApplication(scanBasePackages = {"com.zl.web", "com.zl.common"})
如果采用上述方案完成自动配置,当需要引入大量第三方依赖时就需要在启动类上扫描大量包,这将会非常繁琐并且大面积扫描会降低性能,因此SpringBoot并未采用这种方式进行自动装配。
方案二:
使用@Import注解导入,可以导入普通类,配置类,以及ImportSelector接口的实现类。
示例一:在web模块启动类中导入common模块的JwtUtil Bean将其装配到web模块使用
编辑
示例二:在web模块的启动类上导入common模块的MinIo配置类,将此配置类以及在配置类中注册的Bean都装配到web模块中使用。
编辑
编辑
示例三:在common模块的MyImportSelector中定义要注入的类的全类名,在web模块启动类上将此类注入。
编辑
编辑
示例四:让common模块提供启动注解(@EnableXXX),该注解中封装@Import注解,只需在web模块启动类上增加@EnableXXX注解即可
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类 public @interface EableJwtConfig { }
@EableJwtConfig
3.2 自动装配源码剖析
SpringBoot上的核心注解@SpringBootApplication: 编辑
自动配置核心注解@EnableAutoConfiguration:
编辑
编辑其中,该注解使用了@Import注解,导入了实现了ImportSelector接口的类
AutoConfigurationImportSelector类重写了ImportSelector接口的selectImports方法
编辑
selectImports()方法底层调用getAutoConfigurationEntry()方法,获取可自动配置的配置类集合信息
编辑
getAutoConfigurationEntry()方法通过调用getCandidateConfigurations(annotationMetadata, attributes)方法获取在配置文件中配置的所有自动配置类的集合
编辑
getCandidateConfigurations方法获取图中路径文件下配置类的集合,在我们的起步依赖下可以找到对应的该文件,该文件指定了第三方依赖的配置类。
编辑
当SpringBoot程序启动时,会加载配置文件中定义的配置类并将配置类信息(全类名)封装到String类型数组中,最终由@Import注解将配置类信息加载到IOC容器中。
但是,配置类集合文件中声明的所有配置类并不一定会全部加载,在声明Bean对象时,还会加一个@Conditional注解,这个注解的作用是提供按条件进行装配,只有满足注解中的条件时,才能将Bean注册到IOC容器中。
@Conditional注解是一个父注解,派生出大量子注解:
@ConditionalOnClass:判断环境中是否对应的字节码文件,有才注册到IOC容器中
@ConditionalOnMissingBean:判断环境中没有对应的Bean,没有才注册到IOC容器中
@ConditionalOnProperty:判断配置文件中有对应属性和值才注册到IOC容器中
编辑 编辑 编辑
自动配置过程总结:
编辑
4.手写一个自定义strarter
所谓starter指的就是SpringBoot中的起步依赖,在实际项目中,我们可能会使用到很多第三方技术,但是并非所有第三方技术都提供了与SpringBoot整合的starter依赖,如果这些又非常通用,我们完全可以自己封装这些技术的starter依赖然后传到Maven仓库中,这样,不同项目组的开发者都可以直接引入此依赖进行使用。
这里,我已封装IP监控功能的自定义starter去演示
1.首先定义monitor-spring-boot-autoconfiguration,在里面封装业务逻辑操作
编辑
引入必须依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zl</groupId> <artifactId>monitor-spring-boot-autoconfiguration</artifactId> <version>1.0-SNAPSHOT</version> <name>monitor-spring-boot-autoconfiguration</name> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.zl</groupId> <artifactId>zl-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
监控逻辑:
import com.zl.common.utils.ipUtils.AddressUtil; import com.zl.common.utils.ipUtils.IPUtil; import jakarta.servlet.http.HttpServletRequest; import java.util.*; public class MonitorOperator { private TestProperties properties; public MonitorOperator(TestProperties properties) { this.properties = properties; } /** * 实时IP地址监测接口 * @param request HttpServletRequest * @return IP和归属地信息 */ public Map<String, String> monitorIp(HttpServletRequest request) { Map<String, String> result = new HashMap<>(); String ip = IPUtil.getIpAddr(request); // 获取客户端IP String location = AddressUtil.getRealAddressByIP(ip); // 获取地址 result.put("username", properties.getUsername()); result.put("password", properties.getPassword()); result.put("ip", ip); result.put("location", location); return result; } }
配置properties信息:
@Data @ConfigurationProperties("test") public class TestProperties { private String username; private String password; }
配置类:
@Configuration //将配置文件TestProperties注册为Bean @EnableConfigurationProperties(TestProperties.class) public class MonitorConfiguration { @Bean public MonitorOperator monitorOperator(TestProperties testProperties) { return new MonitorOperator(testProperties); } }
在resource对应路径下的imports文件中指定要自动装配的类名
编辑
编辑
2.定义monitor-spring-boot-starter,此模块只保留pom.xml文件,并且在依赖引入autoconfig模块依赖
编辑
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zl</groupId> <artifactId>monitor-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <name>monitor-spring-boot-starter</name> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.zl</groupId> <artifactId>monitor-spring-boot-autoconfiguration</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
3.定义测试模块,并在测试模块中引入monitor-spring-boot-starter依赖,进行测试
编辑
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zl</groupId> <artifactId>test</artifactId> <version>1.0-SNAPSHOT</version> <name>test</name> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- SpringBoot相关依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.zl</groupId> <artifactId>monitor-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
在application.yml中指定配置
test: username: GuihaoLv password: 123456
定义测试接口:
@RestController("/monitor") public class MonitorController { @Autowired private MonitorOperator monitorOperator; @GetMapping("/getMonitor") public Map<String, String> map(HttpServletRequest request){ return monitorOperator.monitorIp(request); } }
测试结果:
编辑
这样,我们就完成了监测功能的starter定义,其他项目若想调用这个模块的功能,引入依赖,配置一下test信息就可以直接注入使用了。