Spring Boot核心知识--2

二、夯实基础:JavaConfig与常见Annotation

2.1、JavaConfig

我们知道 bean是Spring IOC中非常核心的概念,Spring容器负责bean的生命周期的管理。在最初,Spring使用XML配置文件的方式来描述bean的定义以及相互间的依赖关系,但随着Spring的发展,越来越多的人对这种方式表示不满,因为Spring项目的所有业务类均以bean的形式配置在XML文件中,造成了大量的XML文件,使项目变得复杂且难以管理。

后来,基于纯Java Annotation依赖注入框架 Guice出世,其性能明显优于采用XML方式的Spring,甚至有部分人认为, Guice可以完全取代Spring( Guice仅是一个轻量级IOC框架,取代Spring还差的挺远)。正是这样的危机感,促使Spring及社区推出并持续完善了 JavaConfig子项目,它基于Java代码和Annotation注解来描述bean之间的依赖绑定关系。比如,下面是使用XML配置方式来描述bean的定义:

<bean id="bookService" class="cn.moondev.service.BookServiceImpl"></bean>

而基于JavaConfig的配置形式是这样的:

@Configuration
public class MoonBookConfiguration {

 // 任何标志了@Bean的方法,其返回值将作为一个bean注册到Spring的IOC容器中
 // 方法名默认成为该bean定义的id
 @Bean
 public BookService bookService() {
 return new BookServiceImpl();
 }
}

如果两个bean之间有依赖关系的话,在XML配置中应该是这样:

<bean id="bookService" class="cn.moondev.service.BookServiceImpl">
 <property name="dependencyService" ref="dependencyService"/>
</bean>
<bean id="otherService" class="cn.moondev.service.OtherServiceImpl">
 <property name="dependencyService" ref="dependencyService"/>
</bean>
<bean id="dependencyService" class="DependencyServiceImpl"/>

而在JavaConfig中则是这样:

@Configuration
public class MoonBookConfiguration {

 // 如果一个bean依赖另一个bean,则直接调用对应JavaConfig类中依赖bean的创建方法即可
 // 这里直接调用dependencyService()
 @Bean
 public BookService bookService() {
 return new BookServiceImpl(dependencyService());
 }

 @Bean
 public OtherService otherService() {
 return new OtherServiceImpl(dependencyService());
 }

 @Bean
 public DependencyService dependencyService() {
 return new DependencyServiceImpl();
 }
}

你可能注意到这个示例中,有两个bean都依赖于dependencyService,也就是说当初始化bookService时会调用 dependencyService(),在初始化otherService时也会调用 dependencyService(),那么问题来了?这时候IOC容器中是有一个dependencyService实例还是两个?这个问题留着大家思考吧,这里不再赘述。


2.2、@ComponentScan

@ComponentScan注解对应XML配置形式中的 <context:component-scan>元素,表示启用组件扫描,Spring会自动扫描所有通过注解配置的bean,然后将其注册到IOC容器中。我们可以通过 basePackages等属性来指定 @ComponentScan自动扫描的范围,如果不指定,默认从声明 @ComponentScan所在类的 package进行扫描。正因为如此,SpringBoot的启动类都默认在 src/main/java下。


2.3、@Import

@Import注解用于导入配置类,举个简单的例子:

@Configuration
public class MoonBookConfiguration {
 @Bean
 public BookService bookService() {
 return new BookServiceImpl();
 }
}

现在有另外一个配置类,比如: MoonUserConfiguration,这个配置类中有一个bean依赖于 MoonBookConfiguration中的bookService,如何将这两个bean组合在一起?借助 @Import即可:

@Configuration
// 可以同时导入多个配置类,比如:@Import({A.class,B.class})
@Import(MoonBookConfiguration.class)
public class MoonUserConfiguration {
 @Bean
 public UserService userService(BookService bookService) {
 return new BookServiceImpl(bookService);
 }
}

需要注意的是,在4.2之前, @Import注解只支持导入配置类,但是在4.2之后,它支持导入普通类,并将这个类作为一个bean的定义注册到IOC容器中。


2.4、@Conditional

@Conditional注解表示在满足某种条件后才初始化一个bean或者启用某些配置。它一般用在由 @Component、 @Service、 @Configuration等注解标识的类上面,或者由 @Bean标记的方法上。如果一个 @Configuration类标记了 @Conditional,则该类中所有标识了 @Bean的方法和 @Import注解导入的相关类将遵从这些条件。

