SpringBoot从入门到精通(2)之依赖管理,自动配置初步介绍以及常用注解的基本使用(细)

13 篇文章 0 订阅
3 篇文章 0 订阅

SpringBoot依赖管理,自动配置以及常用注解的基本使用(细)

问题引入

springboot可以帮我们整合那么多框架,如redis等等,那么各个版本之间肯定难免会有冲突或者无法衔接的地方,那么springboot是实现这么轻松的配置的呢?既然是spring的产品那么它肯定整合了spring,那我想使用spring的容器功能,该怎么正确使用?别急,接着往下看

依赖管理

  • 父项目做依赖管理
    • 我们可以发现在初始项目甚至任意springboot项目中都会有一个父依赖

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.5.3</version>
          <relativePath/> <!-- lookup parent from repository -->
      </parent>
      

      那这个到底是干嘛的呢?我们点进去看一下就知道了,按住ctrl,点击“org.springframework.boot”,这是一个父工程,里面定义了很多规范

      我们截取源码的一段,相信大家一定能猜出来是什么意思:

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-dependencies</artifactId>
          <version>2.5.3</version>
        </parent>
      <properties>
        <java.version>1.8</java.version>
        <resource.delimiter>@</resource.delimiter>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      </properties>
      
      <resources>
        <resource>
          <directory>${basedir}/src/main/resources</directory>
          <filtering>true</filtering>
          <includes>
            <include>**/application*.yml</include>
            <include>**/application*.yaml</include>
            <include>**/application*.properties</include>
          </includes>
        </resource>
        <resource>
          <directory>${basedir}/src/main/resources</directory>
          <excludes>
            <exclude>**/application*.yml</exclude>
            <exclude>**/application*.yaml</exclude>
            <exclude>**/application*.properties</exclude>
          </excludes>
        </resource>
      </resources>
      

      猜出来了吗?

      我们选一些我们看得懂的,很容易看出来,第一个代码段中定义了

      • java版本是1.8
      • 使用了UTF-8的编码格式
      • 继承自spring-boot-dependencies(怎么又一个继承?别急,我们接着往下看)

      第二个代码段相信大家不陌生,没错,就是静态资源的过滤,以前我们些ssm项目的时候是不是写过太多了!这里springboot已经帮我们配置好了!贴心!

  • 无需关注版本号,自动版本仲裁

    上一个小点我们看见,有继承spring-boot-dependencies,那这是个什么东西?同样的 ctrl+点击 走一波

    其它的我们不管,properties标签中那几百行的版本,是不是瞬间就懂了!没错,这就是自动版本仲裁的关键,其实每一个springboot版本都会更新dependencies,保证springboot中各个组件的完美兼容,不发生版本上的冲突,极大程度上的简化了我们的开发!我们就不用担心代码跑不起来是不是版本冲突啥的,springboot都帮我们解决了,它帮我们直接定义了一些我们开发中常常会用到的依赖的版本号!我们引入时只需要声明,不用写版本号,例如:

    我们想引入mysql驱动:

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    

    我们写了版本号吗?没有!简化了我们的开发,那我们怎么知道mysql这个链接驱动是什么版本的?-直接去dependencies中去找可以看见这样一段代码:

    <mysql.version>8.0.26</mysql.version>
    ...
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version>
    

    看见了吧?8.0.26版本,那我们要是不想要这个版本的呢?

    在我们自己这个项目中写上:

    <properties>
        <mysql.version>8.0.22</mysql.version>
    </properties>
    

    达到自定义版本号的作用。

  • 开发导入starter场景启动器

    我们开发一个web应用,就要相应的导入

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    这是为什么?我们要开发web,是不是就需要tomcat?那tomcat从哪来?不可能是springboot中一直存在的吧?我不开发web项目tomcat就没有必要引入,其实tomcat就是spring-boot-starter-web这个内置的web容器,类似的,我们可以根据相应的场景去引入不同的starter,也就是引入不同的启动器,这就是启动器的作用,它帮我们内置了该场景中可能需要的各种依赖和容器,简化我们的开发

自动配置

我们引入web场景启动器是,其实已经自动帮我们配置好了大多数东西

例如:

web容器tomcat;

前端控制器DispatcherServlet:拦截所有的前端的请求;

字符编码characterEncodingFilter:解决返回中文字符串乱码问题;

视图解析器viewResolver:对返回的视图进行渲染呈现;

文件上传解析器multipatResolver:文件上传;

