SpringBoot两天

SpringBoot讲义

什么是SpringBoot?

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

Spring 从4.x版本开始提倡java配置和注解结合使用,慢慢离开xml繁琐的配置,所以要入门SpringBoot,需要从java配置开始。在Spirng Boot和Spring Cloud中,大量使用了注解与JavaConfig。

JavaConfig诞生的原因是什么?

Spring IOC有一个非常核心的概念——Bean。由Spring容器来负责对Bean的实例化,装配和管理。早些时候XML是用来描述Bean最为流行的配置方式。随着Spring的日益发展,由于Spring会把几乎所有的业务类都以Bean的形式配置在XML文件中,造成了大量的XML文件。使用XML来配置Bean失去了编译时的类型安全检查。大量的XML配置使得整个项目变得更加复杂。

当随着JAVA EE 5.0的发布,其中引入了一个非常重要的特性——Annotations(注解)。注解是源代码的标签,这些标签可以在源代码层进行处理或通过编译器把它熔入到class文件中。在JAVA EE 5以后的版本中,注解成为了一个主要的配置选项。Spring使用注释来描述Bean的配置与采用XML相比,因类注解是在一个类源代码中,可以获得类型安全检查的好处。可以良好的支持重构。

JavaConfig就是使用注解来描述Bean配置的组件。JavaConfig 是Spring的一个子项目。

JavaConfig常见的注解有哪些?

@Configuration注解

该注解贴在类上,表示是一个配置类。相当于xml配置

@Bean注解

Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名

也可以执行生命周期的回调函数,类似@PostConstruct(初始化方法) 和 @PreDestroy(销毁方法)的方法

@Scope注解

@Scope 注解:用来设置交给spring容器管理的Bean是单例方式,还是多例方式
相当于bean标签中的scope属性

@ComponentScan注解

创建一个配置类,在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的xml

@Import注解

@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名

@ImportResource注解

@ImportResource和@Value进行资源文件读取

@PropertySource, @value 注解

Spring框架提供了PropertySource注解,目的是加载指定的属性文件 @Value 可以获取配置文件中的值

@Profile,@ActiveProfile注解

@profile注解是spring提供的一个用来标明当前运行环境的注解。我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,QA测试是一套环境,线上部署又是一套环境。这样从开发到测试再到部署,会对程序中的配置修改多次,尤其是从QA到上线这个环节,让QA的也不敢保证改了哪个配置之后能不能在线上运行。 为了解决上面的问题,我们一般会使用一种方法,就是配置文件,然后通过不同的环境读取不同的配置文件,从而在不同的场景中跑我们的程序。

那么,spring中的@profile注解的作用就体现在这里。在spring使用DI来依赖注入的时候,能够根据当前制定的运行环境来注入相应的bean。最常见的就是使用不同的DataSource了。

JavaConfig配置的应用场景

在IOC中

需求一:把一个Bean交给Spring管理

回顾使用xml配置方式

applicationContext.xml:






SomeBean:

public class SomeBean {
    public void print() {
        System.out.println("print xmlConfig");
    }
}

XMLConfigTest:

public class XMLConfigTest {
    
    @Test
    public void testXMLConfig() throws Exception {
        // 加载spring的配置文件
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("cn/wolfcode/javaconfig/_01_xmlconfig/applicationContext.xml");

        // 通过spring容器获取SomeBean
        SomeBean someBean = ctx.getBean(SomeBean.class);

        // 执行print方法
        someBean.print();
    }

}

使用javaConfig方式

@Configuration :spring的配置标签,打在哪个类上,这个类就表示spring的配置类, 相当于applicationContext.xml文件

@Bean :打在方法上方法返回的实例对象交给spring容器管理,方法的名称就是Bean标签中的id值。
相当于配置文件中bean标签。
如:


ApplicationConfig:

@Configuration
public class ApplicationConfig {

    @Bean
    public SomeBean someBean(){
        return new SomeBean();
    }
}

SomeBean:

public class SomeBean
{
    public void print(){
        System.out.println("print JavaConfig");
    }
}

这里不再是通过xml配置文件获取spring容器对象,而是通过加载配置类。因此应该选用 AnnotationConfigApplicationContext 类,加载配置类的字节码文件。

JavaConfigTest:

public class JavaConfigTest {
    @Test
    public void testJavaConfig() throws Exception {

        //  加载配置类
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(ApplicationConfig.class);
        // 从spring容器中获取SomeBean对象
        SomeBean someBean = ctx.getBean(SomeBean.class);
        // 打印
        someBean.print();
    }
}

@ComponentScan

使用该注解改良代码。

通过上述的案例,其实我们发现,上面的写法也显得很麻烦。还不如使用配置文件简洁。
在配置类中,通过给方法添加注解@Bean,把该方法的返回值交给spring管理。代码量太大,如果需要交给Spring管理很多Bean的话,我们要写很多个方法,在配置类中。

在javaConfig中,给我们提供一个注解@ComponentScan,可以把需要管理的Bean 贴上注解@Component,然后通过在配置类上面贴注解@ComponentScan来进行扫描贴了注解的Bean,然后把这些Bean交给Spring管理。

@ComponentScan 这个标签也相当于之前在配置文件中的配置的

  
//开启 ioc组件自动扫描功能

注意:@ComponentScan注解也可以通过配置指定扫描哪个包。

    如:@ComponentScan("cn.wolfcode")
    表示扫描“cn.wolfcode”这个包以及它下面的子包,把包中的贴了注解的Bean ,交给spring容器管理。
    如果不指定包,表示扫描当前包以及下面的子包

改良方案如下:

ApplicationConfig:

@Configuration
@ComponentScan("cn.wolfcode.javaconfig._03_scan")
public class ApplicationConfig {

}

JavaConfigTest:

public class JavaConfigTest {
    @Test
    public void testJavaConfig() throws Exception {
        //  加载配置类
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(ApplicationConfig.class);
        // 从spring容器中获取SomeBean对象
        SomeBean someBean = ctx.getBean(SomeBean.class);
        // 打印
        someBean.print();
    }
}

SomeBean:

@Component
public class SomeBean
{
    public void print(){
        System.out.println("print ScanConfig");
    }
}

@Bean中的一些属性的补充

属性:initMethod

该属性相当于bean标签中的initMethod属性,用来设置bean被初始化所调用的方法。
如:


属性:destroyMethod

该属性相当于bean标签中的destroyMethod 属性,用来设置bean被销毁时,执行的操作。
如:


注意:必须是容器正常关闭,销毁bean,才会调用销毁方法。

使用普通的junit测试,是不能关闭spring容器的。原因是,junit测试方法执行完毕,进程就结束了。所以导致spring容器不能正常关闭。

正常关闭容器的方案:

方案一: 容器使用完手动调用close方法,进行关闭。

如:

//获取容器
AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(ApplicationConfig.class)
// 关闭容器
ctx.close();

方案二:使用java1.7的新特性try(),来进行关闭。

如:

try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class)) {
    SomeBean someBean = ctx.getBean(SomeBean.class);
    System.out.println(someBean);
} catch (Exception e) {
    e.printStackTrace();
}

方案三:使用Spring整合的junit单元测试。

如:

在测试类上面添加整合单元测试的注解. 
注意:因为不再是加载配置文件的方式,所以需要使用classes属性加载配置类信息。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)

@Scope注解

@Scope 该注解贴方法上表示是多例还是单例, 相当于配置文件中bean标签的属性scope 的作用。

@Scope("prototype") 表示多例
@Scope("singleton") 表示单例,如果配置@Scope标签,其默认值也是单例。

一般开发中中使用的是单例。

在DI中

需求二:把一个Bean注入给另外一个Bean中

