欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring-boot特性(1)

程序员文章站 2022-05-22 08:32:34
...

初始化Spring-boot

最佳的文档结构。

com
 +- example
     +- myproject
         +- Application.java
         |
         +- domain
         |   +- Customer.java
         |   +- CustomerRepository.java
         |
         +- service
         |   +- CustomerService.java
         |
         +- web
             +- CustomerController.java

spring-boot还是建议按照标准的controller-service-dao结构分层。有一个独立的Application.java作为系统启动入口。

引入

这里仅仅以Maven为例:

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework</groupId>
    <artifactId>gs-spring-boot-demo</artifactId>
    <version>0.1.0</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
	    	<version>1.5.9.RELEASE</version>
        </dependency>
    </dependencies>
    <properties>
        <java.version>1.8</java.version>
    </properties>
</project>

引入了 spring-boot-starter-web,基本上开发一个web应用所需的包都会引入其中。如果需要使用JPA等等功能需要另外引入对应的starter。spring-boot用pom的方式整合了许多开箱即用的工具,官方称之为starter特性,后面会介绍什么是starter。

启动

Spring boot提供了多种启动方式,最简单的方式是在main方法中调用 SpringApplication.run 方法即可启动Spring Boot。当然,run方法必须要配合相关的注解才能实现Spring Boot目标功能。关于spring boot打包以及jara -jar或者CLI启动,后续的博文会介绍。

DEBUG模式

通常情况下,启动Spting Boot时日志输出都是生产模式(关闭DEBUG级别的日志),在启动参数中增加--debug参数即可开启调试模式的日志输出。

Eclipse的设置:工程右键->Debug As->Debug Configurations->打开Arguments选项->在Program arguments中增加 --debug 参数。

Spring-boot特性(1)

纯Java配置——@Configuration

@Configuration是一个用于类的注解,他可以替换原来定义在xml文件中的spring配置。当为某一个类增加这个注解后,会将其视作一个源自配置文件的Bean
其实springioc容器一直以来都没多大变化,延续基于单例的IOC的机制一直向下衍生功能线,不管使用什么注解,基本上所有用到的实例都是一个Bean,所有的Bean都放在同一个的IOC容器中(当然也可以创建多个容器,但是似乎并没什么应用需要这么特殊的实现)。Spring Xml配置是根据xml的描述生成多个Bean,而引入@Configuration注解使得配置可以彻底基于Java代码。

自动配置注入——@EnableAutoConfiguration 

这个注解用于在SpringIOC容器中启用自动推导配置功能(使用boot中定义的默认配置)。其执行过程实际就是根据classpath中的包来决定是否需要注入某个用于资源配置的Bean来支持其工作。比如在classpath中发现了tomcat-embedded.jar 这个包,那么可以推定需要启用tomcat的嵌入工具,那么boot会帮助我们创建一个 TomcatEmbeddedServletContainerFactory 的实例作为Bean放置到容器中以供其使用。我们可以通过注解的  exclude() 和 excludeName() 方法告知不需要自动生成某些配置。也可以通过声明  spring.autoconfigure.exclude JVM参数。 

实质上Spring-Boot-Web就是一个更加自动化的Spring-Webmvc——不用整合servlet容器并且分分钟启动。而Spring-Boot最大的亮点之一就是根据引入的包自动注入配置。如果打开--debug模式会看到很多匹配相关的内容输出。下面是自动匹配输出的一些内容,为了便于说明只选取了很小一部分,实际输出的内容比这个多得多。

=========================
AUTO-CONFIGURATION REPORT
=========================

Positive matches:
-----------------

   DispatcherServletAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)

   DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched:
      - @ConditionalOnClass found required class 'javax.servlet.ServletRegistration'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition)


Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

AUTO-CONFIGURATION REPORT 开始就是匹配日志,Positive matche 之后的表示匹配上的配置,Negative matches之后表示未匹配上的配置。每一项的内容都详细说明了匹配上的依赖关系和未匹配的原因。

包扫描——@ComponentScan

@ComponentScan注解用于设定IOC容器加载Bean的扫描路径,等价于xml配置中的<context:component-scan>元素(@ComponentScan属于Spring Framework的Context模块)。在指定的路径中会将@Component及其子类限定的类(如@Service@Repository@Controller)作为一个Bean添加到IOC容器中。