在Spring里可以很方便的编写你自己的条件类,所要做的就是实现 Condition接口,并覆盖它的 matches()方法。举个例子,下面的简单条件类表示只有在 Classpath里存在 JdbcTemplate类时才生效:

public class JdbcTemplateCondition implements Condition {

 @Override
 public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
 try {
 conditionContext.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
 return true;
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 }
 return false;
 }
}

当你用Java来声明bean的时候,可以使用这个自定义条件类:

@Conditional(JdbcTemplateCondition.class)
@Service
public MyService service() {
 ......
}

这个例子中只有当 JdbcTemplateCondition类的条件成立时才会创建MyService这个bean。也就是说MyService这bean的创建条件是 classpath里面包含 JdbcTemplate,否则这个bean的声明就会被忽略掉。

SpringBoot定义了很多有趣的条件,并把他们运用到了配置类上,这些配置类构成了 SpringBoot的自动配置的基础。SpringBoot运用条件化配置的方法是:定义多个特殊的条件化注解,并将它们用到配置类上。下面列出了 SpringBoot提供的部分条件化注解:

 

2019年一份来自大佬的Spring Boot核心知识清单

 

 


2.5、@ConfigurationProperties与@EnableConfigurationProperties

当某些属性的值需要配置的时候,我们一般会在 application.properties文件中新建配置项,然后在bean中使用 @Value注解来获取配置的值,比如下面配置数据源的代码。

// jdbc config
jdbc.mysql.url=jdbc:mysql://localhost:3306/sampledb
jdbc.mysql.username=root
jdbc.mysql.password=123456
......

// 配置数据源
@Configuration
public class HikariDataSourceConfiguration {

 @Value("jdbc.mysql.url")
 public String url;
 @Value("jdbc.mysql.username")
 public String user;
 @Value("jdbc.mysql.password")
 public String password;

 @Bean
 public HikariDataSource dataSource() {
 HikariConfig hikariConfig = new HikariConfig();
 hikariConfig.setJdbcUrl(url);
 hikariConfig.setUsername(user);
 hikariConfig.setPassword(password);
 // 省略部分代码
 return new HikariDataSource(hikariConfig);
 }
}

使用 @Value注解注入的属性通常都比较简单,如果同一个配置在多个地方使用,也存在不方便维护的问题(考虑下,如果有几十个地方在使用某个配置,而现在你想改下名字,你改怎么做?)。对于更为复杂的配置,Spring Boot提供了更优雅的实现方式,那就是 @ConfigurationProperties注解。我们可以通过下面的方式来改写上面的代码:

@Component
// 还可以通过@PropertySource("classpath:jdbc.properties")来指定配置文件
@ConfigurationProperties("jdbc.mysql")
// 前缀=jdbc.mysql,会在配置文件中寻找jdbc.mysql.*的配置项
pulic class JdbcConfig {
 public String url;
 public String username;
 public String password;
}

@Configuration
public class HikariDataSourceConfiguration {

 @AutoWired
 public JdbcConfig config;

 @Bean
 public HikariDataSource dataSource() {
 HikariConfig hikariConfig = new HikariConfig();
 hikariConfig.setJdbcUrl(config.url);
 hikariConfig.setUsername(config.username);
 hikariConfig.setPassword(config.password);
 // 省略部分代码
 return new HikariDataSource(hikariConfig);
 }
}

@ConfigurationProperties对于更为复杂的配置,处理起来也是得心应手,比如有如下配置文件:

#App
app.menus[0].title=Home
app.menus[0].name=Home
app.menus[0].path=/
app.menus[1].title=Login
app.menus[1].name=Login
app.menus[1].path=/login

app.compiler.timeout=5
app.compiler.output-folder=/temp/

app.error=/error/

可以定义如下配置类来接收这些属性

@Component
@ConfigurationProperties("app")
public class AppProperties {

 public String error;
 public List<Menu> menus = new ArrayList<>();
 public Compiler compiler = new Compiler();

 public static class Menu {
 public String name;
 public String path;
 public String title;
 }

 public static class Compiler {
 public String timeout;
 public String outputFolder;
 }
}

@EnableConfigurationProperties注解表示对 @ConfigurationProperties的内嵌支持,默认会将对应Properties Class作为bean注入的IOC容器中,即在相应的Properties类上不用加 @Component注解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值