java框架篇(四)

一、 springboot
1.springboot是什么?
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

随着动态语言的流行(Ruby、Groovy、Scala、Node.js),Java 的开发显得格外的笨重,繁多的配置、低下的开发效率、复杂的部署流程以及第三方技术集成难度大。
在上述环境下,Spring Boot 应运而生。它使用“习惯优于配置”(项目中存在大量的配置,此外还内置一个习惯性的配置,让你无须手动进行配置)的理念让你的项目快速运行起来。
使用 Spring Boot 很容易创建一个独立运行(运行 jar,内嵌 Servlet 容器)、准生产级别的基于 Spring 框架的项目,使用 Spring Boot 你可以不用或者只需要很少的 Spring 配置。

2.Spring Boot 核心功能
1)独立运行的 Spring 项目
Spring Boot 可以以 jar 包的形式独立运行,运行一个 Spring Boot 项目只需通过 java–jar xx.jar 来运行。
2)内嵌 Servlet 容器
Spring Boot 可选择内嵌 Tomcat、Jetty 或者 Undertow,这样我们无须以 war 包形式部署项目。
3)提供 starter 简化 Maven 配置
Spring 提供了一系列的 starter pom 来简化 Maven 的依赖加载,例如,当你使用了spring-boot-starter-web 时,会自动加入如图 1 所示的依赖包。
4)自动配置 Spring
Spring Boot 会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少我们要使用的配置。当然,Spring Boot 只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置 Bean,而 Spring Boot 没有提供支持,则可以自定义自动配置。
5)准生产的应用监控
Spring Boot 提供基于 http、ssh、telnet 对运行时的项目进行监控。
6)无代码生成和 xml 配置
Spring Boot 的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的,这是 Spring 4.x 提供的新特性。Spring 4.x 提倡使用 Java 配置和注解配置组合,而 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置。

3.Spring Boot的优缺点
1)优点
快速构建项目。
对主流开发框架的无配置集成。
项目可独立运行,无须外部依赖Servlet容器。
提供运行时的应用监控。
极大地提高了开发、部署效率。
与云计算的天然集成。
2)缺点
版本迭代速度很快,一些模块改动很大。
由于不用自己做配置,报错时很难定位。
网上现成的解决方案比较少。

4.spring中的一些注解(有助于理解springboot中的注解)
@Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
   
    <!-- bean定义 -->
</beans>
@Configuration
public class MockConfiguration{
    // bean定义
} 

任何一个标注了 @Configuration 的 Java 类定义都是一个 JavaConfig 配置类。

<bean id="mockService" class="..MockServiceImpl"> ...</bean>
@Configuration
public class MockConfiguration {
    @Bean
    public MockService mockService() {
        return new MockServiceImpl();
    }
}

为了表达 bean 与 bean 之间的依赖关系,在 XML 形式中一般是这样的:

<bean id="mockService" class="..MockServiceImpl">
    <property name="dependencyService" ref="dependencyService" />
</bean>
<bean id="dependencyService" class="DependencyServiceImpl" /> 
@Configuration
public class MockConfiguration {
    @Bean
    public MockService mockService() {
        return new MockServiceImpl(dependencyService());
    }
    @Bean
    public DependencyService dependencyService() {
        return new DependencyServiceImpl();
    }
}

@ComponentScan
@ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于配合一些元信息 Java Annotation,比如 @Component 和 @Repository 等,将标注了这些元信息 Annotation 的 bean 定义类批量采集到 Spring 的 IoC 容器中。

我们可以通过 basePackages 等属性来细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现会从声明 @ComponentScan 所在类的 package 进行扫描。

@ComponentScan 是 SpringBoot 框架魔法得以实现的一个关键组件,大家可以重点关注,我们后面还会遇到它。

@PropertySource 与 @PropertySources
@PropertySource 用于从某些地方加载 *.properties 文件内容,并将其中的属性加载到 IoC 容器中,便于填充一些 bean 定义属性的占位符(placeholder),当然,这需要 PropertySourcesPlaceholderConfigurer 的配合。

如果我们使用 Java 8 或者更高版本开发,那么,我们可以并行声明多个 @PropertySource:

@Configuration
@PropertySource("classpath:1.properties")
@PropertySource("classpath:2.properties")
@PropertySource("...")
public class XConfiguration{
    ...
}

如果我们使用低于 Java 8 版本的 Java 开发 Spring 应用,又想声明多个 @PropertySource,则需要借助 @PropertySources 的帮助了,代码如下所示:

@PropertySources({ @PropertySource("classpath:1.properties"), @PropertySource("classpath:2.properties"), ...})
public class XConfiguration{
    ...
}

@Import 与 @ImportResource
在 XML 形式的配置中,我们通过 的形式将多个分开的容器配置合到一个配置中,在 JavaConfig 形式的配置中,我们则使用 @Import 这个 Annotation 完成同样目的:

@Configuration
@Import(MockConfiguration.class)
public class XConfiguration {
    ...
}