@ComponentScan中包含多个参数,例如basePackagesbasePackageClassesexcludeFilters等,都是用于定义扫描的包路径或限定名。如果没有为@ComponentScan注解设定任何参数,则会扫描当前包以及所有子孙包。

Spring-boot整合——@SpringBootApplication

@SpringBootApplication注解整合了@Configuration@EnableAutoConfiguration@ComponentScan的效果。当为一个入口类(包含启动的main方法)定义一个@SpringBootApplication注解后,意味着增加了上述三个注解的功能——1)当前类是一个资源Bean,2)启用spring boot的自动推导配置(开箱即用)、3)自动扫描入口类之后的所有子包。

所以下面2种写法实现的效果是几乎一致的(在@SpringBootApplication中对@ComponentScan做了参数限定,所以只能说几乎一致。):

@EnableAutoConfiguration
@ComponentScan
@Configuration
public class Demo{
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }
}
@SpringBootApplication
public class Demo{
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Demo.class, args);
    }
}

开箱即用——Starter

Spring Boot通过Maven的方式提供了一系列开箱即用(一站式服务)的工具,包括MQ、AOP、JPA等,文档上将这个特性命名为Starter。前面 引入 部分使用的 spring-boot-starter-web 就是一个Starter 。Starter 特性并没有什么新的技术,仅仅是通过pom文件的方式引用了一些必要的包,然后在引入之后通过Spring Boot的自动推导配置为引入的jar包注入必要的配置Bean。官网的表13.1 列举了所有Sprint Boot官方提供的Starter

当然除了官方提供的Starter我们还可以自定义。不过需要注意的是命名规则——由官方提供的Starter命名规则为spring-boot-starter-*,而自定义(第三方提供)的规则为 acme-spring-boot-starter-*。自定义的Starter在某些使用需要额外指定自动配置功能,详情请看 关于自定义Starter的说明

逐渐替换默认配置

这也是Spring Boot的最佳实践之一。虽然它提供了相当丰富的默认配置,但是并不是所有的东西用默认配置就可以解决。Spring Boot建议根据需要逐渐替换工程所需的配置。例如默认情况下工程引入了 HSQLDB ,并且没有配置DataSource,那么我们所有的数据库操作(例如JPA)都会直接使用HSQLDB内存数据库。如果我们向容器注入了DataSource实例,那么我们定义的配置将会替换默认配置。

开发Spring-boot

全局定义开发环境——spring-boot-devtools

spring-boot-devtools(以下简称Devtools)为开发环境提供了许多快速便捷的设置,仅需要增加一个依赖即可实现开发所需的配置,以Maven为例:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

在引入他之后当前环境自动变为开发环境。需要注意的是如果运行完整打包的工程Spring Boot不启用任何Devtools相关的功能(实际上打包工具spring-boot-maven-plugin默认情况下不会去打包Devtools),为了防止Devtools的作用域污染子项目,我们最好增加 Maven optional 标记。

下面介绍Devtools具体提供了什么功能。

1.代码修改与热部署

Devtools 的一项功能就是能够监控代码的变更,并在发现变更时“热部署”最新的代码。不过这里的热部署不是Jvm bytecode级别的热部属,也和OGSI没任何关系。

根据官方的介绍是实现了两个ClassLoader——BaseClassLoader和RestartClassLoader(推断这2个ClassLoader应该破坏了双亲委派模型)。第一次启动JVM时所有的.class文件和.jar文件中的类都用BaseClassLoader加载,然后在开发的过程中凡是变更过的.class 文件都会被标记,这些被标记的.class之后都会使用RestartClassLoader加载。在初始化一个类时,被标记了用RestartClassLoader加载的Class<?>实例,没有被标则委派给BaseClassLoader加载,每次发起“热部署”时都会新建一个RestartClassLoader重新加载类,这样可以保证变更过的代码都是重新加载的。

Devtools进行“热部署”时会调用spring的上下文挂钩(spring context hook)来重新部署IOC容器。如果你关闭了它——SpringApplication.setRegisterShutdownHook(false),“热部署”无法将新加载的类实例部署到IOC容器中导致代码替换失败。

Spring-boot特性(1)

上面是开发过程中Jconsole的输出,每一次修改代码保存都会新增一些非堆(方法区)的空间,这说明重新加载了新的字节码数据并解析到非堆中。

