SpringBoot入门篇学习笔记(三、Docker,数据访问,启动流程和自定义starter)

五、Docker

Docker可以看我之前跟周阳老师视频学习时记的笔记:https://blog.csdn.net/qq_36903261/article/details/105870268(打开网站Crtl+F直接查询命令)

Docker官网:https://www.docker.com/
DockerHub官网:https://hub.docker.com/

这里需要先安装4个容器,为后续教学做准备:

  1. 安装mysql
    dockerhub的mysql:https://hub.docker.com/_/mysql

    #拉取最新镜像(可以在mysql后面加上冒号加版本号指定下载(如:mysql:5.6  mysql:8.0.18))
    docker pull mysql
    
    #启动mysql
    #--name some-mysql:为启动的mysql容器起名(some-mysql)
    #-v /my/custom:/etc/mysql/conf.d:设置容器中目录和容器外目录进行共享连接(冒号前为linux路径,冒号后为容器路径,linux下的/my/custom目录和mysql容器的/etc/mysql/conf.d进行共享)
    #-p 3306:3306:容器对外暴露的端口(冒号前为对外暴露的端口号,冒号后为容器要暴露的端口号)
    #-e MYSQL_ROOT_PASSWORD=123456:设置root密码(123456)
    #-d:后台运行
    #mysql:运行mysql(如果不是最新版需要在mysql加上冒号和版本号)
    docker run --name myMysql -v /my/custom:/etc/mysql/conf.d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
    
  2. 安装redis(缓存数据库)
    dockerhub的redis:https://hub.docker.com/_/redis

    #拉取最新镜像
    docker pull redis
    
    #运行redis(redis-server启动路径可以会有偏差,具体参照dockerhub官网上关于redis的具体操作)
    docker run -p 6379:6379 --name myRedis -v /angenin/myredis/data:/data -v /angenin/myredis/conf:/usr/local/redis/conf -d redis:5.0.5 redis-server /usr/local/redis/conf/redis.conf --appendonly yes
    
  3. 安装rabbitmq(消息中间件)
    dockerhub的rabbitmq:https://hub.docker.com/_/rabbitmq

    #拉取镜像(management是有带web的管理界面)
    docker pull rabbitmq:3.8.3-management
    
  4. 安装elasticsearch(全文检索)
    dockerhub的elasticsearch:https://hub.docker.com/_/elasticsearch

    #拉取最新镜像
    docker pull elasticsearch
    

六、SpringBoot与数据访问

在这里插入图片描述

1. JDBC

新建项目
在这里插入图片描述
在这里插入图片描述

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

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

启动linux中docker的mysql容器(我使用的是8.0.18版本)。
docker run -p 3306:3306 --name myMysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.18
使用Navicat Premium连接上mysql(使用其他数据库工具都可以)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
并创建一个名为jdbc的数据库。
在resources目录下新建application.yml文件,配置数据库信息

#url使用linux的ip地址
#如果数据库使用的是5.x版本的driver-class-name为com.mysql.jdbc.Driver
#?serverTimezone=GMT是设置时区,8以上版本不设置可能会报错,5版本的可以不加
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://10.211.55.17:3306/jdbc?serverTimezone=GMT
    driver-class-name: com.mysql.cj.jdbc.Driver

在test目录下的测试类SpringBoot06DataJdbcApplicationTests中测试:

@SpringBootTest
class SpringBoot06DataJdbcApplicationTests {
    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads()throws Exception {
    	//class com.zaxxer.hikari.HikariDataSource
        System.out.println(dataSource.getClass());
        Connection connection = dataSource.getConnection();
        //HikariProxyConnection@1905420854 wrapping com.mysql.cj.jdbc.ConnectionImpl@216e9ca3
        System.out.println(connection);
        connection.close();
    }
}

默认使用com.zaxxer.hikari.HikariDataSource数据源,数据源的相关配置都在DataSourceProperties里面。

自动配置原理

