spring boot学习二:Spring Boot自动装配分析与实战
上文中简单介绍了intellij环境下Spring Boot的入门示例,从而见识到了Spring Boot的强大,几乎不用做什么配置,就能运行一个Spring mvc的示例,要知道,Spring之前都是以繁琐的配置而为人诟病,Spring Boot的自动装配,可以根据pom的依赖配置,自动生成相应的bean,并加载到Spring Context中,简化了Spring项目搭建的复杂度,本节主要介绍Spring Boot自动装配的流程,并最终提供了自定义自动装配的示例代码。
一、在这之前,首先要介绍一下Spring4中的条件注解:@Conditional,Spring会根据独立的注解条件来创建类,Spring条件注解示例如下:
1、首先创建ch2_1工程和condmodule模块,项目结构如下所示:
2、创建对应的条件类,其中,MatchCondition表示匹配的条件类,NotMatchCondition表示不匹配的条件类,MatchCondition代码如下:
package com.flagship.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class MatchCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return true; } }
NotMatchCondition代码如下:
package com.flagship.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class NotMatchCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata annotatedTypeMetadata) { return false; } }
3、创建2个类,MatchBean和NotMatchBean,实现同一接口BeanInterface,后续Java配置类中,会根据不同的注解条件,生成相应的类对象,我们用类中的description()来打印信息,以此识别不同的对象信息,BeanInterface代码如下:
package com.flagship.bean; public interface BeanInterface { public void description(); }
MatchBean代码如下:
package com.flagship.bean; public class MatchBean implements BeanInterface { @Override public void description() { System.out.println("this is MatchBean's method!"); } }
NotMatchBean代码如下:
package com.flagship.bean; public class NotMatchBean implements BeanInterface { @Override public void description() { System.out.println("this is NotMatchBean's method!"); } }
4、Java配置类代码如下,此处采用@Configuration注解声明配置类,类似以前的xml配置文件,@Bean注解声明当前方法的返回值是一个Bean对象:
package com.flagship.config; import com.flagship.bean.BeanInterface; import com.flagship.bean.MatchBean; import com.flagship.bean.NotMatchBean; import com.flagship.condition.MatchCondition; import com.flagship.condition.NotMatchCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class ConditionalCfg { @Bean @Conditional(MatchCondition.class) public BeanInterface getMatchBeanObject(){ return new MatchBean(); } @Bean @Conditional(NotMatchCondition.class) public BeanInterface getNotMatchBeanObject(){ return new NotMatchBean(); } }
5、运行类代码如下,根据条件类的matches方法的返回值,最终会调用MatchBean类的description方法:
package com.flagship.condmodule; import com.flagship.bean.BeanInterface; import com.flagship.config.ConditionalCfg; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Application { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ConditionalCfg.class); BeanInterface bean = ctx.getBean(BeanInterface.class); bean.description(); } }
最终控制台打印信息为:this is MatchBean's method!
二、跟踪官网文档说明,Spring Boot条件注解大致分了如下几类如下:
Class conditions:@ConditionalOnClass和@ConditionalOnMissingClass,表示类是否在类路径下的条件注解
Bean conditions:@ConditionalOnBean和@ConditionalOnMissingBean,表示Bean是否被定义的条件注解
Property conditions:@ConditionalOnProperty,使用prefix和name属性用来表示是否有值,默认的话,只要该属性存在值,且不为false,即可匹配
Resource conditions:@ConditionalOnResource表示是否存在指定的resouce的条件注解
Web application conditions:@ConditionalOnWebApplication和@ConditionalOnNotWebApplication,当项目是web项目,或者不是web项目的条件注解
SpEL expression conditions:@ConditionalOnExpression,根据SPEL表达式执行结果作为条件
自动装配代码跟踪:
我们从上一章节的@SpringBootApplication开始,由于@SpringBootApplication是由@EnableAutoConfiguration组成的,我们观察@EnableAutoConfiguration注解的源码如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
EnableAutoConfiguration使用@Import注解将EnableAutoConfigurationImportSelector导入并声明为一个Bean,跟踪EnableAutoConfigurationImportSelector的源码,发现其继承AutoConfigurationImportSelector类,而其中有这么一个方法getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); //...省略其余代码 }继续跟踪SpringFactoriesLoader.loadFactoryNames方法,其代码如下:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") :ClassLoader.getSystemResources("META-INF/spring.factories"); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); }
发现最终读取的就是META-INF/spring.factories文件,点击intellij中【External Libraries】中spring-boot-autoconfigure-1.5.7.RELEASE.jar,打开META-INF/spring.factories文件,查看里面内容
此文件中提供了Spring Boot的默认自动配置,随便点开启动一个配置类:org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,代码如下:
@Configuration @ConditionalOnWebApplication @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class}) @ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) @AutoConfigureOrder(-2147483638) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class}) public class WebMvcAutoConfiguration { public static final String DEFAULT_PREFIX = ""; public static final String DEFAULT_SUFFIX = ""; public WebMvcAutoConfiguration() { }
可以看到,这里使用了上述介绍的条件注解来实现自动装配功能
三、本节我们根据上面介绍的原理,开始自定义实现一个自动装配,实现根据项目properties文件的配置打印当前环境信息日志的功能
1、首先,新建一个maven-archetype-quickstart模版的maven模块,并在src\main目录下建好resources\META-INF\spring.factories文件,架构如下图所示:
2、pom文件中加入autofigure的依赖,代码如下:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>ch2_1</artifactId> <groupId>com.flagship</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>autocfg</artifactId> <packaging>jar</packaging> <name>envLog</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>1.5.7.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
3、新建Java配置类:LogServiceProp,会读取项目配置文件中"env.log"开头的属性值
package com.flagship.autocfg; import org.springframework.boot.context.properties.ConfigurationProperties; /** * Java配置类 */ @ConfigurationProperties(prefix="env.log") public class LogServiceProp { private String runPattern = "run"; public String getRunPattern() { return runPattern; } public void setRunPattern(String runPattern) { this.runPattern = runPattern; } }
4、新建业务实体类,用于打印日志,其中,printEnv方法用来在控制台打印当前的运行模式日志,代码如下:
package com.flagship.autocfg; public class LogService { private String runPatternLog; public String printEnv(){ return "current env is in:" + runPatternLog + " pattern!"; } public String getRunPatternLog() { return runPatternLog; } public void setRunPatternLog(String runPatternLog) { this.runPatternLog = runPatternLog; } }
5、建立自动配置类:LogConfiguration,其中@Configuration注解标识的类,表明作为一个配置类,类似于之前的xml配置文件,@EnableConfigurationProperties告诉Spring Boot 任何被@ConfigurationProperties注解的beans将自动被属性配置,@ConditionalOnClass用来条件注解,当LogService.class存在类路径的时候起效,@ConditionalOnMissingBean当容器中没有这个Bean对象的时候,自动配置这个Bean对象,代码如下:
package com.flagship.autocfg; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(LogServiceProp.class) @ConditionalOnClass(LogService.class) public class LogConfiguration { @Autowired private LogServiceProp logServiceProp; @Bean @ConditionalOnMissingBean(LogService.class) public LogService getLogService(){ LogService service = new LogService(); service.setRunPatternLog(logServiceProp.getRunPattern()); return service; } }
6、spring.factories文件中加入自动配置类,代码如下:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.flagship.autocfg.LogConfiguration
7、回到第一节的condmodule模块,pom加入对envLog模块的依赖,代码如下:
<dependency> <groupId>com.flagship</groupId> <artifactId>autocfg</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
8、src\main\resources\application.properties中加入如下配置:
env.log.runPattern=debug
9、新建mvc入口测试类,代码如下:
package com.flagship.condmodule; import com.flagship.autocfg.LogService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication public class AutoCfgApplication { @Autowired LogService service; @RequestMapping("/") public String getEnvLog(){ return service.printEnv(); } public static void main(String[] args) { SpringApplication.run(AutoCfgApplication.class,args); } }
10、运行后结果如下,此时神奇的效果出现了,工程中并没有配置LogService这个对象,但是却可以通过@Autowired注解进行注入,这就是Spring Boot自动配置的威力:
current env is in:debug pattern!- spring boot学习二:Spring Boot自动装配分析与实战
- SpringBoot学习笔记---自动装配续2之条件装配
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
- SpringBoot实战和原理分析学习
- Spring学习(三)—— 自动装配案例分析
- Spring boot实战中自动配置原理分析
- SpringBoot SpringApplication底层源码分析与自动装配
- Spring4 学习笔记(5)-Spring 的自动装配
- Spring 学习笔记【一】自动装配bean
- JavaConfig方式以及处理自动装配的歧义性_spring基础学习
- SpringBoot学习笔记(5) Spring Boot集成Redis实现自动配置
- Java【SpringBoot实战—微信点餐系统学习总结】
- Spring Boot实战学习笔记2
- spring学习总结(五):IOC & DI 配置 Bean 之自动装配及bean之间的关系
- Spring框架学习(4):Spring的自动装配
- java架构师、集群、高可扩展、高性能、高并发、性能优化、Spring boot、Dubbo、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战学习架构师之路
- 学习Spring(三) -- Spring的自动装配
- SpringBoot内部配置:“application.properties配置”和”使用XML配置”,读取属性文件中的内容,日志配置,Profile配置(学习:SpringBoot实战)
- 深入浅析SpringBoot中的自动装配
- spring实战-自动装配bean