jvm环境中classPath路径下的任何文件修改都会触发Devtools 的热部署,某些时候并不需要都监控所有的路径,例如/resources、/static、/template等,我们可以通过设定spring.devtools.restart.exclude属性来排除热部署监控的位置。例如:

spring.devtools.restart.exclude=static/**

此外,使用“热部署”时还需注意以下几点(个个都有可能是引发问题的坑啊):

  1. 属性spring.devtools.restart.additional-paths属性可以用来增加监控classpath之外的路径。
  2. Devtools内嵌了LiveReload,如果不想启用它可以将spring.devtools.livereload.enabled属性设置为fasles
  3. Devtools会自动忽略某些包的扫描,例如spring-boot、spring-boot-devtools、spring-boot-autoconfigure、spring-boot-actuator、spring-boot-starter
  4. Devtools会修改SpringContext指定的ResourceLoader,如果自定义了一个新的ResourceLoader,修改后的getResource方法将无法生效。
  5. spring.devtools.restart.enabled属性设置为false可以关闭Devtools的“热部署”功能。
  6. 某些IDE整合了代码监控功能,可以通过spring.devtools.restart.trigger-file属性指定要监控的文件,只有这个文件发生变更时才会触发Devtools进行全局的文件变更检查。
  7. 前面介绍了Devtools的“热部署”是通过2个ClassLoaderBaseClassLoader、RestartClassLoader)实现的,默认情况下.jar包中的类只会使用BaseClassLoader加载。我们可以通过在根目录新建一个META-INF/spring-devtools.properties文件,然后在其中设置restart.exclude. 和 restart.include. 属性来指定被 RestartClassLoader 加载的 .jar 类。详情见官网例子

2.缓存启用和停用

很多框架、工具都提供了缓存功能,在生产环境中对某些热数据进行适当的缓存能够有效的提高性能。但是在开发环境这些缓存反而会影响我们验证功能。所以Devtools全局提供了缓存管理,并默认关闭大部分工具或框架的缓存。开发人员可用通过设置运行环境properties的方式来指定缓存功能,例如:

System.setProperty("spring.thymeleaf.cache", "true");

就可以指定启用thymeleaf模板引擎的缓存。缓存管理相关的配置请看 github上spring-boot-devtools环境设置相关的代码

3.文件配置

除了使用参数,我们可以把Devtools的所有配置写到$HOME目录下一个".spring-boot-devtools.properties"的文件中。例如:

spring.devtools.reload.trigger-file=.reloadtrigger

4.远程开发

Devtools除了提供本机开发的增强功能之外,还增加了强大的远程开发与调试功能。

首先,我们需要在打包的时候连同spring-boot-devtools一起打包并发布,而spring-boot-maven-plugin默认不是打包Devtools的,所以我们需要将Pom文件的plugins配置简单修改一下:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludeDevtools>false</excludeDevtools>
            </configuration>
        </plugin>
    </plugins>
</build>

发布之前需要设置一个属性:

spring.devtools.remote.secret=mysecret

 特别需要注意:这个属性会带来安全风险,所以仅仅用于测试和开发,切记不要用于生产运行。

将打好的包部署到远程服务器即可,我们称之为服务端。

然后,要在本地开发环境配置一个客户端

客户端需要配合IDE一起使用。假设你的工程名字为my-app在Eclipse下进行下面的配置:

  1. Run 菜单栏目里选择Run Configurations...
  2. 创建一个新的 Java Application(在Java Application处右键,然后选择new)。
  3. Project一栏里选择my-app工程。
  4. Main Class一栏里使用org.springframework.boot.devtools.RemoteSpringApplication作为main方法类。
  5. Arguments选项卡中,在Program arguments中添加服务端的地址(类似https://myapp.cfapps.io的格式)

最后,启用了spring.devtools.remote.secret之后,客户端会监控本地classpath下文件变更。一旦触发“热部署”它会先在本地完成,然后将变更的内容推送到远程服务端触发“热部署”。就像你在本地开发一样,这对开发一些回调应用和不同环境的调试带来了极大的便利。

还有,Devtools在基于jdwp远程调式的基础上进行了扩展,提供支持HTTP传输远程调试信息。绝大部分情况下都能使用Java的远程调试能解问题,如有特殊需求(如用到docker等),可以看 这里

转载于:https://my.oschina.net/chkui/blog/1609325