org.springframework.boot.autoconfigure.jdbc:

  1. 参考DataSourceConfiguration,根据配置创建数据源,默认使用hikari连接池,可以使用spring.datasource.type指定自定义的数据源类型。
  2. SpringBoot默认支持:jdbc、hikari、dbcp2
  3. 自定义数据源
    	/**
     * Generic DataSource configuration.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type")
    static class Generic {
    
    	@Bean
    	DataSource dataSource(DataSourceProperties properties) {
    		//使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
    		return properties.initializeDataSourceBuilder().build();
    	}
    
    }
    
使用sql文件自动建表

在resources目录下新建schema-all.sql文件


SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

在application.yml中加入initialization-mode: always(和username同级)始终初始化数据源。
在这里插入图片描述

然后运行SpringBoot项目后,数据库里多了张表
在这里插入图片描述
默认只识别schema.sql或schema-all.sql文件,如果想自定义sql文件名(department.sql),可以使用下面这种方法:
在application.yml文件中加入

	#schema接受集合,所以使用 - 代表一个集合元素,一个 - 代表一个元素
	schema:
		- classpath:department.sql
#		- classpath:xxx.sql
#		- ...

在这里插入图片描述

测试JdbcTemplate

操作数据库:SpringBoot自动配置了JdbcTemplate和增强版NamedParameterJdbcTemplate来操作数据库。

  1. 在数据库表中插入一条数据
  2. 在springboot目录下新建controller/Hellocontroller
@Controller
public class HelloController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @ResponseBody
    @GetMapping("/query")
    public Map<String, Object> map() {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from department");
        return list.get(0);
    }
}

注意:每次启动项目时,SpringBoot都会自动建表(如果有sql文件的话,重新建表会覆盖原来的数据),所以如果配置文件中加了schema: - classpath:department.sql,需要注释掉(如果没加但是sql文件名是schema.sql或schema-all.sql,需要将文件改名或移除)
启动项目,在浏览器中输入http://localhost:8080/query
在这里插入图片描述

2. Druid

pom.xml中导入druid的依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
</dependency>

然后在application.yml中加入type: com.alibaba.druid.pool.DruidDataSource(和username同级别)
在这里插入图片描述
运行测试类,打印的结果为:class com.alibaba.druid.pool.DruidDataSource(切换成功)

在application.yml设置数据源的其他配置:

#url使用linux的ip地址
#如果数据库使用的是5.x版本的driver-class-name为com.mysql.jdbc.Driver
spring:
  datasource:
    #基本配置
    username: root
    password: 123456
    url: jdbc:mysql://10.211.55.17:3306/jdbc?serverTimezone=GMT
    driver-class-name: com.mysql.cj.jdbc.Driver
    initialization-mode: always
    type: com.alibaba.druid.pool.DruidDataSource
#    schema:
#      - classpath:department.sql

    #其他配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙  
    filters: stat,wall,log4j2
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

这里设置的其他配置是黄颜色的,这是因为上面的基本属性和DataSourceProperties类里的属性相对应,而其他配置DataSourceProperties类里没有相对应的属性,所以是不生效的。
在这里插入图片描述
debug测试类发现配置文件中设置的属性没生效
在这里插入图片描述

所以我们要自己写一个配置类进行配置,在springboot目录下新建config/DruidConfig

@Configuration
public class DruidConfig {

    //自己创建一个数据源
    @ConfigurationProperties(prefix = "spring.datasource")  //引入配置
    @Bean   //添加到容器中
    public DataSource druid(){
        return new DruidDataSource();
    }
}

重新debug测试类,设置的属性生效了
在这里插入图片描述

配置druid的监控

在DruidConfig配置类里添加:

    //配置druid的监控
    //1. 配置一个管理后台的servlet   ServletRegistrationBean为注册servlet
    @Bean
    public ServletRegistrationBean statViewServlet(){
        //第一个参数是StatViewServlet为管理后台的servlet,第二个参数为管理的路径
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        Map<String, String> initParams = new HashMap<>();
        //登录后台时用的用户名
        initParams.put("loginUsername", "admin");
        //登录使用的密码
        initParams.put("loginPassword", "123456");
        //允许什么登录(访问)
        initParams.put("allow", "");    //默认允许使用
        //拒绝指定访问
        initParams.put("deny", "192.168.0.107");    //192.168.0.107是我电脑的ip
        //设置初始化参数
        bean.setInitParameters(initParams);
        return bean;


    }

    //2. 配置一个监控的filter  FilterRegistrationBean为注册filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        //WebStatFilter为监控的filter
        FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());

        Map<String, String> initParams = new HashMap<>();
        //设置不拦截的资源
        initParams.put("exclusions", "*.js,*.css,/druid/*");
        //设置初始化参数
        bean.setInitParameters(initParams);
        //设置拦截请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }

如果启动项目,在浏览器输入http://localhost:8080/druid
在这里插入图片描述
输入我们刚才在servlet中设置的用户名和密码,进入后台:
在这里插入图片描述
在这里插入图片描述

3. MyBatis

新建项目
在这里插入图片描述
在这里插入图片描述
mybatis的依赖:

 <dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.1.2</version>
 </dependency>

在这里插入图片描述

准备工作

  1. 引入druid
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.20</version>
    </dependency>
    
  2. 新建一个名为mybatis的数据库
    在这里插入图片描述
  3. 在resources目录下新建application.yml文件
    spring:
      datasource:
        #   数据源基本配置
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.211.55.17:3306/mybatis?serverTimezone=GMT
        type: com.alibaba.druid.pool.DruidDataSource
        #   数据源其他配置
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,wall,log4j2
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
  4. 在springboot目录下新建config/DruidConfig配置类
    package com.angenin.springboot.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DruidConfig {
    
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druid(){
            return new DruidDataSource();
        }
    
        @Bean
        public ServletRegistrationBean statViewServlet(){
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    
            Map<String, String> initParams = new HashMap<>();
            initParams.put("loginUsername", "admin");
            initParams.put("loginPassword", "123456");
            initParams.put("allow", ""); //默认允许使用
            initParams.put("deny", "192.168.0.107");
            bean.setInitParameters(initParams);
            return bean;
        }
    
        @Bean
        public FilterRegistrationBean webStatFilter(){
            FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
    
            Map<String, String> initParams = new HashMap<>();
            initParams.put("exclusions", "*.js,*.css,/druid/*");
            bean.setInitParameters(initParams);
            bean.setUrlPatterns(Arrays.asList("/*"));
            return bean;
        }
    }
    
  5. 启动项目,在浏览器输入http://localhost:8080/druid/,测试登录
    在这里插入图片描述
  6. 停止项目,在resources目录下新建sql目录,并在里面创建department.sql和employee.sql文件
    department.sql
    SET FOREIGN_KEY_CHECKS=0;
    
    DROP TABLE IF EXISTS `department`;
    CREATE TABLE `department` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `departmentName` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    employee.sql
    SET FOREIGN_KEY_CHECKS=0;
    
    DROP TABLE IF EXISTS `employee`;
    CREATE TABLE `employee` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `lastName` varchar(255) DEFAULT NULL,
      `email` varchar(255) DEFAULT NULL,
      `gender` int(2) DEFAULT NULL,
      `d_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
  7. 在application.yml配置文件里添加
    #与connectionProperties同级
    initialization-mode: always
    schema:
      - classpath:sql/department.sql
      - classpath:sql/employee.sql
    
    在这里插入图片描述
  8. 重新启动项目,创建数据表
    在这里插入图片描述
  9. 停止项目,注释掉刚刚在application.yml添加的配置(不注释的话,每次启动项目都会运行指定文件在sql文件,覆盖原来的数据)
    在这里插入图片描述
  10. 创建两个数据表对应的bean,在建bean之前,推荐一下lombok(它可以让我们的bean变得看起来不臃肿,虽然它的功能不仅仅只有如此)
    引入相关的依赖
     <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.10</version>
        <!--只在编译阶段生效-->
        <scope>provided</scope>
    </dependency>
    
    添加IDE工具对Lombok的支持:
    安装Lombok插件:
    在这里插入图片描述
    在这里插入图片描述
  11. 设置完后在springboot目录下新建bean包,在包下新建Employee和Department两个实体类
    Employee
    @Data   //set/get方法
    @AllArgsConstructor //有参构造器
    @NoArgsConstructor  //无参构造器
    public class Employee {
    	private Integer id;
    	private String lastName;
    	private Integer gender;
    	private String email;
    	private Integer dId;
    }
    
    Department
    @Data   //set/get方法
    @AllArgsConstructor //有参构造器
    @NoArgsConstructor  //无参构造器
    public class Department {
        private Integer id;
        private String departmentName;
    }
    
    这里只用了lombok的一个注解,想使用
    的其他注解可以自行百度学习,当然,如果不喜欢lombok也可以创建set和get方法,这里只是安利一个好用的插件。

注解

  1. 在springboot目录下新建mapper.DepartmentMapper接口(这里注解和xml都会使用,这里先用注解来实现,然后再用xml来实现)
    @Mapper //指定这是一个操作数据库的mapper
    public interface DepartmentMapper {
    
        @Select("select * from department where id=#{id}")
        Department getDeptById(Integer id);
    
        @Delete("delete from department where id=#{id}")
        int deleteDeptById(Integer id);
    
        //插入后主键会重新封装进Department中
    	//useGeneratedKeys = true表示使用自动生成的主键,keyProperty = "id"代表department里的id属性是主键
    	@Options(useGeneratedKeys = true, keyProperty = "id")
        @Insert("insert into department(departmentName)values(#{departmentName})")
        int insertDept();
    
        @Update("update department set departmentName=#{departmentName} where id=#{id}")
        int updateDept(Department department);
    }
    
  2. 在springboot目录下新建controller/DeptController类
    @Controller
    public class DeptController {
    
        @Autowired
        DepartmentMapper departmentMapper;
    
        @GetMapping("/dept/{id}")
        public Department getDepartment(@PathVariable("id") Integer id){
     	    //返回查询到的数据
            return departmentMapper.getDeptById(id);
        }
    
        @GetMapping("/dept")
        public Department insertDept(Department department){
            departmentMapper.insertDept(department);
            //返回我们插入的数据
            return department;
        }
    
    }
    
  3. 启动项目,在浏览器输入http://localhost:8080/dept?departmentName=AA插入一条数据,http://localhost:8080/dept/1然后查询。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  4. 问题一:如果插入的时间表里属性名和对应的bean属性名不一致时
    1. 在写sql语句为数据表里的属性起别名。
    2. 这里我把departmentName改为department_name,查询结果:
      在这里插入图片描述
      自定义Mybatis的配置规则,开启驼峰命名法映射规则,在config包里新建MyBatisConfig配置类
      import org.apache.ibatis.session.Configuration;
      
      @org.springframework.context.annotation.Configuration
      public class MyBatisConfig {
      
          @Bean
          public ConfigurationCustomizer configurationCustomizer(){
              //setMapUnderscoreToCamelCase(true)开启驼峰命名法映射规则
              //Lambda表达式写法
              return (Configuration configuration) -> configuration.setMapUnderscoreToCamelCase(true);
      
              //普通写法
      //        return new ConfigurationCustomizer(){
      //            @Override
      //            public void customize(Configuration configuration) {
      //                configuration.setMapUnderscoreToCamelCase(true);
      //            }
      //        };
          }
      }	
      
      重新查询的结果:在这里插入图片描述
  5. 问题二:如果有多有很多mapper,每个都加@Mapper注解太麻烦了,我们可以在MyBatisConfig配置类(或者SpringBoot06DataMybatisApplication主配置类)上加上@MapperScan(value = “com.angenin.springboot.mapper”)注解,把mapper包下的所有接口作为mapper,批量扫描所有的mapper接口。
    在这里插入图片描述

xml

  1. 在springboot/mapper下新建EmployeeMapper
    //已经在MyBatisConfig配置类里加了@MapperScan注解,所以不需要加@Mapper
    public interface EmployeeMapper {
    
        Employee getEmpById(Integer id);
    
        void insertEmp(Employee employee);
    
    }
    
  2. 在resources目录下新建mybatis目录,在里面新建mybatis-config.xml全局配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <settings>
            <!--  开启驼峰命名法映射规则  -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
    </configuration>
    
  3. 在resources/mybatis目录下mapper目录,在里面新建EmployeeMapper.xml文件
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.angenin.springboot.mapper.EmployeeMapper">
    
        <select id="getEmpById" resultType="com.angenin.springboot.bean.Employee">
            select * from employee where id=#{id}
        </select>
    
        <insert id="insertEmp">
            insert into employee(lastName,email,gender,d_id)values(#{lastName},#{email},#{gender},#{dId})
        </insert>
    
    </mapper>
    
  4. 在application.yml中添加
    mybatis:
      config-location: classpath:mybatis/mybatis-config.xml
      mapper-locations: classpath:mybatis/mapper/*.xml
    
  5. 在DeptController中添加:
    @Autowired
    EmployeeMapper employeeMapper;
    
    
    
  6. 往employee数据表中添加数据:
    insert into employee(lastName,email,gender,d_id)values('zhangsan','zhangsan@qq.com',1,1);
    insert into employee(lastName,email,gender,d_id)values('lisi','lisi@qq.com',0,2);
    
  7. 启动项目,浏览器输入http://localhost:8080/emp/1
    在这里插入图片描述

4. JPA

JPA:基于ORM(Object Relational Mapping)思想,所以需要编写实体类(bean)和数据表进行映射,并且配置好映射关系。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
新建项目
在这里插入图片描述
在这里插入图片描述

  1. 新建数据库
    在这里插入图片描述

  2. 在resources目录下新建application.yml,配置数据源

    spring:
      datasource:
        username: root
        password: 123456
        url: jdbc:mysql://10.211.55.17:3306/jpa?serverTimezone=GMT
        driver-class-name: com.mysql.cj.jdbc.Driver
    
  3. 在springboot目录下entity/User实体类(这里不使用lombok,常用普通写法,和lombok做对比)

    //使用JPA注解配置映射关系
    @Entity //告诉JPA这是一个实体类(和数据表映射的类)
    @Table(name = "tbl_user")   //指定和哪个数据表对应,如果省略name不写,默认是对应的数据表名是类名首字母小写即user
    public class User {
    
        @Id //这是一个主键
        @GeneratedValue(strategy = GenerationType.IDENTITY) //设置主键自增
        private Integer id;
    
        @Column(name = "last_name", length = 50) //设置和数据表对应的列名
        private String lastName;
    
        @Column //省略默认属性名就是列名
        private String email;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    }
    
  4. 在springboot下新建repository/UserRepository,来操作实体类对应的数据表(即dao层)

    @Repository //继承JpaRepository即可操作数据库(第一个参数为要操作的类,第二个参数为主键的类型)
    public interface UserRepository extends JpaRepository<User, Integer> {
    }
    
  5. 对应的数据表可以自己创建,也可以配置后自动创建,在application.yml中添加配置,具体可查看JpaProperties(jpa与datasource同级别)

      jpa:
        hibernate:
          ddl-auto: update  #更新或创建数据表结构
        show-sql: true  #控制台显示sql
    

    在这里插入图片描述

  6. 启动项目后查看数据库
    在这里插入图片描述

  7. 对数据的增删查改,在springboot包里新建controller/UserController

    @RestController
    public class UserController {
    
        @Autowired
        UserRepository userRepository;
    
    
        @GetMapping("/user/{id}")
        public User getUser(@PathVariable("id") Integer id){
            return userRepository.findById(id).get();
        }
    
        @GetMapping("/user")
        public User insertUser(User user){
            return userRepository.save(user);   //save方法返回的user对象有自增的主键
        }
    
    }
    
  8. 启动项目,浏览器输入http://localhost:8080/user?lastName=zhangsan&email=zhangsan@qq.com插入一条数据,然后输入http://localhost:8080/user/2(因为我先插了一条,忘截图了,所以这里id是2)
    在这里插入图片描述
    在这里插入图片描述

七、SpringBoot启动配置原理

在这里插入图片描述
在这里插入图片描述

启动流程

  1. 创建SpringApplication对象

    	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    		this.resourceLoader = resourceLoader;
    		//判断primarySources是否为空,如果为空就抛出异常throw new IllegalArgumentException(msg),代码如果不捕捉处理这个异常,代码不往下执行,不为空代码继续向下执行。
    		Assert.notNull(primarySources, "PrimarySources must not be null");
    		//保存主配置类
    		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    		//保存当前应用的类型SERVLET(有三种类型:REACTIVE、SERVLET、NONE)
    		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    		//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然后保存起来(详情看下图)
    		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    		//从类路径下找到META-INF/spring.factories配置的所有ApplicationListener,然后保存起来(详情看下图)
    		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    		//从多个配置类中找到有main方法的主配置类
    		this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    保存起来的META-INF/spring.factories配置的所有ApplicationContextInitializer,共7个。
    在这里插入图片描述
    保存起来的META-INF/spring.factories配置的所有ApplicationListener,共11个。
    在这里插入图片描述

  2. 运行run方法

    public ConfigurableApplicationContext run(String... args) {
    	StopWatch stopWatch = new StopWatch();
    	//开启停止监听
    	stopWatch.start();
    	//声明一个IOC容器
    	ConfigurableApplicationContext context = null;
    	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    	//与awt相关(图形界面)
    	configureHeadlessProperty();
    	//获取SpringApplicationRunListeners(点进去发现也是从类路径META-INF/spring.factories中获取(详情看下图))
    	SpringApplicationRunListeners listeners = getRunListeners(args);
    	//回调(运行)获取到的所有SpringApplicationRunListeners的starting()方法(因为只有一个,所以在里面只循环了一次)
    	listeners.starting();
    	try {
    		//封装命令行参数
    		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    		//准备环境
    		//	1. 创建并配置环境
    		//	2. 回调(运行)获取到的所有SpringApplicationRunListeners的environmentPrepared方法(表示环境准备完成)
    		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    		//得到系统属性spring.beaninfo.ignore,如果为空设置为true
    		configureIgnoreBeanInfo(environment);
    		//打印Banner图标(详情看下图)
    		Banner printedBanner = printBanner(environment);
    		//创建ApplicationContext(根据应用的类型创建IOC容器(当前为应用类型为servlet))
    		context = createApplicationContext();
    		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    				new Class[] { ConfigurableApplicationContext.class }, context);
    		//准备上下文环境
    		//	1. 将environment保存到ioc容器中
    		//	2. applyInitializers(context):回调(运行)之前获取到的所有ApplicationContextInitializer的initialize方法
    		//	3. listeners.contextPrepared(context):回调(运行)之前获取到的所有ApplicationListener的contextPrepared方法
    		//	...
    		//	4. 最后listeners.contextLoaded(context):回调(运行)之前获取到的所有ApplicationListener的contextLoaded方法
    		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    		//刷新容器(IOC容器初始化完成,此时所有的组件bean都创建了)
    		//扫描、创建、加载所有组件的地方()
    		refreshContext(context);
    		//点进去发现是个空方法
    		afterRefresh(context, applicationArguments);
    		stopWatch.stop();
    		if (this.logStartupInfo) {
    			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    		}
    		//回调(运行)之前获取到的所有ApplicationListener的started方法
    		listeners.started(context);
    		callRunners(context, applicationArguments);
    	}
    	catch (Throwable ex) {
    		handleRunFailure(context, ex, exceptionReporters, listeners);
    		throw new IllegalStateException(ex);
    	}
    
    	try {
    		listeners.running(context);
    	}
    	catch (Throwable ex) {
    		handleRunFailure(context, ex, exceptionReporters, null);
    		throw new IllegalStateException(ex);
    	}
    	//整个springboot应用启动完成以后返回启动的IOC容器
    	return context;
    }
    

    从类路径META-INF/spring.factories中获取的SpringApplicationRunListeners
    在这里插入图片描述
    在这里插入图片描述

事件监听机制

几个主要的事件回调机制:

配置在META-INF/spring.factories中

  • ApplicationContextInitializer
  • SpringApplicationRunListener

只需要放在IOC容器中

  • ApplicationRunner
  • CommandLineRunner

创建实现了上面4个接口的类
ApplicationContextInitializer

package com.angenin.springboot07.listener;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

//泛型是监听的对象(ConfigurableApplicationContext是IOC容器对象)
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("HelloApplicationContextInitializer...initialize..." + configurableApplicationContext);
    }
}

SpringApplicationRunListener

package com.angenin.springboot07.listener;

import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {


    //需要配置有参构造器,不然会报错
    public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
        
    }

    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting...监听容器的开始");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        //environment为环境信息
        //获取系统的属性,os.name是系统的名字
        Object o = environment.getSystemEnvironment().get("os.name");
        System.out.println("SpringApplicationRunListener...environmentPrepared...环境准备好了 " + o);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //context为IOC容器
        System.out.println("SpringApplicationRunListener...contextPrepared...IOC容器准备好了");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...contextLoaded...服务器环境加载完成");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...started...");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...running...");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener...failed...");
    }
}

ApplicationRunner

package com.angenin.springboot07.listener;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run...");
    }
}

CommandLineRunner

package com.angenin.springboot07.listener;

import org.springframework.boot.CommandLineRunner;

import java.util.Arrays;

@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run..." + Arrays.asList(args));
    }
}

ApplicationRunner和CommandLineRunner只需要加上@Component添加到容器中即可使用。
ApplicationContextInitializer和SpringApplicationRunListener需要在resources目录下新建META-INF目录,并在里面新建一个名为spring.factories的文件。

org.springframework.context.ApplicationContextInitializer=\
com.angenin.springboot07.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.angenin.springboot07.listener.HelloSpringApplicationRunListener

启动后控制台输出的信息

SpringApplicationRunListener...starting...监听容器的开始
SpringApplicationRunListener...environmentPrepared...环境准备好了 null

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

HelloApplicationContextInitializer...initialize...org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@23f7d05d, started on Thu Jan 01 08:00:00 CST 1970
SpringApplicationRunListener...contextPrepared...IOC容器准备好了
2020-05-24 21:53:25.458  INFO 41926 --- [           main] c.a.s.SpringBoot07Application            : Starting SpringBoot07Application on prodeMacBook-Pro.local with PID 41926 (/Users/pro/IdeaProjects/spring-boot-07/target/classes started by pro in /Users/pro/IdeaProjects/spring-boot-07)
2020-05-24 21:53:25.465  INFO 41926 --- [           main] c.a.s.SpringBoot07Application            : No active profile set, falling back to default profiles: default
SpringApplicationRunListener...contextLoaded...服务器环境加载完成
2020-05-24 21:53:27.954  INFO 41926 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-05-24 21:53:27.972  INFO 41926 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-05-24 21:53:27.973  INFO 41926 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.35]
2020-05-24 21:53:28.099  INFO 41926 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-05-24 21:53:28.099  INFO 41926 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2548 ms
2020-05-24 21:53:28.467  INFO 41926 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-24 21:53:28.782  INFO 41926 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-24 21:53:28.794  INFO 41926 --- [           main] c.a.s.SpringBoot07Application            : Started SpringBoot07Application in 3.978 seconds (JVM running for 5.535)
SpringApplicationRunListener...started...
ApplicationRunner...run...
CommandLineRunner...run...[]
SpringApplicationRunListener...running...

如果需要在项目特定位置运行相应的代码,可以使用上面的实现类来完成。

八、SpringBoot自定义starters

starter:场景启动器

  1. 这个场景需要使用到什么依赖?
  2. 如何编写自动配置
    @Configuration	//指定这个类是配置类
    @ConditionalOnXXX	//在指定条件成立的情况下自动配置类生效
    @AutoConfigureAfter	//指定自动配置类的顺序
    @Bean	//给容器中添加组件
    
    @ConfigurationPropertie	//结合相关xxxProperties类来绑定相关的配置
    @EnableConfigurationProperties	//让xxxProperties加入到容器中生效
    
    //让自动配置类能加载:需要将需要启动就加载的自动配置类,配置到META-INF/spring.factories。
    //例如:
    //org.springframework.context.ApplicationContextInitializer=\
    //org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    //org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    
  3. 模式:
    在这里插入图片描述
    启动器只用来做依赖导入,专门写一个自动配置模块,然后启动器依赖自动配置模块,而别人只需要引入启动器(starter),自动配置也会被引入。
    官方命名为:spring-boot-starter-模块名
    我们自定义命名:模块名-spring-boot-starter
创建自定义驱动器

在这里插入图片描述
添加模块:
在这里插入图片描述
选择Maven
在这里插入图片描述
在这里插入图片描述
在创建一个模块,选择Spring Initializr
在这里插入图片描述
在这里插入图片描述
不引入模块
在这里插入图片描述
在这里插入图片描述
Maven创建的用来做启动器,Spring Initializr创建的用来做自动配置。
复制自定义的自动配置模块的坐标
在这里插入图片描述
在启动器的pom.xml中引入
在这里插入图片描述
删除自动配置模块的主程序和主配置。
在这里插入图片描述
删除自动配置pom.xml中的test依赖,和插件(spring-boot-starter是所有starter的基本配置,所以需要留着)
并且把根目录下的test目录也删掉
在这里插入图片描述
写业务
在这里插入图片描述
HelloProperties

package com.angenin.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "angenin.hello")
public class HelloProperties {

    //前缀
    private String prefix;
    //后缀
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

HelloService

package com.angenin.starter;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHello(String name){
        return helloProperties.getPrefix() + name + helloProperties.getSuffix();
    }

}

HelloServiceAutoConfiguration

package com.angenin.starter;

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHello(String name){
        return helloProperties.getPrefix() + name + helloProperties.getSuffix();
    }

}

而要让这个自动配置类能生效,需要在resources目录下新建META-INF目录,在里面新建一个名为spring.factories的文件。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.angenin.starter.HelloServiceAutoConfiguration

把设置好的自动配置模块安装到maven仓库中
在这里插入图片描述
然后再把启动器也安装到maven仓库中
在这里插入图片描述
新建项目,用于测试自定义的starter
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复制自定义starter的坐标
在这里插入图片描述
在新建的项目里引入
在这里插入图片描述
在这里插入图片描述
在新建的项目springboot目录下新建controller/Hellocontroller,自动注入自定义的starter中的HelloService

@RestController
public class Hellocontroller {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.sayHello("haha");
    }

}

自动配置HelloProperties的前后缀可以在application.properties配置文件设置

angenin.hello.prefix=ANGENIN
angenin.hello.suffix=HELLOWORLD

启动项目,在浏览器中输入http://localhost:8080/hello
在这里插入图片描述

下一篇笔记:SpringBoot高级篇学习笔记(四、缓存与消息)

学习视频(p53-p72):https://www.bilibili.com/video/BV1gW411W76m?p=53

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值