方式一:直接调用方法的形式

 @Bean
 public SomeBean someBean() {
    SomeBean someBean = new SomeBean();
    System.out.println("someBean create");
    someBean.setOtherBean(otherBean());
    return someBean;
  }
  
 @Bean
 public OtherBean otherBean() {
    OtherBean otherBean = new OtherBean();
    System.out.println("otherBean create");
    return otherBean;
 }

方式二:使用传参的形式

@Bean
public SomeBean someBean(OtherBean otherBean){
   someBean.setOtherBean(otherBean);
    return someBean;
}

@Bean
public OtherBean otherBean(){
    return new OtherBean();
}

注意:

@Bean标签的生命周期
在一个@Configuration类中,重复调用一个方法,得到的对象默认也是singleton的

配置类/文件的导入

为何需要导入配置类/配置文件?

在我们实际开发中,项目很大的情况下,采用JavaConfig配置类的方式,为了便于开发和管理,我们的配置类也会有很多个。那么多个配置类之间,只有一个主配置类,这时就需要,把其他的配置类或者配置文件导入到主配置类上面。所以我们需要导入配置类或者配置文件。

导入配置类/配置文件需要哪些注解?

@Import注解

比如一个场景:我们之前项目中可以有多个 配置文件,那么在javaConfig中,也可能存在多个配置类,那么在很多个配置类的情况下,应该有一个主配置类,其他的配置都关联到主配置类上面,那么问题是怎么关联其他配置类呢? 使用@Import注解

@ImportResource注解

比如一个场景:有时候根据项目的需求和对项目的设计,我们可能需要配置类和配置文件混用的情况。例如,使用spring的AOP帮我们处理事务。可能使用javaConfig的方式,比较繁琐,修改代码太过于频繁,那么这块我们就可以继续使用xml文件的方式统一处理。那么这种情形就需要,在主配置类中关联配置文件了。

@Import注解的用法:

导入普通类

例子如下:

    1. 新建一个TestA
public class TestA {
    public void fun(String str) {
        System.out.println(str);
} 
public void printName() {
    System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
    }
}
    1. 新建一个ImportConfig,在类上面加上@Configuration,加上@Configuration是为了能让Spring 扫描到这个类,并且直接通过@Import引入TestA类
@Import({TestA.class})
@Configuration
public class ImportConfig {
}

导入带有@Configuration的配置类

    1. 新建TestB
@Configuration
public class TestB {
    public void fun(String str) {
        System.out.println(str);
    }
    public void printName() {
        System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
    }
}

    1. 在ImportConfig.class里面直接引入TestB
@Import({TestA.class,TestB.class})
@Configuration
public class ImportConfig {
} 

通过ImportSelector 方式导入的类

    1. 新建TestC.class
public class TestC {
  public void fun(String str) {
        System.out.println(str);
  }
  public void printName() {
    System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
  }
}
    1. 新建SelfImportSelector.class 实现ImportSelector 接口,注入TestC.class
public class SelfImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.test.importdemo.TestC"};
    }
}
    1. ImportConfig上面引入SelfImportSelector.class
@Import({TestA.class,TestB.class,SelfImportSelector.class})
@Configuration
public class ImportConfig {

}

通过 ImportBeanDefinitionRegistrar 方式导入的类

    1. 新建TestD.class
public class TestD {
    public void fun(String str) {
         System.out.println(str);
   } 
   public void printName() {
      System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
   }
}
    1. 新建SelfImportBeanDefinitionRegistrar.class,实现接口ImportBeanDefinitionRegistrar,注入TestD.class
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition root = new RootBeanDefinition(TestD.class);
        registry.registerBeanDefinition("testD", root);
    }
}
    1. ImportConfig类上加上导入SelfImportBeanDefinitionRegistrar.class