@Import 只负责引入 JavaConfig 形式定义的 IoC 容器配置,如果有一些遗留的配置或者遗留系统需要以 XML 形式来配置(比如 dubbo 框架),我们依然可以通过 @ImportResource 将它们一起合并到当前 JavaConfig 配置的容器中。

5.SpringBoot中@SpringBootApplication注解的三体结构解析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic
@interface
SpringBootApplication{...}

虽然它的定义使用了多个 Annotation 进行元信息标注,但实际上对于 SpringBoot 应用来说,重要的只有三个 Annotation,而“三体”结构实际上指的就是这三个 Annotation:
@Configuration
@EnableAutoConfiguration
@ComponentScan

所以,如果我们使用如下的 SpringBoot 启动类,整个 SpringBoot 应用依然可以与之前的启动类功能对等:

@Configuration
@EnableAutoConfiguration
@ComponentScanpublic
class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

但每次都写三个 Annotation 显然过于繁琐,所以写一个 @SpringBootApplication 这样的一站式复合 Annotation 显然更方便些。

5.1@Configuration
这里的 @Configuration 对我们来说并不陌生,它就是 JavaConfig 形式的 Spring IoC 容器的配置类使用的那个 @Configuration,既然 SpringBoot 应用骨子里就是一个 Spring 应用,那么,自然也需要加载某个 IoC 容器的配置,而 SpringBoot 社区推荐使用基于 JavaConfig 的配置形式,所以,很明显,这里的启动类标注了 @Configuration 之后,本身其实也是一个 IoC 容器的配置类!

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration {
    @Bean
    public Controller controller() {
        return new Controller();
    }
}

public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoConfiguration.class, args);
    }
}

5.2@EnableAutoConfiguration
@EnableAutoConfiguration 其实也没啥“创意”,各位是否还记得 Spring 框架提供的各种名字为 @Enable 开头的 Annotation 定义?

比如 @EnableScheduling、@EnableCaching、@EnableMBeanExport 等,@EnableAutoConfiguration 的理念和“做事方式”其实一脉相承,简单概括一下就是,借助 @Import 的支持,收集和注册特定场景相关的 bean 定义:
@EnableScheduling 是通过 @Import 将 Spring 调度框架相关的 bean 定义都加载到 IoC 容器。
@EnableMBeanExport 是通过 @Import 将 JMX 相关的 bean 定义加载到 IoC 容器。

而 @EnableAutoConfiguration 也是借助 @Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 IoC 容器,仅此而已!

@EnableAutoConfiguration 作为一个复合 Annotation,其自身定义关键信息如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

其中,最关键的要属 @Import(EnableAutoConfigurationImportSelector.class),借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。

借助于 Spring 框架原有的一个工具类:SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以“智能”地自动配置功效才得以大功告成!

6.SpringFactoriesLoader详解

SpringFactoriesLoader 属于 Spring 框架私有的一种扩展方案(类似于 Java 的 SPI 方案 java.util.ServiceLoader),其主要功能就是从指定的配置文件 META-INF/spring.factories 加载配置,spring.factories 是一个典型的 java properties 文件,配置的格式为 Key=Value 形式,只不过 Key 和 Value 都是 Java 类型的完整类名(Fully qualified name),比如:

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2 然后框架就可以根据某个类型作为 Key 来查找对应的类型名称列表了:
public abstract class SpringFactoriesLoader {
// …
public static List loadFactories(Class factoryClass, ClassLoader classLoader) {

}
public static List loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

}
// …
}
对于 @EnableAutoConfiguration 来说,SpringFactoriesLoader 的用途稍微不同一些,其本意是为了提供 SPI 扩展的场景,而在 @EnableAutoConfiguration 的场景中,它更多是提供了一种配置查找的功能支持,即根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key,获取对应的一组 @Configuration 类:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
\org.springframework.boot.autoconfigure.admin.SpringApplicationAdmin- JmxAutoConfiguration,
\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
\org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,
\org.springframework.boot.autoconfigure.PropertyPlaceholderAuto- Configuration,
\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
\org.springframework.boot.autoconfigure.cassandra.CassandraAuto-Configuration,
\org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
\org.springframework.boot.autoconfigure.context.ConfigurationProperties-AutoConfiguration,
\org.springframework.boot.autoconfigure.dao.PersistenceException-TranslationAutoConfiguration,
\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-DataAutoConfiguration,
\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-RepositoriesAutoConfiguration,
\...

以上是从 SpringBoot 的 autoconfigure 依赖包中的 META-INF/spring.factories 配置文件中摘录的一段内容,可以很好地说明问题。

所以,@EnableAutoConfiguration 自动配置的魔法其实就变成了:从 classpath 中搜寻所有 META-INF/spring.factories 配置文件,并将其中 org.spring-framework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射(Java Reflection)实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。

7.@ComponentScan
因为原则上来说,作为 Spring 框架里的“老一辈革命家”,@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。

8.@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注解。

发布了16 篇原创文章 · 获赞 12 · 访问量 152

猜你喜欢

转载自blog.csdn.net/qq_36435947/article/details/105132658
今日推荐