开发颠覆者SpringBoot实战---------SpringBoot的基础学习

一、介绍

随着动态语言的流行,常规的java开发S显得格外笨重:繁多的配置、低下的开发效率、复杂的部署流程以及第三方技术集成难度大。为了解决这些问题,SpringBoot应运而生。它使用“习惯优化配置的理念”(项目中存在大量的配置,还内置一个习惯性配置,让你无需手动进行配置)来让项目快速运行起来,很容易创建一个独立运行、准生产级别的项目,可以不用或者需要很少的Spring配置。

1、核心功能:

  • 独立运行Spring项目
  • 内嵌Servlet容器
  • 提供start简化Maven配置
  • 自动配置Spring
  • 准生产的应用监控

2、SpringBoot快速搭建:

下载http://start.spring.io,导入项目,或者自行创建。

简单演示:

直接在入口类编写控制器:

@SpringBootApplication
@RestController
public class Springboot2Application {
    @RequestMapping("/")
    public String index(){
        return "hello spring boot";
    }
    public static void main(String[] args) {
        SpringApplication.run(Springboot2Application.class, args);
    }
}

运行主方法即可。

@SpringBootApplication注解组合了三个有用的注解:

  • Spring的 @Configuration:Spring基于java的配置类
  • Spring的 @ComponentScan:开启组建扫描
  • SpringBoot的 @EnableAutoConfiguration:开启Springboot的自动配置

3、使用起步依赖