@Import({TestA.class,TestB.class,SelfImportSelector.class,
    SelfImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportConfig {
}

@ImportResource注解的用法:

有时候根据项目的需求和对项目的设计,我们可能需要配置类和配置文件混用的情况。
比如,使用spring的AOP帮我们处理事务。可能使用javaConfig的方式,比较繁琐,修改代码太过于频繁,那么这块我们就可以继续使用xml文件的方式统一处理。那么这种情形就需要,在主配置类中关联配置文件了。
使用@ImportResource注解
是引入spring配置文件.xml,实现xml配置的装载;
通过locations属性或者value属性,加载对应的xml配置文件,同时需要配合@Configuration注解一起使用,定义为配置类;引入的配置类必须在启动类中扫描到.

例子如下:

ApplicationConfig:

@Configuration
@ImportResource("classpath:cn/wolfcode/javaconfig/_07_import_resource/applicationContext.xml")
public class ApplicationConfig {
    @Bean
    public SomeBean someBean(){
        return new SomeBean();
    }
}

applicationContext.xml:






ImportResourceConfigTest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class ImportResourceConfigTest {
    @Autowired
    private SomeBean someBean;
    @Autowired
    private OtherBean otherBean;
    @Test
    public void testImportResource() throws Exception {
        System.out.println("someBean = " + someBean);
        System.out.println("otherBean = " + otherBean);
    }
}

资源文件的导入

为何需要导入资源文件?

Spring Boot 官网使用的是application.properties文件来实现文件的配置。但是实际情况下一个配置文件是不够用的,比如项目集成redis,mq,以及数据库比如mysql的时候,多个配置文件有利于开发及维护的管理。

导入资源文件需要学习哪些注解?

@PropertySource注解

大多数情况,项目需要配置的内容会比较复杂,这时候需要使用多个文件进行配置
@PropertySource是Spring boot为了方便引入properties配置文件提供的一个注解,可以标注在SpringBoot的启动类上,还可以标注在配置类(使用@Configuration标注的类)上。

@Value注解

@Value用于将外部的值动态地注入Bean中,相当于使用.xml中的如下的代码。通常用于对某个值进行单个的注入,毕竟如果对整个Bean内的值进行注入我们有更好用的@ConfigurtionProperties可以使用。

@ConfigurtionProperties注解

@Value通常用于对某个位置获取配置文件中的的某一个变量的时候,而@ConfigurationProperties用于javaBean和配置文件进行整体映射。上述的使用场景只是说通常情况,具体的使用,还是要根据@Value和@ConfigurationProperties各自的特点来进行判断。

@Value主要作用到变量上,而@ConfigurationProperties,主要用于方法。

@PropertySource注解的用法:

加载指定的属性文件;
注解的属性value,是一个String数组,内容为一个或多个xml/properties文件的路径(相对于classpath);
注解内部的属性使用,配合@value注解.
properties属性文件是key-value格式的,因此使用起来 是采用@value("${key}"); 这种形式

如:

@Configuration
@PropertySource(value = "classpath:db.properties",
ignoreResourceNotFound = false ,
encoding = "utf-8")
public class ApplicationConfig {
}

属性value

value值是设置需要加载的属性文件,可以一次性加载多个。

属性encoding

encoding用于指定读取属性文件所使用的编码,我们通常使用的是UTF-8;

属性ignoreResourceNotFound

ignoreResourceNotFound含义是当指定的配置文件不存在是否报错,默认是false;实际项目开发中,最好设置为false。

Environment对象的使用

回想mybaits配置文件中的 Environment ,表示mybatis的当前环境
Environment :代表了spring的当前环境,在该环境中只有两个东西

1,外部读入的资源文件 :如通过PropertySource 引入的资源文件

2,还有一个东西叫 profile

那么在javaconfig中如何使用 Environment 实现资源文件的注入呢 ?

@Autowired
private Environment evn;

@Configuration
//@PropertySource:把资源文件导入到Spring当前环境
@PropertySource("classpath:jdbc.properties")
public class ApplicationConfig
{
// Environment spring的环境对象,其中有两个东西:① propertis ② profile
 @Bean
public MyDataSource myDataSource(Environment environment){
    return new MyDataSource(environment.getProperty("jdbc.username")
                ,environment.getProperty("jdbc.password")
                ,environment.getProperty("jdbc.driverClassName")
                ,environment.getProperty("jdbc.url"));
        }
}

@Value注解的用法:

我们可以点进去看一下@Value的源码,如下,我们发现它其中只包括一个String类型的变量,也就是说我们@Value注解后面只能接一个String类型的变量,无论什么内容都必须包括在双引号之下。

可注入类型:

    1. 字面量:包括布尔、数值型、字符串
  • a. 字符串
@Value("字符串内容")
适用于一般的自变量、数组、文件资源和URL资源,Spring Boot会根据配置的变量类型对字符串内容进行转换。
例如:
@Value("true")
private Boolean blog;

  • b. SpEL
@Value("#{SpEL内容}")。
通常SpEL内的字符串使用单引号''括起来。适用于需要计算的自变量、特殊数组、键值对、其他bean属性等。
例如:
    @Value("#{12*15}")
private Integer clickNum;

  • c. 注入配置文件中的数据
@Configuration
@PropertySource("classpath:jdbc.properties")
public class ApplicationConfig
{
    //取资源文件中的内容,对于的值设置到该字段上
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.password}")
    private String password;

   @Bean
  public MyDataSource myDataSource(){
      return new MyDataSource(username,password,driverClassName,url);
}

注意:

如果spring的jar版本过低,必须配置PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer ,完成资源文件属性到对象参数的绑定,从spring环境对象中拿到资源文件配置,完成对 {key} 表达式的替换 ,加static保证该类在其他所有bean之前初始化,因为该类要在其他类初始化之前完成{key} 表达式的替换

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
}

PropertySourcesPlaceholderConfigurer 类最终实现了一个 BeanFactoryPostProcessor 接口 ,他的作用是在BeanFactory创建每一个bean的过程当中加入一些自己的业务逻辑

而实现了BeanFactoryPostProcessor 接口的bean 会在其他所有bean 初始化之前去做一些事情 , 而 PropertySourcesPlaceholderConfigurer 就是在其他所有的bean初始化之前去寻找是否有 value 标签, 如果有完成属性的替换, 所以我们得需要保证 PropertySourcesPlaceholderConfigurer 在其他所有类之前被创建,所以需要在 该bean的定义方法是 static

    1. 数组(List)

字符串:与字面量类似,字符串之间使用英文的“,”隔开。
如:

@Value("Spring Boot, IDEA")
private List key;

SpEL:比如我们需要字符串中有逗号的时候,使用SpEL就会更合适一些。
如:

@Value("#{'Spring Boot;IDEA'.split(';')}")
private List key;
    1. 键值对(Map)

对于键值对我们不能直接使用字符串,可以使用SpEL,也可以配合配置文件来进行使用。

a. SpEL,注意第二层的括号一定要有:

@Value("#{{color:'red', language:'Chinese'}}")
private Map otherProperties;

b. 如果结合配置文件使用,我们这里使用.properties格式和.yml格式的配置的方式有些不同,先来看.properties配置文件代码:

    valuesMap={color:'red', language:'Chinese'}
    代码中获取:
    @Value("#{${valuesMap}}")
    private Map otherProperties;

如果配置文件使用yml,需要注意。
yml在进行单行键值对赋值的时候使用{},所以需要加上双引号"",因为单引号会将字符串内容转义,所以我们这里使用双引号。.yml配置文件代码:

valuesMap: "{color:'red', language:'Chinese'}"
代码中获取:
@Value("#{${valuesMap}}")
private Map otherProperties;
    1. 注入其它bean属性

otherBean是一个Bean对象,name是它的一个属性,可以通过@Value(“#{otherBean.name}”)将这个bean的属性注入@Value声明的属性里

@Value("#{otherBean.name}")
private String someName;
    1. URL资源

与字面量中的字符串类型变量的注入差不多,支持多种形式,我们只示例字符串的形式:
如:

@Value("http://www.baidu.com")
private Resource address;
    1. 操作系统属性

操作系统的属性是静态全局变量systemEnvironment存入,可通过它获取到操作系统的属性。

@Value("#{systemProperties['os.name']}")
private String name;
    1. 文件资源

与字面量中的字符串类型变量的注入差不多,支持多种形式,我们只示例一种。注意这里我们一定要在文件路径前面加上classpath,并且将路径从文件夹处复制过来的时候,最好更改格式,把里面的“\”和“\”改为“/”。当然你也可以不改,Spring Boot也能够识别:
如:

 @Value("classpath:D:/code/application/a.txt")
 private Resource intro;

开发环境的切换

为何需要学习@Profile注解?

由于我们平时在开发中,通常会出现在开发的时候使用一个开发数据库,测试的时候使用一个测试的数据库,而实际部署的时候需要一个数据库。以前的做法是将这些信息写在一个配置文件中,当我把代码部署到测试的环境中,将配置文件改成测试环境;当测试完成,项目需要部署到现在了,又要将配置信息改成现在的。非常麻烦。

而使用了Profile之后,我们就可以分别定义3个配置文件,一个用于开发、一个用户测试、一个用户生产,其分别对应于3个Profile。当在实际运行的时候,只需给定一个参数来激活对应的Profile即可,那么容器就会只加载激活后的配置文件,这样就可以大大省去我们修改配置信息而带来的烦恼。

@Profile/@ActiveProfile注解

spring3.1开始引入,一般用于多环境配置
@Profile注解用于实现通过修改运行时参数,切换不同的开发环境

@Profile注解可以加在类上,也可以加载注入bean的方法上

@Profile只有一个默认的java.lang.String[]类型的属性value,因此它可以指定一个或多个配置名。

@Profile注解,匹配的名字才会去处理,没有匹配的则会被抛弃。

@Profile标签能够定义不同的运行环境,在测试的时候,可以通过

@ActiveProfile来指定运行环境

如何使用两个注解完成环境的切换

1.png

开发环境配置:

@Configuration
@PropertySource("classpath:dev.properties")
@Profile("dev")
public class ApplicationDevConfig
{    
    @Bean
public MyDataSource myDataSource(Environment environment){
    return new MyDataSource(environment.getProperty("jdbc.username")  ,environment.getProperty("jdbc.password")          ,environment.getProperty("jdbc.driverClassName")      ,environment.getProperty("jdbc.url"));

    }
}

测试环境配置:


@Configuration
@PropertySource("classpath:test.properties")
@Profile("test")
public class ApplicationTestConfig
{
    @Bean
 public MyDataSource myDataSource(Environment environment){
        return new MyDataSource(environment.getProperty("jdbc.username")            ,environment.getProperty("jdbc.password")
              ,environment.getProperty("jdbc.driverClassName")
                ,environment.getProperty("jdbc.url"));
 }
}

主配置:

@Configuration
@Import({ApplicationDevConfig.class,ApplicationTestConfig.class})
public class ApplicationConfig
{
}

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
@ActiveProfiles("test")
public class JavaConfigTest {
    @Autowired
    private MyDataSource myDataSource;
    @Test
    public void someBeanTest(){   
    System.out.println(myDataSource.toString());
    }
}

HelloWorld案例

第一个helloWorld案例:

pom.xml:


    org.springframework.boot
    spring-boot-starter-parent
    2.0.5.RELEASE
    


 
  
     org.springframework.boot
     spring-boot-starter-web
  
        

HelloWordController:

@Controller
@SpringBootApplication 
public class HelloWordController {
    
    @RequestMapping("/helloboot")
    @ResponseBody
    public String hello() {
        System.out.println("hello springBoot");
        return "hello springBoot";
    }

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

运行原理分析:

1,使用 继承spring-boot-starter-parent,引入基本的依赖管理配置;

2,引入spring-boot-starter-web,自动引入了springweb相关的包;


@SpringBootApplication 标签做了哪些事情 ?

@SpringBootApplication由三个主要的标签构成:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan

1)@SpringBootConfiguration:本质就是一个@Configuration,代表这是spring容器的主配置类;

2)@EnableAutoConfiguration:开启自动配置,Springboot使用这个标签自动的把内置的符合条件的@Configuration类加载进入应用;

可以查看spring-boot-autoconfigure包中的META-INF/spring.factories文件中的配置项(原理,由@EnableAutoConfiguration标签引入的AutoConfigurationImportSelector类中,使用Spring的SpringFactoriesLoader类实现加载)

3)@ComponentScan:自动扫描;