默认包结构

  • 主程序下的包及其下面的所有子包里面的组件都能被默认扫描出来

  • 无需以前的扫描配置

  • 想要改变扫描路径,就要用到注解***ComponentScan***但是主程序入口中***SpringBootApplication***已经帮我们定义好了***ComponentScan***的值,注解无法重复,我们可以将***SpringBootApplication***拆分(这里可以点击***SpringBootApplication***的源码查看,除了几个java原生注解,就是三个:SpringBootConfigurationEnableAutoConfigurationComponentScan

    测试:

    写一个hello2

    请添加图片描述

    package com.lwh;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController2 {
        @RequestMapping("/hello2")
        public String hello(){
            return "hello";
        }
    }
    

    不修改配置,访问localhost:8080/hello2是无法访问的,出现了内置页面404,因为默认的包扫描路径在主程序入口的com.lwh.hello中,我创建hello2在com.lwh中,扫描不到!

    解决办法:

    SpringBootApplication
    =
    SpringBootConfiguration+EnableAutoConfiguration+ComponentScan
    

    所以修改主程序入口上的注解

    package com.lwh.hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.SpringBootConfiguration;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    
    //@SpringBootApplication
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan("com.lwh")//修改包扫描,扫描com.lwh下的所有包和类,组件等
    public class HelloApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HelloApplication.class, args);
        }
    
    }
    

    再次访问,发现hello和hello2都能够被扫描到,修改成功!

容器功能

组件添加常用详解

@Configuration详解

springboot的底层实现就是spring,自然也会有spring的技术,spring有一个特点就是帮你托管一切实体类,所以springboot也有容器,为了让springboot项目更加简洁,我们省去了spring的xml配置,改用纯java代码的配置,这就要用到一个注解:@Configuration

新建一个包,叫config,用来编写配置信息,创建两个实体类,user和phone

User:

package com.lwh.hello.pojo;

public class User {
    private String name;
    private String sex;
    private int age;
    private Phone phone;
	//为了简洁,省去了get,set,有参无参构造器和tostring方法,实操时务必写上
}

Phone:

package com.lwh.hello.pojo;

public class Phone {
    private String name;
	//为了简洁,省去了get,set,有参无参构造器和tostring方法,实操时务必写上
}

Myconfig:

详解:和spring一样,用Configuration来代表这是一个bean的配置文件,@Bean代表下面这个方法交给spring进行托管,方法名就相当于唯一标识托管类的id(和xml配置中的id名是一个意思),返回的时一个User对象。

package com.lwh.hello.config;

import com.lwh.hello.pojo.Phone;
import com.lwh.hello.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {
    @Bean
    public User User(){
        User user = new User("张三", "男", 18);
        user.setPhone(Phone());
        return user;
    }

    @Bean
    public Phone Phone(){
        return new Phone("锤子🔨");
    }
}

去主程序入口测试:

public class HelloApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(HelloApplication.class, args);
        User user = run.getBean("User", User.class);
        System.out.println(user);
        //输出:User{name='张三', sex='男', age=18, phone=Phone{name='锤子🔨'}}
    }

}

新建springboot中主程序中只有一句话:

SpringApplication.run(HelloApplication.class, args),其实这个run方法返回的是一个容器,我们获得这个容器再用getBean方法就可以获取到这个托管的User对象。

注意:这里加了一个新特性,我们可以再次点击源码看Configuration注解的内容,

加了一个proxyBeanMethods(),而且它默认就是ture,这个字面意思就是代理bean方法,那么true和false有什么不同:

  • 它为ture代表时单例模式的实现,每次调用getBean获取到的bean,springboot都会在容器中检查一遍是否有这个对象,有就直接返回,没有再创建

  • 它为false代表每次getBean时都创建一个新的对象,不管之前是否创建过

看下面代码体会一下:

public static void main(String[] args) {
    ConfigurableApplicationContext run = SpringApplication.run(HelloApplication.class, args);
    User user = run.getBean("User", User.class);
    Phone phone=run.getBean("Phone",Phone.class);
    /**
    	当proxyBeanMethods=true时,输出为false
    	当proxyBeanMethods=false,输出为true
    */
    System.out.println(user.getPhone()==phone);
}

那么springboot为什么要分成这两种情况呢?

proxyBeanMethods为true时,更加适用于组件之间的依赖关系,springboot每次新增都会检查,启动会变慢,相反,为false时,组件之间的依赖无法体现,每次新增不用去找容器中是否存在,启动更快!

其它的容器注解有:

  • @Component,作用在类上,代表这个类是一个组件,通知springboot自动装配到Spring容器中
  • @Controller,代表是一个Controller控制器
  • @Service,代表是一个service逻辑
  • @Repository,作用在类上
@improt详解

这个一般写在Config上,只要这个类在项目中,就可以交给springboot进行托管,对象的名就是全类名,(导入的jar包中的对象也可以托管)

