spring boot学习二:Spring Boot自动装配分析与实战

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
上文中简单介绍了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模块,项目结构如下所示: ![20191017100104\_1.png][20191017100104_1.png] 2、创建对应的条件类,其中,MatchCondition表示匹配的条件类,NotMatchCondition表示不匹配的条件类,MatchCondition代码如下: Java代码 1. **package** com.flagship.condition; 2. 3. **import** org.springframework.context.annotation.Condition; 4. **import** org.springframework.context.annotation.ConditionContext; 5. **import** org.springframework.core.type.AnnotatedTypeMetadata; 6. 7. **public** **class** MatchCondition **implements** Condition \{ 8. @Override 9. **public** **boolean** matches(ConditionContext conditionContext, 10. AnnotatedTypeMetadata annotatedTypeMetadata) \{ 11. **return** **true**; 12. \} 13. \} NotMatchCondition代码如下: Java代码 1. **package** com.flagship.condition; 2. 3. **import** org.springframework.context.annotation.Condition; 4. **import** org.springframework.context.annotation.ConditionContext; 5. **import** org.springframework.core.type.AnnotatedTypeMetadata; 6. 7. **public** **class** NotMatchCondition **implements** Condition \{ 8. 9. @Override 10. **public** **boolean** matches(ConditionContext conditionContext,AnnotatedTypeMetadata annotatedTypeMetadata) \{ 11. **return** **false**; 12. \} 13. \} 3、创建2个类,MatchBean和NotMatchBean,实现同一接口BeanInterface,后续Java配置类中,会根据不同的注解条件,生成相应的类对象,我们用类中的description()来打印信息,以此识别不同的对象信息,BeanInterface代码如下: Java代码 1. **package** com.flagship.bean; 2. 3. **public** **interface** BeanInterface \{ 4. **public** **void** description(); 5. \} MatchBean代码如下: Java代码 1. **package** com.flagship.bean; 2. 3. **public** **class** MatchBean **implements** BeanInterface \{ 4. @Override 5. **public** **void** description() \{ 6. System.out.println(“this is MatchBean’s method!”); 7. \} 8. \} NotMatchBean代码如下: Java代码 1. **package** com.flagship.bean; 2. 3. **public** **class** NotMatchBean **implements** BeanInterface \{ 4. @Override 5. **public** **void** description() \{ 6. System.out.println(“this is NotMatchBean’s method!”); 7. \} 8. \} 4、Java配置类代码如下,此处采用@Configuration注解声明配置类,类似以前的xml配置文件,@Bean注解声明当前方法的返回值是一个Bean对象: Java代码 1. **package** com.flagship.config; 2. 3. **import** com.flagship.bean.BeanInterface; 4. **import** com.flagship.bean.MatchBean; 5. **import** com.flagship.bean.NotMatchBean; 6. **import** com.flagship.condition.MatchCondition; 7. **import** com.flagship.condition.NotMatchCondition; 8. **import** org.springframework.context.annotation.Bean; 9. **import** org.springframework.context.annotation.Conditional; 10. **import** org.springframework.context.annotation.Configuration; 11. 12. @Configuration 13. **public** **class** ConditionalCfg \{ 14. @Bean 15. @Conditional(MatchCondition.**class**) 16. **public** BeanInterface getMatchBeanObject()\{ 17. **return** **new** MatchBean(); 18. \} 19. 20. @Bean 21. @Conditional(NotMatchCondition.**class**) 22. **public** BeanInterface getNotMatchBeanObject()\{ 23. **return** **new** NotMatchBean(); 24. \} 25. \} 5、运行类代码如下,根据条件类的matches方法的返回值,最终会调用MatchBean类的description方法: Java代码 1. **package** com.flagship.condmodule; 2. 3. **import** com.flagship.bean.BeanInterface; 4. **import** com.flagship.config.ConditionalCfg; 5. **import** org.springframework.context.annotation.AnnotationConfigApplicationContext; 6. 7. **public** **class** Application \{ 8. **public** **static** **void** main(String\[\] args) \{ 9. AnnotationConfigApplicationContext ctx = **new** AnnotationConfigApplicationContext(ConditionalCfg.**class**); 10. BeanInterface bean = ctx.getBean(BeanInterface.**class**); 11. bean.description(); 12. \} 13. \} 最终控制台打印信息为: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注解的源码如下: Java代码 1. // 2. // Source code recreated from a .class file by IntelliJ IDEA 3. // (powered by Fernflower decompiler) 4. // 5. 6. **package** org.springframework.boot.autoconfigure; 7. 8. **import** java.lang.annotation.Documented; 9. **import** java.lang.annotation.ElementType; 10. **import** java.lang.annotation.Inherited; 11. **import** java.lang.annotation.Retention; 12. **import** java.lang.annotation.RetentionPolicy; 13. **import** java.lang.annotation.Target; 14. **import** org.springframework.context.annotation.Import; 15. 16. @Target(\{ElementType.TYPE\}) 17. @Retention(RetentionPolicy.RUNTIME) 18. @Documented 19. @Inherited 20. @AutoConfigurationPackage 21. @Import(\{EnableAutoConfigurationImportSelector.**class**\}) 22. **public** **@interface** EnableAutoConfiguration \{ 23. String ENABLED\_OVERRIDE\_PROPERTY = “spring.boot.enableautoconfiguration”; 24. 25. Class\[\] exclude() **default** \{\}; 26. 27. String\[\] excludeName() **default** \{\}; 28. \} EnableAutoConfiguration使用@Import注解将EnableAutoConfigurationImportSelector导入并声明为一个Bean,跟踪EnableAutoConfigurationImportSelector的源码,发现其继承AutoConfigurationImportSelector类,而其中有这么一个方法getCandidateConfigurations Java代码 1. **protected** List getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) \{ 2. List configurations = SpringFactoriesLoader.loadFactoryNames(**this**.getSpringFactoriesLoaderFactoryClass(), **this**.getBeanClassLoader()); 3. //…省略其余代码 4. \} 继续跟踪SpringFactoriesLoader.loadFactoryNames方法,其代码如下: Java代码 1. **public** **static** List loadFactoryNames(Class factoryClass, ClassLoader classLoader) \{ 2. String factoryClassName = factoryClass.getName(); 3. **try** \{ 4. Enumeration urls = classLoader != **null** ? classLoader.getResources(“META-INF/spring.factories”) :ClassLoader.getSystemResources(“META-INF/spring.factories”); 5. ArrayList result = **new** ArrayList(); 6. **while**(urls.hasMoreElements()) \{ 7. URL url = (URL)urls.nextElement(); 8. Properties properties = PropertiesLoaderUtils.loadProperties(**new** UrlResource(url)); 9. String factoryClassNames = properties.getProperty(factoryClassName); 10. result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); 11. \} 发现最终读取的就是META-INF/spring.factories文件,点击intellij中【External Libraries】中spring-boot-autoconfigure-1.5.7.RELEASE.jar,打开META-INF/spring.factories文件,查看里面内容 ![20191017100104\_2.png][20191017100104_2.png] 此文件中提供了Spring Boot的默认自动配置,随便点开启动一个配置类:org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,代码如下: Java代码 1. @Configuration 2. @ConditionalOnWebApplication 3. @ConditionalOnClass(\{Servlet.**class**, DispatcherServlet.**class**, WebMvcConfigurerAdapter.**class**\}) 4. @ConditionalOnMissingBean(\{WebMvcConfigurationSupport.**class**\}) 5. @AutoConfigureOrder(-2147483638) 6. @AutoConfigureAfter(\{DispatcherServletAutoConfiguration.**class**, 7. ValidationAutoConfiguration.**class**\}) 8. **public** **class** WebMvcAutoConfiguration \{ 9. **public** **static** **final** String DEFAULT\_PREFIX = “”; 10. **public** **static** **final** String DEFAULT\_SUFFIX = “”; 11. 12. **public** WebMvcAutoConfiguration() \{ 13. \} 可以看到,这里使用了上述介绍的条件注解来实现自动装配功能 三、本节我们根据上面介绍的原理,开始自定义实现一个自动装配,实现根据项目properties文件的配置打印当前环境信息日志的功能 1、首先,新建一个maven-archetype-quickstart模版的maven模块,并在src\\main目录下建好resources\\META-INF\\spring.factories文件,架构如下图所示: ![20191017100104\_3.png][20191017100104_3.png] 2、pom文件中加入autofigure的依赖,代码如下: Xml代码 1. **<****project** xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” 2. xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”**>** 3. **<****parent****>** 4. **<****artifactId****>**ch2\_1**** 5. **<****groupId****>**com.flagship**** 6. **<****version****>**0.0.1-SNAPSHOT**** 7. **** 8. **<****modelVersion****>**4.0.0**** 9. 10. **<****artifactId****>**autocfg**** 11. **<****packaging****>**jar**** 12. 13. **<****name****>**envLog**** 14. **<****url****>**http://maven.apache.org**** 15. 16. **<****properties****>** 17. **<****project.build.sourceEncoding****>**UTF-8**** 18. **** 19. 20. **<****dependencies****>** 21. **<****dependency****>** 22. **<****groupId****>**org.springframework.boot**** 23. **<****artifactId****>**spring-boot-autoconfigure**** 24. **<****version****>**1.5.7.RELEASE**** 25. **** 26. **<****dependency****>** 27. **<****groupId****>**junit**** 28. **<****artifactId****>**junit**** 29. **<****version****>**3.8.1**** 30. **<****scope****>**test**** 31. **** 32. **** 33. **** 3、新建Java配置类:LogServiceProp,会读取项目配置文件中”env.log”开头的属性值 Java代码 1. **package** com.flagship.autocfg; 2. **import** org.springframework.boot.context.properties.ConfigurationProperties; 3. /\*\* 4. \* Java配置类 5. \*/ 6. @ConfigurationProperties(prefix=“env.log”) 7. **public** **class** LogServiceProp \{ 8. 9. **private** String runPattern = “run”; 10. 11. **public** String getRunPattern() \{ 12. **return** runPattern; 13. \} 14. **public** **void** setRunPattern(String runPattern) \{ 15. **this**.runPattern = runPattern; 16. \} 17. \} 4、新建业务实体类,用于打印日志,其中,printEnv方法用来在控制台打印当前的运行模式日志,代码如下: Java代码 1. **package** com.flagship.autocfg; 2. 3. **public** **class** LogService \{ 4. 5. **private** String runPatternLog; 6. 7. **public** String printEnv()\{ 8. **return** “current env is in:” \+ runPatternLog + ” pattern!”; 9. \} 10. 11. **public** String getRunPatternLog() \{ 12. **return** runPatternLog; 13. \} 14. **public** **void** setRunPatternLog(String runPatternLog) \{ 15. **this**.runPatternLog = runPatternLog; 16. \} 17. \} 5、建立自动配置类:LogConfiguration,其中@Configuration注解标识的类,表明作为一个配置类,类似于之前的xml配置文件,@EnableConfigurationProperties告诉Spring Boot 任何被@ConfigurationProperties注解的beans将自动被属性配置,@ConditionalOnClass用来条件注解,当LogService.class存在类路径的时候起效,@ConditionalOnMissingBean当容器中没有这个Bean对象的时候,自动配置这个Bean对象,代码如下: Java代码 1. **package** com.flagship.autocfg; 2. 3. **import** org.springframework.beans.factory.annotation.Autowired; 4. **import** org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 5. **import** org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 6. **import** org.springframework.boot.context.properties.ConfigurationProperties; 7. **import** org.springframework.boot.context.properties.EnableConfigurationProperties; 8. **import** org.springframework.context.annotation.Bean; 9. **import** org.springframework.context.annotation.Configuration; 10. 11. @Configuration 12. @EnableConfigurationProperties(LogServiceProp.**class**) 13. @ConditionalOnClass(LogService.**class**) 14. **public** **class** LogConfiguration \{ 15. @Autowired 16. **private** LogServiceProp logServiceProp; 17. 18. @Bean 19. @ConditionalOnMissingBean(LogService.**class**) 20. **public** LogService getLogService()\{ 21. LogService service = **new** LogService(); 22. service.setRunPatternLog(logServiceProp.getRunPattern()); 23. **return** service; 24. \} 25. \} 6、spring.factories文件中加入自动配置类,代码如下: Xml代码 1. \# Auto Configure 2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\ 3. com.flagship.autocfg.LogConfiguration 7、回到第一节的condmodule模块,pom加入对envLog模块的依赖,代码如下: Xml代码 1. **<****dependency****>** 2. **<****groupId****>**com.flagship**** 3. **<****artifactId****>**autocfg**** 4. **<****version****>**0.0.1-SNAPSHOT**** 5. **** 8、src\\main\\resources\\application.properties中加入如下配置: env.log.runPattern=debug 9、新建mvc入口测试类,代码如下: Java代码 1. **package** com.flagship.condmodule; 2. 3. **import** com.flagship.autocfg.LogService; 4. **import** org.springframework.beans.factory.annotation.Autowired; 5. **import** org.springframework.boot.SpringApplication; 6. **import** org.springframework.boot.autoconfigure.SpringBootApplication; 7. **import** org.springframework.web.bind.annotation.RequestMapping; 8. **import** org.springframework.web.bind.annotation.RestController; 9. 10. @RestController 11. @SpringBootApplication 12. **public** **class** AutoCfgApplication \{ 13. 14. @Autowired 15. LogService service; 16. 17. @RequestMapping(“/”) 18. **public** String getEnvLog()\{ 19. **return** service.printEnv(); 20. \} 21. 22. **public** **static** **void** main(String\[\] args) \{ 23. SpringApplication.run(AutoCfgApplication.**class**,args); 24. \} 25. \} 10、运行后结果如下,此时神奇的效果出现了,工程中并没有配置LogService这个对象,但是却可以通过@Autowired注解进行注入,这就是Spring Boot自动配置的威力: current env is in:debug pattern! [20191017100104_1.png]: https://gitee.com/chenssy/blog-home/raw/master/image/series-images/springboot/20191017100104_1.png [20191017100104_2.png]: https://gitee.com/chenssy/blog-home/raw/master/image/series-images/springboot/20191017100104_2.png [20191017100104_3.png]: https://gitee.com/chenssy/blog-home/raw/master/image/series-images/springboot/20191017100104_3.png ------- 来源:[]()
赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot学习二:Spring Boot自动装配分析与实战

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