总结一句话:

@SpringBootApplication:这个注解告诉springboot自动的去完成相关配置,包括基础类的加载,bean的扫描等等,这个后面详细介绍;简单理解为这个标签为我们的应用配置完成了很多基本功能;


SpringApplication.run 又做了哪些事情?

读取配置对象,配置相关自动配置对象,启动tomcat加载应用

总结一句话:

SpringApplication.run:这个是springboot为我们提供的最大的区别,在于springboot不再是一个web应用,需要我们自己去打包,部署,启动tomcat,springboot默认把tomcat打包到应用中,我们可以以正常的运行jar的方式来运行springboot应用;

springBoot项目独立运行方式:

注意:默认的Maven打包方式是不能正常的打包SpringBoot项目的,需要额外的引入打包插件,才能正常的对SpringBoot项目 打成jar包,以后只要拿到该jar包就能脱离IDE工具独立运行了
    1. 使用插件 springboot:run

添加插件

 
    
        
            org.springframework.boot
            spring-boot-maven-plugin
        
    
 
 
对于eclipse 项目要打包,引入插件之后  直接用 package 命令、
对于idea直接点击打包插件即可
    1. 打包独立运行

执行java -jar xxx.jar 运行(在字节码编译目录中会有打包好的 jar)

    1. 使用main方法启动

方式一:直接调用run方法

    SpringApplication.run(StartModeController.class,args);

方式二:可以通过创建对象的方式来运行SpringApplication

    SpringApplication application = new SpringApplication(StartModeController.class); 
    application.run(args);

方式三:通过builder完成:

    SpringApplication application = new SpringApplicationBuilder(StartModeController.class).build();
    application.run(args);

Springboot的优缺点:

  1. 创建独立的Spring应用程序

  2. 嵌入的Tomcat,无需部署WAR文件

  3. 简化Maven配置

  4. 自动配置Spring

  5. 提供生产就绪型功能,如日志,健康检查和外部配置

  6. XML没有要求配置

  7. 非常容易和第三方框架集成起来;

缺点:

1,版本更新较快,可能出现较大变化;

2,因为约定大于配置,所以经常会出现一些很难解决的问题;

Springboot基本使用

应用构建

使用官网进行构建springboot项目

构建项目的地址:http://start.spring.io/

使用开发工具构建springboot项目

新建一个项目 ,选则 spring  Initializr 选项。
填写相关的信息。选则相关的jar依赖。

热部署

直接添加


    org.springframework.boot
    spring-boot-devtools
    true


即可

可以在application.properties文件中设置一些属性