@Import(User.class)//托管的名就要做:com.lwh.hello.pojo.user
@Configuration
public class MyConfig {
    ...
}
@ConditionalOnBean注解

应用场景:字面意思,就是条件装配,适用于一些互相依赖的组件,例如本文中一直使用的Phone和User,User中依赖Phone的创建,那么想要User中的Phone属性不发生一些不必要的错误,我们可以想办法当User要创建时我们去找Phone,如果Phone创建了,就添加进去,如果Phone没有创建,那么User就不创建

说了那么多,上代码:

@Configuration
public class MyConfig {
    @ConditionalOnBean(name = "Phone")
    @Bean
    public User user1(){
        User user = new User("张三", "男", 18);
        user.setPhone(Phone());
        return user;
    }

    @Bean
    public Phone Phone(){
        return new Phone("锤子🔨");
    }
}

以上就是,当Phone创建了后,User才能被创建,我们可以去主方法中测试一下

public static void main(String[] args) {
     ConfigurableApplicationContext run = SpringApplication.run(HelloApplication.class, args);

        boolean phone=run.containsBean("Phone");
        boolean user = run.containsBean("user1");
        System.out.println(user+" "+phone);
        Phone phone1 = run.getBean("Phone",Phone.class);
        User user2 = run.getBean("user1", User.class);
        System.out.println(user2);
}

理论上,我们第一个输出是“true false",第二个输出是将user内的属性输出,但是!!!这里直接报了一个线程错误,如下:

“No bean named ‘user1’ available”

本人一开始也很费解,怎么也不知道为什么出错,直到突发奇想,是不是讲究顺序?于是将MyConfig中的两个方法换了个位置,成功输出!!!

true true
User{name='张三', sex='男', age=18, phone=Phone{name='锤子🔨'}}

再将Phone方法的@Bean注释掉,发现User无法被创建,达到了目的,即Phone没有被托管就不能创建User!

天坑注意:Bean的创建是有顺序的!!!!,使用ConditionalOnBean注解时一定要注意

像这样的条件注解有很多,在SpringBoot底层出现的很多,大家像了解可以去官方文档中查阅,这里只列举了一个。

原生配置文件引入之@ImportResource详解

一般作用在Config配置文件上,用来将xml文件中需要注册的bean真正的注册进springboot的容器中,例如项目resource目录下有这样的一个beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
    <bean id="User2" class="com.lwh.hello.pojo.User">
        <property name="name" value="wuhu"/>
        <property name="age" value="12"/>
        <property name="phone">
            <null></null>
        </property>
        <property name="sex" value=""/>
     </bean>
</beans>

直接使用是springboot 并不能将User2创建出来,因为Springboot 并不知道你这个beans.xml到底是干什么的,所以无法识别,我们可以将config目录下的 MyConfig类上加上这样的一句话:

@Configuration
@ImportResource("classpath:beans.xml")
public class MyConfig {
	....
}

这样就可以使用beans.xml配置组件了,但是有一点需要注意,我们springboot中尽量将这样的xml配置简化到最小,所以像这样的bean的配置,我们一般采用java代码的方式,这种情况适用与一些ssm项目导入,或者依赖的jar包中有这样的xml文件需要注册读取的时候。

配置绑定之ConfigurationProperties详解

使用场景:我们在写java程序的时候往往要使用很多配置,我们最开始学的就是properties配置文件的使用,我们写properties文件最终是想要其中的key和value值,想要他们能够赋值给我们的对象,以便使用,但是采用java原生代码时,拿到其中的key和value值比较繁琐,特别是当你的配置较多时,我们拿到指定的数据就更加麻烦,所以,Springboot就将步骤简化,提供了ConfigurationProperties注解

@ConfigurationProperties+@Component组合使用

我在springboot的总配置文件application.properties中想要给我需要的组件赋值

myphone.name="huawei"

然后将在Phone上加上注解

@Component//代表注册组件
@ConfigurationProperties(prefix = "myphone")//代表取到properties文件中的值
public class Phone {
    private String name;
    //省略有参无参构造方法,get,set方法和toString方法
}

写一个Controller验证一下:

@RestController
public class HelloController {
    @Autowired//自动装配
    Phone phone;

    @RequestMapping("/phone")
    public String phone(){
        return phone.toString();
        //成功看见浏览器页面:Phone{name='"huawei"'}
    }
}
@ConfigurationProperties+@EnableConfigurationProperties组合使用

第二部发生变化:我们不加Component注解,不注册组件到容器中,但是我们去配置类中注册:

@Configuration
@EnableConfigurationProperties(Phone.class)
public class MyConfig {
    ...
}

第三步不变,然后再运行,就可以看见同样的效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值