Spring Boot通过提供众多起步依赖降低项目依赖的复杂度。起步依赖本质上是一个Maven项目对象模型(Project Object Model, POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。很多起步依赖的命名都暗示了它们提供的某种或某类功能。如:

  • org.springframework.boot:spring-boot-starter-web:添加web项目依赖
  • org.springframework.boot:spring-boot-starter-thymeleaf:添加Thymeleaf模块试图依赖
  • org.springframework.boot:spring-boot-starter-data-jpa:添加Spring Data JPA依赖
  • org.springframework.boot:spring-boot-starter-test:添加测试依赖

我们并不需要指定版本号,起步依赖本身的版本是由正在使用的Spring Boot的版本来决定的,而起步依赖则会决定它们引入的传递依赖的版本。不知道自己所用依赖的版本,你多少会有些不安。你要有信心,相信Spring Boot经过了足够的测试,确保引入的全部依赖都能相互兼容。

通过传递依赖,添加这些依赖就等价于加了一大把独立的库。这些传递依赖涵盖了Spring MVC、Spring Data JPA、 Thymeleaf等内容,它们声明的依赖也会被传递依赖进来。最值得注意的是,这四个起步依赖的具体程度恰到好处。我们并没有说想要Spring MVC,只是说想要构建一个Web应用程序。我们并没有指定JUnit或其他测试工具,只是说我们想要测试自己的代码。 Thymeleaf和Spring Data JPA的起步依赖稍微具体一点,但这也只是由于没有更模糊的方法声明这种需要。

同时我们也可以自己声明其他的依赖,如果与传递依赖重复,会自动覆盖传递依赖,但要注意版本的兼容性,但要知道,起步依赖中各个依赖版本之间的兼容性都经过了精心的测试。应该只在特殊的情况下覆盖这些传递依赖(比如新版本修复了一个bug)。

二、核心

1、基本配置

SpringBoot的全局配置文件application.properties,可以对一些默认配置进行修改,如修改端口号和访问路径:

server.port=9090
server.servlet.context-path=/springboot2

如有一些特殊要求必须使用xml配置,可以使用@ImportResource来加载xml文件:

@ImportResource({"classpath:other.xml"})

2.、外部配置

常规属性配置:全局配置文件中增加

springboot.author=think
springboot.time=2018-5-18

修改入口类:

    @Value("${springboot.author}")
    private String author;
    @Value("${springboot.time}")
    private String time;
    @RequestMapping("/")
    public String index(){
        return "hello spring boot,Springboot.author=" + author + ",Springboot.time=" + time;
    }

类型安全配置: SpringBoot还允许使用properties文件作为参数引入:(prefix:引入的配置文件参数前缀,locations配置文件位置)

/**
 * 作为参数引入类
 * @author think
 */
@Component
@ConfigurationProperties(prefix = "springboot")
//prefix指定配置文件中参数的前缀,locations执行配置文件位置
public class MyAuthor {
    private String author;
    private String time;
    public String getTime() {
        return time;
    }
    public void setTime(String time) {
        this.time = time;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
}
/**
 * 修改入口类
 * @author think
 */
    @Autowired
    private MyAuthor author;
    @RequestMapping("/")
    public String index(){
        return "hello spring boot,Springboot.author=" + author.getAuthor() + ",Springboot.time=" + author.getTime();
    }

3、日志配置

SpringBoot支持多种日志框架,并已为当前使用的日志框架做好了配置,默认情况下使用Logback作为日志框架。

增加配置:

logging.file=F:/springboot/log.log
logging.level.org.springframework.web=debug

4、Profile配置

Profile是Spring用来针对不同环境来使用不同的配置。

增加两个配置文件:

application-dev.properties

server.port=8085

application-prod.properties

server.port=8088

在全局配置文件application.properties中增加:

spring.profiles.active=dev

启动后就可以看见端口号的变化。spring.profiles.active是固定api,根据增加的不同环境的配置文件application-XX.properties中的XX来识别不同环境。

三、SpringBoot原理

1、运作原理

关于SpringBoot运作原理,体现在@EnableAutoConfiguration这个注解中,源码都在org.springframework.boot.autoconfigure包中。下面是@EnableAutoConfiguration注解的源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

这里的关键功能是@Import注解导入的配置功能,下面是一段AutoConfigurationImportSelector的源码:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

AutoConfigurationImportSelector使用SpringFactoriesLoader.loadFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包,这个jar包就在自动配置的核心包org.springframework.boot.autoconfigure中。

2、核心注解

spring.factories文件中声明了有哪些自动配置,随意打开一个autoconfigure的文件,都会有下面的条件注解:

  • @ConditionalOnBean:当容器中指定了Bean的条件下
  • @ConditionalOnClass:当类路径下有指定类的条件下
  • @ConditionalOnExpression:基于SpEL表达式作为判断条件
  • @ConditionalOnJava:基于JVM版本作为判断条件
  • @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
  • @ConditionalOnMissingBean:当容器里没有指定Bean的情况下
  • @ConditionalOnMissingClass:当类路径没有指定类的条件下
  • @ConditionalOnNotWebApplication:当前木箱不是Web项目的条件下
  • @ConditionalOnProperty:指定的属性是否有指定的值
  • @ConditionalOnResource:类路径是否有指定的值
  • @ConditionalOnSingleCandidate:当指定Bean容器中只有一个,或者虽然有多个暗示指定首选的Bean
  • @ConditionalOnWebApplication:当前项目是Web项目的条件下

这些注解都是组合了@Conditional元注解,只是使用了不用的条件(condition),如ConditionalOnWebApplication:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
    ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;
    public static enum Type {
        ANY,
        SERVLET,
        REACTIVE;
        private Type() {
        }
    }
}

ConditionalOnWebApplication注解的条件是OnWebApplicationCondition,下面是OnWebApplicationCondition中的一段代码:

private ConditionOutcome isServletWebApplication(ConditionContext context) {
    Builder message = ConditionMessage.forCondition("", new Object[0]);
    if (!ClassUtils.isPresent("org.springframework.web.context.support.GenericWebApplicationContext", context.getClassLoader())) {
        return ConditionOutcome.noMatch(message.didNotFind("web application classes").atAll());
    } else {
        if (context.getBeanFactory() != null) {
            String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
            if (ObjectUtils.containsElement(scopes, "session")) {
                return ConditionOutcome.match(message.foundExactly("'session' scope"));
            }
        }
        if (context.getEnvironment() instanceof ConfigurableWebEnvironment) {
            return ConditionOutcome.match(message.foundExactly("ConfigurableWebEnvironment"));
        } else {
            return context.getResourceLoader() instanceof WebApplicationContext ? ConditionOutcome.match(message.foundExactly("WebApplicationContext")) : ConditionOutcome.noMatch(message.because("not a servlet web application"));
        }
    }
}

可以看出它的判断条件是:

  1. GenericWebApplicationContext是否在路径中;
  2. 容器中是否有名为session的scope;
  3. 当前容器的Environment是否是ConfigurableWebEnvironment;
  4. 当前容器的getResourceLoader是否是WebApplicationContext(ResourceLoader是ApplicationContext的顶级接口之一)
  5. 构造ConditionOutcome对象,通过isMatch再确定。

3、实例分析

在常规项目中配置http编码时候是在web.xml中配置一个filter:

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

自动配置要满足两个条件:

  1. 能配置CharacterEncodingFilter这个Bean
  2. 能配置encoding和forceEncoding这两个参数

SpringBoot也是基于这一点来实现的,在全局配置文件application.properties中直接配置:

//默认编码方式为UTF-8
spring.http.encoding.charset=utf-8
//设置forceEncoding
spring.http.encoding.force=true

上述的属性信息通过类型安全的配置,注入到下面这个类中:

@ConfigurationProperties(
    prefix = "spring.http.encoding"
)//在application.properties配置中,前缀是spring.http.encoding的
public class HttpEncodingProperties {
    ......
}

通过调用上述的配置,并根据条件配置CharacterEncodingFilter的Bean:

@Configuration
//开启属性注入,通过@EnableConfigurationProperties声明,使用@Autowired注入
@EnableConfigurationProperties({HttpEncodingProperties.class})
@ConditionalOnWebApplication(type = Type.SERVLET)
//当CharacterEncodingFilter在类路径的条件下
@ConditionalOnClass({CharacterEncodingFilter.class})
//当设置spring.http.encoding=enabled的情况下,如果没有设置默认为true,即条件符合
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
    private final HttpEncodingProperties properties;
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = properties;
    }
    @Bean//配置这个Bean
    @ConditionalOnMissingBean//当容器没有这Bean时候新建Bean
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.RESPONSE));
        return filter;
    }
    @Bean
    public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
        return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
    }
    private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
        private final HttpEncodingProperties properties;
        LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {
            this.properties = properties;
        }
        public void customize(ConfigurableServletWebServerFactory factory) {
            if (this.properties.getMapping() != null) {
                factory.setLocaleCharsetMappings(this.properties.getMapping());
            }
        }
        public int getOrder() {
            return 0;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值