#默认排除的资源 spring.devtools.restart.exclude=static/**,templates/**,public/** #增加额外的排除资源
spring.devtools.restart.additional-exclude=public/** #处理默认配置排除之外的 spring.devtools.restart.enabled=false #禁用自动重启

热部署的原理

SpringBoot重启是restart重启,通过监控classpath的变化,如果classpath中的文件发生变化,即出发重启。springboot通过两个classpath来完成reload,一个basic classloader中加载不变的类,一个restart classloader中加载classpath中的类,重启的时候,restart classloader中的类丢弃并重新加载,所以

设置自动编译:如果您觉得 SpringBoot的热部署不怎么好用,那你也可以去掉热部署的依赖,使用自动编译:

1,开启自动编译 :setting -> Build... -> compiler -> Build project automatically

2,ctrl + alt + shift +/ -> registry -> compiler.automake.allow.when.app.running

设置banner

1,可以直接在classpath下添加一个banner.txt文件即可;

2,介绍springboot的配置文件。springboot默认在classpath中通过application.properties文件来进行配置;比如:
spring.main.banner-mode=off
就可以关闭banner输出

SpringApplication的参数

可以通过注入一个ApplicationArguments对象来完成参数的封装;

@Autowired
private ApplicationArguments args;
    @RequestMapping("/args")
    public String args() {
        return args.getNonOptionArgs().toString();
}

Springboot开发WEB应用

集成日志框架

为什么要用日志?

1,比起System.out.println,日志框架可以把日志的输出和代码分离;
2,日志框架可以方便的定义日志的输出环境,控制台,文件,数据库;
3,日志框架可以方便的定义日志的输出格式和输出级别;

java常用日志框架类别介绍

    1. Log4j

Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。

    1. Log4j 2

Apache Log4j 2是apache开发的一款Log4j的升级产品。

    1. Commons Logging

Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。

    1. Slf4j

类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。

    1. Logback

一套日志组件的实现(slf4j阵营)。

Java常用日志框架历史

1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。

期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4j到java的标准库中,但Sun拒绝了。

2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。

接着,Apache推出了Jakarta Commons Logging,JCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是log4j,也可以是Java Util Logging。

后来(2006年),Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于Commons Logging)和Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。

Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2。Log4j 2具有logback的所有特性。Log4j2与Log4j1发生了很大的变化,log4j2不兼容log4j1。

Springboot的默认日志使用

1,Springboot默认已经开启日志;默认的日志格式为:时间 日志级别 PID 线程名称 日志类 日志说明

2,Springboot的日志区别系统日志和应用日志;

3,Springboot推荐使用Logback作为日志框架

Logback使用方法(推荐使用logback自己的配置文件)

1,springboot默认支持logback.xml或者logback-spring.xml,推荐使用logback-spring.xml,springboot会增加额外功能;

2,可以通过logging.config=classpath:mylogback.xml指定自己的logback配置文件(不推荐);

3,一个典型的logback配置文件:

Logback使用介绍:

使用日志框架的步骤:

a. 在resources文件夹下面创建一个xml文件,文件的名称使用 logback-spring.xml。

b. 在logback-spring.xml文件中编写日志文件的内容:

 

    
    
        
            %d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n
        
    
    
 
    
        
        
    
  

c.启动项目即可。

参考日志格式:

%d{yyyy-MM-dd-HH:mm:ss} %level [%thread]-%class:%line >> %msg %n

格式中的标识符组成:

%logger{n}: 输出Logger对象类名,n代表长度
%class{n}: 输出所在类名, 
%d{pattern}或者date{pattern}: 输出日志日期,格式同java  %L/line: 日志所在行号
%m/msg: 日志内容 
%method: 所在方法名称 
%p/level: 日志级别  
%thread: 所在线程名称

关于logback-spring.xml配置文件的详细解析:

1,configuration:Logback配置根元素

属性包括:

a,scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。

b,scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。

c,debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。

子元素:

:上下文名字;
:定义属性,可以使用${}在配置文件中使用;

2,appender:在logback中是用于具体记录日志的组件,可以用来配置日志记录方式,日志记录格式等;

属性包括:

name:appender的名字,用于下面在配置日志的时候指定;
class:使用到的appender类;

常见的appender:

1,ch.qos.logback.core.ConsoleAppender:输出到控制台;




    
        %d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n
      

2,ch.qos.logback.core.FileAppender:输出到文件;

file:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。 
append:文件结尾,如果是 false,清空现存文件,默认是true。 
encoder:对日志进行格式化 



    
        %d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n
    
    false
    mylog.log

3,ch.qos.logback.core.rolling.RollingFileAppender:输出到文件,可以配置滚动策略,当日志达到某个条件之后分文件记录;

append:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
rollingPolicy:滚动策略,涉及文件移动和重命名。

常用滚动策略:

ch.qos.logback.core.rolling.TimeBasedRollingPolicy:按照时间控制来控制记录文件;

fileNamePattern:文件名称格式,以%d{pattern};
maxHistory: 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。(以文件最小时间为准)



    
        %d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n
    
    
        mylog.%d{yyyy-MM-dd}.log
        30
    

ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy:按照时间和大小控制记录文件;

fileNamePattern:文件名称格式,可以使用%i来控制索引编号;
maxFileSize:这是活动文件的大小,默认值是10MB
maxHistory:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。(以文件最小时间为准)



    
        %d{yyyy-MM-dd-HH:mm:ss.SSS} %level [%thread]-%class:%line>>%msg%n
    
    
        mylog.%d{yyyy-MM-dd}.%i.log
        1kb
        30
    

元素:用来设置某一个包或具体的某一个类的日志打印级别及对应的appender;

属性:

name: 用来指定受此loger约束的某一个包或者具体的某一个类;

子节点:

appender-ref:可以配置多个,标识这个appender将会添加到这个logger

如:



    

元素:特殊的logger,代表根配置,如果没有单独指定日志包层级,都默认使用root定义的日志输出级别;

属性:

level:指定日志级别;

如:



    

在代码中可以使用自定义的Logger对象,输出日志信息。

实现的方式一:通过LoggerFactory工厂对象创建Logger对象

// 方式一:自己定义一个Logger对象
    private static final Logger LOGGER = LoggerFactory.getLogger(LogController.class);

    @RequestMapping("/log")
    @ResponseBody
    public String log() {
        // 方式一的出生日志的方式
        LOGGER.info("方式一:我是info");
        LOGGER.debug("方式一:我是debug");
        LOGGER.error("方式一:我是error");
        return "log";
    }


实现的方式二:使用注解的方式

@Slf4j // 方式二:使用注解来输出日志,前提是需要依赖lombok包
public class LogController {

    @RequestMapping("/log")
    @ResponseBody
    public String log() {
        // 方式二的输出方式
        log.trace("方式二: trace信息");
        log.debug("方式二: debug信息");
        log.info("方式二:  info{}信息", 33333);
        log.error("方式二: error信息");
        return "log";
    }

SpringBoot项目的目录结构

我们通过idea创建出来的SpringBoot项目,会生成一个标准的项目的目录结构。

2.png

application.properties优先级

一个项目中可以有多个application.properties文件存放在不同目录中,此时他们会遵循固定的优先级来处理有冲突的属性配置

  1. 项目/config/application.properties

  2. 项目/application.properties

  3. classpath:config/application.properties

  4. classpath:application.properties

一般都在classpath:application.properties做配置,其他方式不使用

静态资源

  1. 默认情况下,Springboot会从classpath下的 /static , /public , /resources , /META-INF/resources下加载静态资源

  2. 可以在application.properties中配置spring.resources.staticLocations属性来修改静态资源加载地址

spring.resources.static-locations=classpath:/hello
注意设置完以后,会覆盖之前的设置。

  1. 因为应用是打成jar包,所以之前的src/main/webapp就作废了,如果有文件上传,那么就的必须去配置图片所在的路径
spring.resources.static-locations=classpath:/static,file:///Users/tony/Desktop/image/
    

集成Freemarker

为何要集成Freemarker

1.性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。

2、宏定义比jsp tag方便

3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便

4、支持jsp标签

5、可以实现严格的mvc分离

6、易学易用

我是看了一天文档就用得挺熟练了,freemarker文档写得太好了,例子丰富,照做一遍全都会了。

7、功能强大
比Velocity强大多了,还支持JSP Tag。不过最有意义的是macro功能,可以自定义常用的macro,实现页面常规操作的可复用性。

8、报错信息友好
很多应用服务器的JSP报错信息是无法定位到源代码行的。不过freemarker报错定位很准确,丝毫不差,而且信息丰富,一看就知道怎么回事(虽然那个黄黄的页面看起来让人难受)

与JSP相比,FreeMarker的一个优点在于不能轻易突破模板语言开始编写Java代码,因此降低了领域逻辑漏进视图层的危险几率。但缺点是需要一点附加配置来将其平稳地集成到应用程序中,一些IDE(集成开发环境)可能并不完全支持它,当然还有开发者或设计者也许需要学习一门陌生的模板语言。相关的JAR文件将要添加到WEB-INF/lib(在需要的时候,它们包含在Spring中) 。

Freemarker的一些常见语法回顾

获取当前日期,必须要格式化显示


获取Controller控制器中共享给页面的对象中的普通属性

 

获取Controller控制器中共享给页面的对象中的日期类型属性,必须要格式化显示


获取Controller控制器中共享给页面的对象中的boolean类型属性,必须要处理显示

 

对null 或者不存在的对象进行取值,可以设置默认值

例:${var!'我是默认值'}
即,有值时显示正常值,无值时显示默认值 

如果需要判断,使用if标签判断

<#--判断是否小于25-->
<#if user.age < 25 >
    young
<#else>
    old


<#--判断是否大于25--> <#if user.age > 25 > old <#else> young

如果需要判断,可以使用switch标签判断

    <#switch user.age>
      <#case 30>
         very old
         <#break >
      <#case 25>
         old
         <#break >
      <#default >
         young
        <#break >
    

获取Controller控制器中共享给页面的list集合,必须要遍历进行显示

<#list userList as person>
    
     ${person.name}
     ${(person.age>25) ?string("old","young")}
     ${(person.birthday?string('yyyy-MM-dd hh:mm:ss'))!}
  ${person.active?string("是","否")}
   
  

userList 这个名字必须和controller控制器中的共享的key保持一致。

获取Controller控制器中共享给页面的map集合,必须要遍历进行显示

<#list map?keys as key>
    ${key}:${map[key]}

map 这个名字必须和controller控制器中的共享的key保持一致。

自定义变量

<#assign num = 100 />  

自定义的变量参与计算

 ${num * 10} 

处理list集合

first: 取List值第一个值
${userList?first} last: 取List值最后一个值
${userList?last} size: 集合大小
${userList?size} sort: 对集合进行排序
<#list userList?sort as item> ${item.name}

如何集成Freemarker

    1. 导入相关的依赖:

  
       org.springframework.boot
       spring-boot-starter-freemarker
  
    1. 在resources/templates目录下面创建对应的freemarker模板。

freemarker.ftl:




    
    



    <#list userList as person>
     
姓名 年龄 备注 生日 是否激活
${person.name} ${person.age} ${(person.age>25) ?string("old","young")} ${(person.birthday?string('yyyy-MM-dd hh:mm:ss'))!} ${person.active?string("是","否")}
    1. 编写相应的Controller

FreemarkerController:

@Controller
@RequestMapping("/freemarker")
public class FreemarkerController {

    @RequestMapping("/hello")
    public String hello(ModelMap map) {
        User u = new User();
        u.setName("haozz");
        u.setAge(24);
        u.setPassword("qwerty");
        u.setBirthday(new Date());
        u.setBirthday(null);
        u.setActive(false);
        
        User u1 = new User();
        u1.setName("nico robin");
        u1.setAge(35);
        u1.setPassword("qwerty");
        u1.setBirthday(new Date());
        u1.setActive(true);
        
        List userList = new ArrayList<>();
        userList.add(u);
        userList.add(u1);
        map.addAttribute("userList",userList);
    return "freemarker";  
  }   

集成原理

SpringBoot的自动配置中含有FreeMarkerAutoConfiguration配置对象,该配置对象又导入了 FreeMarkerReactiveWebConfiguration配置对象,在里面创建了FreeMarkerConfigurer和FreeMarkerViewResolve 两个对象交给Spring管理,并且设置了默认的属性值,这些属性值来源于FreeMarkerProperties对象

常见的属性配置

spring.freemarker.enabled=true: 是否开启freemarker支持

spring.freemarker.allow-request-override: 是否允许request中的属性覆盖model中同名属性,默认false 

spring.freemarker.allow-session-override: 是否允许session中的属性覆盖model中同名属性,默认false 

spring.freemarker.cache: 是否支持模板缓存,默认false

spring.freemarker.charset=UTF-8: 模板编码

spring.freemarker.content-type=text/html: 模板contenttype 

spring.freemarker.expose-request-attributes: 是否开启request属性暴露,默认false

spring.freemarker.expose-session-attributes: 是否开启session属性暴露,默认false 

spring.freemarker.expose-spring-macro-helpers: 是否开启spring的freemarker宏支持,默认为false 

spring.freemarker.prefer-file-system-access: 默认为true,支持实时检查模板修改 

spring.freemarker.prefix: 加载模板时候的前缀

spring.freemarker.settings.*: 直接配置freemarker参数

spring.freemarker.suffix: 模板文件后缀 

spring.freemarker.template-loader-path=classpath:/templates/: 模板加载地址

集成Thymeleaf

为何要集成Thymeleaf

Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。

Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。Thymeleaf的可扩展性也非常棒。你可以使用它定义自己的模板属性集合,这样就可以计算自定义表达式并使用自定义逻辑。这意味着Thymeleaf还可以作为模板引擎框架。

thymeleaf优点:静态html嵌入标签属性,浏览器可以直接打开模板文件,便于前后端联调。

Thymeleaf模板引擎是springboot中默认配置,与freemarker相似,可以完全取代jsp,在springboot中,它的默认路径是src/main/resources/templates 静态文件css, js 等文件默认路径是src/main/resources/static,所有项目中如果没有这个目录需要手动加上了

Thymeleaf的一些常见语法回顾

获取Controller控制器中共享给页面的对象中的普通属性


获取Controller控制器中共享给页面的对象中的日期类型属性


获取Controller控制器中共享给页面的对象中的boolean类型属性


获取Controller控制器中共享给页面的list集合,必须要遍历进行显示


    
    
    
    

如果需要判断,使用if标签判断

young

如果需要判断,可以使用switch标签判断

最帅的

妮可罗宾

娜美

处理表单(action跳转地址,method设置请求方式,object抽取相同的部分)

处理html内容被解析,转译


th:utext 会进行转译
th:text 不会进行转译

常见的th标签汇总

th标签汇总图.png

如何集成Thymeleaf

    1. 导入相关的依赖。


    org.springframework.boot
    spring-boot-starter-thymeleaf

    1. 在resources/templates目录下面创建对应的thymeleaf模板。



    
    Title


姓名 年龄 备注 生日 婚否
    1. 编写相应的Controller
@Controller
@RequestMapping("/freemarker")
public class FreemarkerController {

    @RequestMapping("/hello")
    public String hello(ModelMap map) {
        User u = new User();
        u.setName("haozz");
        u.setAge(24);
        u.setPassword("qwerty");
        u.setBirthday(new Date());
        u.setBirthday(null);
        u.setActive(false);
        
        User u1 = new User();
        u1.setName("nico robin");
        u1.setAge(35);
        u1.setPassword("qwerty");
        u1.setBirthday(new Date());
        u1.setActive(true);
        
        List userList = new ArrayList<>();
        userList.add(u);
        userList.add(u1);
        map.addAttribute("userList",userList);
        return "freemarker"; 
  }

集成原理

SpringBoot的自动配置中含有ThymeleafAutoConfiguration配置对象,该配置对象的里面创建了视图解析器ThymeleafViewResolver和模板引擎 SpringTemplateEngine 和模板解析器SpringResourceTemplateResolver 三个对象交给Spring管理,并且设置了默认的属性值,这些属性值来源于ThymeleafProperties对象

常见属性设置

spring.thymeleaf.prefix=classpath:/templates/  设置thymeleaf路径默认为src/main/resources/templates
spring.thymeleaf.suffix=.html  设置thymeleaf模板后缀
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false  是否开启缓存默认为true
spring.thymeleaf.mode=LEGACYHTML5  设置thymeleaf严格校验
spring.thymeleaf.encoding=UTF-8    设置编码

了解@ConfigurtionProperties注解的用法:

属性名匹配规则

@ConfigurationProperties的属性名匹配规则(Relaxed binding):Spring Boot可以对下面四种类型的命名进行自动匹配,也就是说配置文件和类文件中的名称不需要完全相同,使用下面的任意两种,系统就会将二者匹配上

标准方式:变量之间通过下一个单词的首字母大写的方式隔开,
eg:student.isFemale
使用“-”进行单词之间的连接:
eg:student.is-female
使用“_”进行单词之间的连接:
eg:student.is_female
全部大写,使用“_”连接:通常对系统属性变量推荐这种写法。
eg:STUDENT_IS_FEMALE

属性ignoreInvalidFields

标记当有非法字段绑定到这个对象时可以被忽略。"非法"指根据使用的binder非法,通常这意味着字段类型错误(或者无法强制转为正确的类型).默认false。这意味着不能忽略字段非法,所以只能报错

属性ignoreUnknownFields

标记当有未知字段绑定到这个对象时可以被忽略。未知字段可能是属性中的一个符号错误。默认true。

属性prefix/value

要绑定到这个对象的有效的属性的名字前缀。value 和 prefix 是同义词

案例如下:


@ConfigurationProperties(prefix = "spring.datasource")
@Setter@Getter@ToString
@Repository
public class MyDataSource {
    private String username;
    private String url;
    private String password;
    private String driverClassName;
}

application.properties:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///crm
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource


@Controller
@SpringBootApplication
public class DataSourcesController {

    @Autowired
    private MyDataSource myDataSource;

    @RequestMapping("/source")
    @ResponseBody
    public String hello() {

        return myDataSource.toString();
    }
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    } 
}

集成Druid连接池

为何要集成Druid

1、性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 。hikariCP的高性能得益于最大限度的避免锁竞争。

2、druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。

3、综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池,比较方便对jdbc接口进行监控跟踪等。

4、可开启prepareStatement缓存,对性能会有大概20%的提升。

psCache是connection私有的,所以不存在线程竞争的问题,开启pscache不会存在竞争的性能损耗。
psCache的key为prepare执行的sql和catalog等,value对应的为prepareStatement对象。开启缓存主要是减少了解析sql的开销。

5、3p0历史悠久,代码及其复杂,不利于维护。并且存在deadlock的潜在风险。

6、Druid可以打印SQL,慢查询方面的日志

回顾Druid连接池在SSM框架中的用法

在pom.xml文件中导入依赖:


    
      mysql
      mysql-connector-java
      5.1.38
      runtime
    

    
    
      com.alibaba
      druid
      1.0.26
    
    
 
      org.springframework
      spring-jdbc
      ${spring.version}
    
    

spring的主配置文件:




    
    
    
    
    
    

    
    
        
        
        
        
    


如何集成Druid

方式一

pom.xml:

 
   
   
      mysql
      mysql-connector-java
   
   
   
      com.alibaba
      druid
      1.1.16
   
   
 
     org.springframework.boot
     spring-boot-starter-jdbc
        
        

application.properties:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///crm
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

方式二

pom.xml:


   
   
      mysql
      mysql-connector-java
   
   
   
      com.alibaba
      druid-spring-boot-starter
      1.1.10
   
   
 
     org.springframework.boot
     spring-boot-starter-jdbc
  
 

application.properties:

spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql:///crm
spring.datasource.druid.username=root
spring.datasource.druid.password=root123

测试案例:

@RestController
public class DataSourceController {

    @Autowired
    private DataSource dataSource;

    @RequestMapping("/dataSource")
    public String showData() throws SQLException {
        return dataSource.getConnection().toString();
    }
}

集成原理

SpringBoot的自动配置中含有DataSourceAutoConfiguration配置对象,该配置对象含有个内部类,该内部类也是配置 类,当容器中没有连接池对象时,就会Generic方式来创建连接池对象交给Spring管理,用到是属性值来源于 DataSourceProperties对象

集成Junit框架

为何要集成Junit

我们在开发的过程中,肯定需要不断的对我们自己写的代码进行测试。所以离不开Junit单元测试。

  1. 导入依赖:


    org.springframework.boot
    spring-boot-starter-test


  1. 准备配置类
public class SomBean {

    public SomBean() {
        System.out.println("构造器");
    }
}

@Configuration
public class JunitConfig {

    @Bean
    public SomBean somBean(){
        return  new SomBean();
    }
}

  1. 编写测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JunitConfig.class)
public class App 
{

    @Autowired
    private SomBean somBean;

    @Test
    public void test(){
        System.out.println("somBean = " + somBean);
    }

}

集成MyBatis框架

为何要集成MyBatis

1、简单易学

   mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活

   mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合

   通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的orm字段关系映射

5、提供对象关系映射标签,支持对象关系组建维护

6、提供xml标签,支持编写动态sql(主要优势之一)。

回顾MyBatis在之前SSM框架中集成的方式

pom.xml:


    
      org.mybatis
      mybatis
      ${mybatis.version}
    

    
    
      org.mybatis
      mybatis-spring
      1.3.0
    
    

applicationContext.xml:

 
    
        
        
        
        
        
        
        
        
        
        
            
                
                    
                    
                        
                            helperDialect=mysql
                            reasonable=true
                            pageSizeZero=true
                        
                    
                
            
        
    
    

    
        
    
    

mybatisCfg.xml:





    
        
        
        
        
        
        
     


如何集成MyBatis

pom.xml:

 
 
   org.mybatis.spring.boot
   mybatis-spring-boot-starter
   1.3.0
 

mybatis-config.xml:





    
        
        
        
        
        
        
     


application.properties:

#mybatis
#给包起别名,因为使用MBG生成的mapper,
#sql里面默认是使用的类的全限定名,因此我们可以不配别名,都使用全名
#mybatis.type-aliases-package=cn.wolfcode.springboot.integrase._5_mybatis.domain
#加载mybatis的主配置文件
mybatis.config-location=classpath:mybatis-config.xml

我们也可以去掉MyBatis的主配置文件:把懒加载的配置配置到SpringBoot的配置文件中:

如:

mybatis.configuration.lazy-load-trigger-methods=clone
mybatis.configuration.aggressive-lazy-loading=false
mybatis.configuration.lazy-loading-enabled=true

domain:

@Setter
@Getter
@ToString
public class Department {

    private Long id;
    private String name;
    private String sn;

}

mapper:

public interface DepartmentMapper {


    List selectAll();

}

mapper.xml:




    


主配置类:

注意:通过@MapperScan扫描器来扫描mapper的接口


@SpringBootApplication
@MapperScan("cn.wolfcode.springboot._06_mybatis.mapper")
public class MyBatisConfig {
    
}

App测试类:


@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyBatisConfig.class)
public class App 
{
    @Autowired
    private DepartmentMapper departmentMapper;

    @Test
    public void test(){
        List list = departmentMapper.selectAll();
        list.forEach(System.out::println);
    }
}

常见属性

#懒加载
mybatis.configuration.lazy-loading-enabled=true mybatis.configuration.lazy-load-trigger-methods=clone 


mybatis.mapper-locations=classpath:cn/wolfcode/xxx/mapper/*Mapper.xml 

mybatis.type-aliases-package=cn.wolfcode.xxx.domain #连接池对象不用配置,会自动注入

集成PageHelper

回顾PageHelper在之前SSM框架中集成的方式

pom.xml:

 
    com.github.pagehelper
    pagehelper
    5.1.6
 
 

配置文件中配置pageHelper

方式一:分页插件配置在mybatis的主配置文件中





   
       
       
       
       
       
       
   


   
    
       
           
           
           
       
   



方式二:分页插件配置在spring的配置文件中

 
   
       
       
       
       
       
       
       
       
       
       
           
               
                   
                   
                       
                           helperDialect=mysql
                           reasonable=true
                           pageSizeZero=true
                       
                   
               
           
       
   
    

如何集成PageHelper

pom.xml:


    com.github.pagehelper
    pagehelper-spring-boot-starter
    1.2.10


application.properties:


#pageHelper
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.page-size-zero=true

集成事务管理

回顾事务管理在之前SSM框架中的使用

方式一:在spring的xml配置文件中

    
    
        
    

    
        
            
            
            
            
        
    

    
        
        
    

方式二:使用注解的方式

        
    
        
    
    
    
    

Spring中注解的方式@Transactional标注事务方法。为了将方法定义为支持事务处理,可以在方法上添加@Transactional注解。根据Spring AOP基于代理机制,只能标注公有方法。如果在类上标注@Transactional注解,那么这个类中所有公有方法都会被定义为支持事务。

代码如下:

        //添加事务注解
    //1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
    //如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
    //REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起. 
    //2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
    //3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
    //属性进行设置. 通常情况下去默认值即可. 
    //4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 
    //这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
    //5.使用 timeout 指定强制回滚之前事务可以占用的时间.
    @Transactional(propagation=Propagation.REQUIRES_NEW,
            isolation=Isolation.READ_COMMITTED,
            noRollbackFor={UserAccountException.class},
            rollbackFor = IOException.class,
            readOnly=false,
            timeout=3)
    @Override
    public void purchase(String username, String isbn) {}
        

注意有一种情况事务会失效:

    @Transactional
    @Override
    public void purchase(String username, String isbn) {
        this.update(username, isbn);
    }
 
    @Transactional
    public void update(String username, String isbn) {
         // TO DO
    }

失效的原因是:

因为spring事务是基于aop的代理机制,当方法中调用this本身的方法时候即使在this的方法标明事务注解,但是事务注解会失效。

解决办法:

在配置中添加如下内容




将上述调用的地方改成如下:

@Transactional
@Override
public void purchase(String username, String isbn) {
    ((BookShopServiceImpl)AopContext.currentProxy()).update(username, isbn);
}

如何集成事务管理

方式一: 使用注解的方式

在Service的实现类上面添加@Transactional注解即可。

如果是使用的springboot的较低版本,需要在配置类上面使用@EnableTransactionManagement开启注解,可以理解为相当于


方式二:使用xml文件的方式

  1. 提供一个配置事务的xml文件。

    
    
    
        
    
    
      
        
        
    
    
    
    
        
            
            
            
            
            
            
        
    
    

2.在主配置文件中通过@ImportResources

@ImportResource("classpath:tx.xml")

集成FastJSON

为何要集成FastJSON

fastjson可以只依赖于jdk环境,其他的条件可以不用依赖

fastjson可以进行序列化和反序列换(所谓的序列化就是将对象转换成对应的json字符串,反序列化就是将json字符串进行转换成对应的对象)

fastjson进行json转换的的速度远远大于jackjson

回顾FastJSON在之前SSM框架中的使用

pom.xml:


 
com.alibaba
fastjson
1.2.29


mvc.xml



            
            
                
                    
                        text/html;charset=UTF-8
                        application/json
                        application/xml;charset=UTF-8  
                    
                
                
                    
                    
                        WriteMapNullValue
                        QuoteFieldNames
                        
                          WriteDateUseDateFormat
                    
                
            
        


@jsonFiled(format="yyyy-MM-dd HH:mm:ss")
private Date creadate;

如何集成FastJSON

pom.xml:

  
com.alibaba
fastjson
1.2.29


方式一:

在一个配置类中定义一个bean

 @Bean
public HttpMessageConverters fastjsonHttpMessageConverters(){
//定义一个convert转换消息的对象
FastJsonHttpMessageConverter converter=new FastJsonHttpMessageConverter();

//添加fastjson的配置信息,比如是否要格式化返回的json信息
FastJsonConfig fastJsonConfig=new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//解决fastjson 中文乱码
List fastMediaTypes = new ArrayList();
    fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
       
//将配置信息添加到convert中
converter.setFastJsonConfig(fastJsonConfig);
//将编码配置加入convert中
converter.setSupportedMediaTypes(fastMediaTypes);
HttpMessageConverter httpMessageConverter=converter;
return new HttpMessageConverters(converter);
}

方式二:

让配置类实现 接口 WebMvcConfigurer ,然后覆盖一个方法configureMessageConverters 如:


 @Override
    public void configureMessageConverters(List> converters) {
       // 1、需要先定义一个 convert 转换消息的对象;
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

        //2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据;
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat
        );

        //3、在convert中添加配置信息.
        fastConverter.setFastJsonConfig(fastJsonConfig);

        //4、将convert添加到converters当中.
        converters.add(fastConverter);
    }
    

注解:

// 输出到页面,格式化日期格式
@JSONField(format = "yyyy-MM-dd")
// serialize:是否需要序列化属性.如果设置成false,页面上就不会出现

@JSONField(serialize = false)

集成Jackson

SPringBoot已经集成了Jackson,所以我们不需要导入依赖。可直接使用。

 private Long id;
    private String name;
    // 输出日期到表单,进行格式化 。注意:必须要带上时区,不然时间会减少一天
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    // 接收表单传来到日期,进行格式化。
    @DateTimeFormat(pattern ="yyyy-MM-dd")
    private Date hiredate;

    @JsonIgnore // 忽略该属性在浏览器中进行显示数据
    private String password;

集成SpringData JPA

JPA是什么

SpringData:其实SpringData就是Spring提供了一个操作数据的框架。而SpringData JPA只是SpringData框架下的一个基于JPA标准操作数据的模块。

SpringData JPA:基于JPA的标准数据进行操作。简化操作持久层的代码。只需要编写接口就可以。

如何集成JPA

  1. 导入依赖

        
            org.springframework.boot
            spring-boot-starter-data-jpa
        

  1. application.properties文件中添加配置
#jpa
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

  1. 实体类
@Entity
@Table(name="role")
public class Role {

    @Id    //主键id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略
    @Column(name = "id")//数据库字段名
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "sn")
    private String sn;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sn='" + sn + '\'' +
                '}';
    }
}

  1. 编写Dao接口
@Repository
public interface RoleRepository extends JpaRepository {


    Role findRoleById(Long id);

    Role findRoleByName(String name);


}
  1. 测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class JPATest {

    @Autowired
    private RoleRepository roleRepository;

    @Test
    public void testFindById(){
        Role  role = roleRepository.findRoleById(2L);
        System.out.println(role);
    }

    @Test
    public void testFindByName(){
        Role  role = roleRepository.findRoleByName("人事管理");
        System.out.println(role);
    }
}

SpringBoot统一异常处理

SpringBoot为何需要统一异常处理

在 Java EE 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过 程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。 每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。 那么将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一, 也实现了异常信息的统一处理和维护

统一异常处理在之前SSM框架中的处理方式回顾

Spring MVC 处理异常有 3 种方式:

(1)使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver; 
(2)实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器; 
(3)使用@ExceptionHandler 注解实现异常处理;

回顾方式一:

SecurityException自定义异常类:

public class SecurityException extends Exception {
    
    public SecurityException() {    
        super();    
    }   
    public SecurityException(String message){
        super(message);
    }
 }

springmvc.xml中的配置全局异常处理器:

 




 
    
         cn.wolfcode.web.exception.SecurityException=commons/nopermission 

        
    


回顾方式二:

CustomException自定义异常类:

/**
 *
 * 系统自定义异常类
 */
public class CustomException extends Exception {
    //异常信息
    public String message;
    public CustomException(String message){
        super(message);
        this.message = message;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

全局异常处理器:

/**
 * 
 * 全局异常处理器
 */
public class CustomExceptionResolver implements HandlerExceptionResolver{
    /**
     * 系统抛出的异常
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e 系统抛出的异常
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
       // 解析出异常类型
        CustomException customException = null;
        // 若该异常类型是系统自定义的异常,直接取出异常信息在错误页面展示即可。
        if(e instanceof CustomException){
            customException = (CustomException)e;
        }else{
            // 如果不是系统自定义异常,构造一个系统自定义的异常类型,信息为“未知错误”
            customException = new CustomException("未知错误");
        }
        //错误信息
        String message = customException.getMessage();
        ModelAndView modelAndView = new ModelAndView();
        //将错误信息传到页面
        modelAndView.addObject("message",message);
        //指向错误页面
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

错误页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    失败!


${message}



springmvc.xml中的配置全局异常处理器:

    
    

回顾方式三:

@ControllerAdvice
通过使用@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型。
�ErrorControllerAdvice:

@ControllerAdvice
public class ErrorControllerAdvice {

    @ExceptionHandler(Exception.class)
    public String error(Model model, Exception e){
         model.addAttribute("error",e.getMessage());
         return "error";
    }
}

error.ftl:




    
    Title


   ${error}


在SpringBoot中如何统一异常处理

方式一:

通过使用@ControllerAdvice定义统一的异常处理类,而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型。

ErrControllerAdvice:

@ControllerAdvice
public class ErrControllerAdvice {

    @ExceptionHandler
    public String error(Exception e , Model model){
        model.addAttribute("error",e.getMessage());
        return "error";
    }
}

注意:如果项目中报错,会被上述异常进行拦截。上述方法,它会拦截到异常,并把异常信息存入model中,然后跳转error的页面,这个页面需要自己编写,在templates模版文件夹中。

error.ftl:




    
    


  我是通过注解ControllerAdvice进来的错误页面
  error : ${error}



利用统一异常的处理类,我们可以自定义自己的异常,比如,自定义异常,返回json格式的数据。

第一步:自定义异常类

public class MyCustomException extends  RuntimeException {

    public MyCustomException(String message) {
        super(message);
    }

    public MyCustomException(Throwable cause) {
        super(cause);
    }
}

第二步:在异常的处理类中添加自定义异常的拦截方法

@ExceptionHandler
    @ResponseBody
    public JSONResult customException(MyCustomException e) {
        JSONResult jsonResult = new JSONResult();
        jsonResult.setMark(e.getMessage());
        return jsonResult;
    }


第三步:在service层添加异常的处理(try...catch(自定义异常))

@Service
public class ExceptionServiceImpl {

    public void exception() {
        try {
            int i = 1 / 0;
        }catch (Exception e){
            throw new MyCustomException(e.getMessage());
        }
    }
}

第四步: 编写对应的controller,在方法中,调用service中的方法。

 @RequestMapping("/ex")
    @ResponseBody
    public JSONResult list2(){

        ExceptionServiceImpl service = new ExceptionServiceImpl();
        service.exception();

        return new JSONResult();

    }

方式二:

1,SpringBoot默认情况下,把所有错误都重新定位到/error这个处理路径上,由BasicErrorController类完成处理;

2,SpringBoot提供了默认的替换错误页面的路径:

1,静态错误页面默认结构:
src/
    resources/
    public/
        error/
            404.html
            401.html
            5xx.html�
2,也可以使用模板页面:
src/
    resources/
    templates/
        error/
            5xx.ftl

该路径方式是通过ErrorMvcAutoConfiguration中的DefaultErrorViewResolver完成的;

你可能感兴趣的:(SpringBoot两天)