springboot pom文件指定jdk_Spring Boot 入门

springboot pom文件指定jdk_Spring Boot 入门_第1张图片

SpringBoot 基本应用

约定优于配置

约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计范式。

本质上是说,系统、类库或框架应该假定合理的默认值,而非要求提供不必要的配置。比如说模型中有一个名为 User 的类,那么数据库中对应的表就会默认命名为 User。只有在偏离这一个约定的时候,例如想要将该表命名为 person,才需要写有关这个名字的配置。

比如平时架构师搭建项目就是限制软件开发随便写代码,制定出一套规范,让开发人员按统一的要求进行开发编码测试之类的,这样就加强了开发效率与审查代码效率。所以说写代码的时候就需要按要求命名,这样统一规范的代码就有良好的可读性与维护性了。

约定优于配置简单来理解,就是遵循约定。

Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了尽可能快的跑起来 Spring 应用程序并且尽可能减少配置文件。

SpringBoot 概念

Spring 优缺点分析

优点:Spring 是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品。无需开发重量级的 Enterprise Java Bean - EJB,Spring 为企业级 Java 开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的 Java 对象(Plain Old Java Object,POJO)实现了 EJB 的功能。

缺点:虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring 用 XML 配置,而且是很多 XML 配 置。Spring 2.5 引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式 XML 配置。Spring 3.0 引入 了基于 Java 的配置,这是一种类型安全的可重构配置方式,可以代替 XML。

所有这些配置都代表了开发时的损耗。因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring 实用,但与此同时它要求的回报也不少。

除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。SSM 整合:Spring、Spring MVC、Mybatis、Spring-Mybatis 整合包、数据库驱动,引入依赖的数量繁多、容易存在版本冲突。

Spring Boot 解决上述 Spring 的问题

SpringBoot 对上述 Spring 的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

起步依赖:

起步依赖本质上是一个 Maven 项目对象模型 (Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。 
简单的说,起步依赖就是将具备某种功能的依赖坐标打包到一起,并提供一些默认的功能。

自动配置:

springboot 的自动配置,指的是 springboot 会自动将一些配置类的 bean 注册进 ioc 容器,在需要的地方使用 @autowired 或者 @resource 等注解来使用它。
“自动”的表现形式就是只需要引想用功能的包,相关的配置完全不用管,springboot 会自动注入这些配置 bean,直接使用这些 bean 即可。

Springboot 可以简单、快速、方便地搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率。

SpringBoot 入门案例

案例需求:请求 Controller 中的方法,并将返回值响应到页面。

1) 依赖管理

pom.xml



    org.springframework.bootgroupId>
    spring-boot-starter-parentartifactId>
    2.2.2.RELEASEversion>
parent>


    
    
        org.springframework.bootgroupId>
        spring-boot-starter-webartifactId>
    dependency>
dependencies>



    
        
            org.springframework.bootgroupId>
            spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>
2) 启动类

com.renda.SpringBootDemo1Application

/**
 * SpringBoot 的启动类通常放在二级包中,
 * 比如:com.renda.SpringBootDemo1Application。
 * 因为 SpringBoot 项目在做包扫描,
 * 会扫描启动类所在的包及其子包下的所有内容。
 *
 * @author Renda Zhang
 * @since 2020-10-28 23:11
 */
@SpringBootApplication // 标识当前类为 SpringBoot 项目的启动类
public class SpringBootDemo1Application {

    public static void main(String[] args) {
        // 样板代码
        SpringApplication.run(SpringBootDemo1Application.class, args);
    }

}
3) Controller

com.renda.controller.HelloController

@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/boot")
    public String helloSpringBoot() {
        return "Hello Spring Boot";
    }

}

SpringBoot 快速构建

案例需求:请求 Controller 中的方法,并将返回值响应到页面。

1)使用 Spring Initializr 方式构建 Spring Boot 项目

本质上说,Spring Initializr 是一个 Web 应用,它提供了一个基本的项目结构,能够快速构建一个基础的 Spring Boot 项目。

注意使用快速方式创建 Spring Boot 项目时,所在主机须在联网状态下;本质上是在开发工具执行各项参数后,由 Spring 提供的 URL 所对应的服务器生成, IDEA 将服务器生成的 SpringBoot 项目下载到本地的工作空间中。

Project SDK 用于设置创建项目使用的 JDK 版本,这里,使用之前初始化设置好的 JDK 版本即可;在 Choose Initializr Service URL 下使用默认的初始化服务地址 https://start.spring.io 进行 Spring Boot 项目创建。

创建完成后的 pom 文件



    4.0.0modelVersion>
    
        org.springframework.bootgroupId>
        spring-boot-starter-parentartifactId>
        2.2.2.RELEASEversion>
         
    parent>
    com.rendagroupId>
    springbootdemo2artifactId>
    0.0.1-SNAPSHOTversion>
    springbootdemo2name>
    Demo project for Spring Bootdescription>

    
        11java.version>
    properties>

    
        
            org.springframework.bootgroupId>
            spring-boot-starter-webartifactId>
        dependency>

        
            org.springframework.bootgroupId>
            spring-boot-starter-testartifactId>
            testscope>
            
                
                    org.junit.vintagegroupId>
                    junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

    
        
            
                org.springframework.bootgroupId>
                spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

创建完成后,可以删除自动生成的 .mvn.gitignoreHELP.mdmvnwmvnw.cmd

创建好的 Spring Boot 项目结构如图:

/src/main/java/
    com.renda.Springbootdemo2Application - 项目主程序启动类
/src/main/resources/
    static - 静态资源文件夹
    templates - 模板页面文件夹
    application.properties - 全网配置文件
/src/test/java/
    com.renda.Springbootdemo2ApplicationTests - 项目测试类

使用 Spring Initializr 方式构建的 Spring Boot 项目会默认生成项目启动类、存放前端静态资源和页面的文件夹、编写项目配置的配置文件以及进行项目单元测试的测试类。

2)创建一个用于 Web 访问的 Controller

注意:确保项目启动类 Springbootdemo2Application 在 com.renda 包下。

com.renda.controller.HelloController

@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/boot")
    public String hello() {
        return "What's up! Spring Boot!";
    }

}
3) 运行项目

application.properties 中修改 tomcat 的默认端口号:

server.port=8888

运行主程序启动类 Springbootdemo2Application,项目启动成功后,在控制台上会发现 Spring Boot 项目默认启动的端口号为 8888,此时,可以在浏览器上访问 http://localhost:8888/hello/boot。

页面输出的内容是 “What's up! Spring Boot!”,至此,构建 Spring Boot 项目就完成了。

单元测试与热部署

单元测试

开发中,每当完成一个功能接口或业务方法的编写后,通常都会借助单元测试验证该功能是否正确。

1)添加 spring-boot-starter-test 测试依赖启动器,在项目的 pom.xml 文件中添加 spring-boot-starter-test 测试依赖启动器,示例代码如下 :


    org.springframework.bootgroupId>
    spring-boot-starter-testartifactId>
    testscope>
    
        
            org.junit.vintagegroupId>
            junit-vintage-engineartifactId>
        exclusion>
    exclusions>
dependency>

注意:使用 Spring Initializr 方式搭建的 Spring Boot 项目,会自动加入 spring-boot-starter-test 测试依赖启动器,无需再手动添加。

2)编写单元测试类和测试方法:

/**
 *  SpringJUnit4ClassRunner.class: Spring 运行环境
 *  JUnit4.class: JUnit 运行环境
 *  SpringRunner.class: Spring Boot 运行环境
 */
@RunWith(SpringRunner.class) // @RunWith: 运行器
@SpringBootTest // 标记为当前类为 SpringBoot 测试类,加载项目的 ApplicationContext 上下文环境
class Springbootdemo2ApplicationTests {

    @Autowired
    private HelloController helloController;

    @Test
    void contextLoads() {
        String result = helloController.hello();
        System.out.println(result);
    }

}

上述代码中,先使用 @Autowired 注解注入了 HelloController 实例对象,然后在 contextLoads() 方法中调用了 HelloController 类中对应的请求控制方法 hello(),并输出打印结果。

热部署

在开发过程中,通常会对一段业务代码不断地修改测试,在修改之后往往需要重启服务,有些服务需要加载很久才能启动成功,这种不必要的重复操作极大的降低了程序开发效率。为此,Spring Boot 框架专门提供了进行热部署的依赖启动器,用于进行项目热部署,而无需手动重启项目 。

热部署:在修改完代码之后,不需要重新启动容器,就可以实现更新。

使用步骤:

1)添加 spring-boot-devtools 热部署依赖启动器。

在 Spring Boot 项目进行热部署测试之前,需要先在项目的 pom.xml 文件中添加 spring-boot-devtools 热部署依赖启动器:



    org.springframework.bootgroupId>
    spring-boot-devtoolsartifactId>
dependency>

由于使用的是 IDEA 开发工具,添加热部署依赖后可能没有任何效果,接下来还需要针对 IDEA 开发工具进行热部署相关的功能设置。

2)开启 IDEA 的自动编译。

选择 IDEA 工具界面的 【File】->【Settings】 选项,打开 Compiler 面板设置页面。

选择 Build 下的 Compiler 选项,在右侧勾选 “Build project automatically” 选项将项目设置为自动编译,单击 【Apply】->【OK】 按钮保存设置。

3)开启 IDEA 的在项目运行中自动编译的功能。

在项目任意页面中使用组合快捷键 “Ctrl+Shift+Alt+/” 打开 Maintenance 选项框,选中并打开 Registry 页面。

列表中找到 “compiler.automake.allow.when.app.running”,将该选项后的 Value 值勾选,用于指定 IDEA 工具在程序运行过程中自动编译,最后单击 【Close】 按钮完成设置。
热部署效果测试

启动 Springbootdemo2Application http://localhost:8888/hello/boot

页面原始输出的内容是 “What's up! Spring Boot!”。

为了测试配置的热部署是否有效,接下来,在不关闭当前项目的情况下,将 HelloController 类中的请求处理方法 hello() 的返回值修改为 “Hello! Spring Boot!” 并保存,查看控制台信息会发现项目能够自动构建和编译,说明项目热部署生效。

再次刷新浏览器,输出了 “Hello! Spring Boot!”,说明项目热部署配置成功。

全局配置文件

全局配置文件能够对一些默认配置值进行修改。Spring Boot 使用一个 application.properties 或者 application.yaml 的文件作为全局配置文件,该文件存放在 src/main/resource 目录或者类路径的 /config,一般会选择 resource 目录。

Spring Boot 配置文件的命名及其格式:

  • application.properties

  • application.yaml

  • application.yml

`application.properties` 配置文件

引入相关依赖:


    org.springframework.bootgroupId>
    spring-boot-starter-jdbcartifactId>
dependency>


    mysqlgroupId>
    mysql-connector-javaartifactId>
    6.0.6version>
dependency>

使用 Spring Initializr 方式构建 Spring Boot 项目时,会在 resource 目录下自动生成一个空的 application.properties 文件,Spring Boot 项目启动时会自动加载 application.properties 文件。

# 修改 tomcat 的版本号
server.port=8080
# 定义数据库的连接信息 JdbcTemplate
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/renda01
spring.datasource.username=root
spring.datasource.password=password

可以在 application.properties 文件中定义 Spring Boot 项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置。

预先准备了两个实体类文件,后续会演示将 application.properties 配置文件中的自定义配置属性注入到 Person 实体类的对应属性中。

1)先在项目的 com.renda 包下创建一个 pojo 包,并在该包下创建两个实体类 Pet 和 Person。

/**
 * 宠物类
 *
 * @author Renda Zhang
 * @since 2020-10-29 1:03
 */
public class Pet {
    // 品种
    private String type;
    // 名称
    private String name;
    // setter and getter, toString ...
}

/**
 * 人类
 *
 * @author Renda Zhang
 * @since 2020-10-29 1:04
 */
@Component
/**
 * 将配置文件中所有以 person 开头的配置信息注入当前类中
 * 前提 1:必须保证配置文件中 person.xx 与当前 Person 类的属性名一致
 * 前提 2:必须保证当前 Person 中的属性都具有 set 方法
 */
@ConfigurationProperties(prefix = "person")
public class Person {
    // id
    private int id;
    // 名称
    private String name;
    // 爱好
    private List hobby;
    // 家庭成员
    private String[] family;
    private Map map;
    // 宠物
    private Pet pet;
    // setter and getter, toString ...
}

@ConfigurationProperties(prefix = "person") 注解的作用是将配置文件中以 person 开头的属性值通过 setXX() 方法注入到实体类对应属性中。

@Component 注解的作用是将当前注入属性值的 Person 类对象作为 Bean 组件放到 Spring 容器中,只有这样才能被 @ConfigurationProperties 注解进行赋值。

2)打开项目的 resources 目录下的 application.properties 配置文件,在该配置文件中编写需要对 Person 类设置的配置属性。

# 自定义配置信息注入到 Person 对象中
person.id=100
person.name=张人大
## list
person.hobby=看书,写代码,吃饭
## String[]
person.family=兄弟,父母
## map
person.map.k1=v1
person.map.k2=v2
## pet 对象
person.pet.type=dog
person.pet.name=旺财

3)查看 application.properties 配置文件是否正确,同时查看属性配置效果,打开通过 IDEA 工具创建的项目测试类,在该测试类中引入 Person 实体类 Bean,并进行输出测试。

@RunWith(SpringRunner.class)
@SpringBootTest
class Springbootdemo2ApplicationTests {

    @Autowired
    private Person person;

    @Test
    void showPersonInfo() {
        System.out.println(person);
    }

}

可以看出,测试方法 showPersonInfo() 运行成功,同时正确打印出了 Person 实体类对象。至此,说明 application.properties 配置文件属性配置正确,并通过相关注解自动完成了属性注入。

4)解决浏览器请求出现中文乱码问题。

调整文件编码格式:Settings -> Editor -> File Encodings -> 确保 Global Encoding 和 Project Encoding 为 UTF-8,修改 Default ecoding for properties files 为 UTF-8 并勾选 Transparent native-to-ascii conversion。

application.properties 文件中增加配置:

# 解决中文乱码
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
`application.yaml` 配置文件

YAML 文件格式是 Spring Boot 支持的一种 JSON 文件格式,相较于传统的 Properties 配置文件,YAML 文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。application.yaml 配置文件的工作原理和 application.properties 是一样的,只不过 yaml 格式配置文件看起来更简洁一些。

  • YAML 文件的扩展名可以使用 .yml 或者 .yaml

  • application.yml 文件使用 key: value 格式配置属性,使用缩进控制层级关系。

SpringBoot 的三种配置文件是可以共存的:application.propertiesapplication.yamlapplication.yml

1)value 值为普通数据类型(例如数字、字符串、布尔等)

当 YAML 配置文件中配置的属性值为普通数据类型时,可以直接配置对应的属性值,同时对于字符串类型的属性值,不需要额外添加引号,示例代码如下:

server:
  port: 8080
  servlet:
    context-path: /hello

2)value 值为数组和单列集合

当 YAML 配置文件中配置的属性值为数组或单列集合类型时,主要有两种书写方式:缩进式写法和行内式写法。

其中,缩进式写法还有两种表示形式,示例代码如下:

person: 
  hobby: 
    - play
    - read
    - sleep 

或者使用如下示例形式:

person:
  hobby:
    play,
    read,
    sleep

上述代码中,在 YAML 配置文件中通过两种缩进式写法对 person 对象的单列集合(或数组)类型的爱好 hobby 赋值为 play、read 和 sleep。其中一种形式为 “- 属性值”,另一种形式为多个属性值之前加英文逗号分隔;注意,最后一个属性值后不要加逗号。

person: 
  hobby: [play,read,sleep]

通过上述示例对比发现,YAML 配置文件的行内式写法更加简明、方便。另外,包含属性值的中括号 “[]” 还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对。

3)value 值为 Map 集合和对象

当 YAML 配置文件中配置的属性值为 Map 集合或对象类型时,YAML 配置文件格式同样可以分为两种书写方式 - 缩进式写法和行内式写法。

其中,缩进式写法的示例代码如下:

person: 
  map: 
    k1: v1
    k2: v2

对应的行内式写法示例代码如下:

person:
  map: {k1: v1,k2: v2}

在YAML配置文件中,配置的属性值为Map集合或对象类型时,缩进式写法的形式按照YAML文件格式编写即可,而行内式写法的属性值要用大括号“{}”包含。

接下来,在 Properties 配置文件演示案例基础上,注释掉 Properties 中跟 Person 相关的注入,然后通过配置 application.yaml 配置文件对 Person 对象进行赋值,具体使用如下。

在项目的 resources 目录下,新建一个 application.yaml 配置文件,在该配置文件中编写为 Person 类设置的配置属性:

person:
  id: 1000
  name: 张人大
  hobby:
    - 跑步
    - 瑜伽
    - 游泳
  family:
    - 张小明
    - 李小红
  map:
    k1: 这是 k1 对应的 value
    k2: 这是 k2 对应的 value
  pet:
    type: dog
    name: 金毛

再次执行测试。

可以看出,测试方法 showPersonInfo() 同样运行成功,并正确打印出了 Person 实体类对象。

需要说明的是,本次使用 application.yaml 配置文件进行测试时需要提前将 application.properties 配置文件中编写的配置注释,这是因为 application.properties 配置文件会覆盖 application.yaml 配置文件。

配置文件属性值的注入

spring-boot-starter-parent 的 pom 文件可以知道配置文件的优先级从低到高如下:


    **/application*.ymlinclude>
    **/application*.yamlinclude>
    **/application*.propertiesinclude>
includes>

使用 Spring Boot 全局配置文件设置属性时:

如果配置属性是 Spring Boot 已有属性,例如服务端口 server.port,那么 Spring Boot 内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。

如果配置的属性是用户自定义属性,例如刚刚自定义的 Person 实体类属性,还必须在程序中注入这些配置属性方可生效。

Spring Boot 支持多种注入配置文件属性的方式,下面来介绍如何使用注解 @ConfigurationProperties@Value 注入属性。

使用 `@ConfigurationProperties` 注入属性

Spring Boot 提供的 @ConfigurationProperties 注解用来快速、方便地将配置文件中的自定义属性值批量注入到某个 Bean 对象的多个对应属性中。假设现在有一个配置文件,如果使用 @ConfigurationProperties 注入配置文件的属性,示例代码如下:

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private int id;
    private String name;
    private List hobby;
    private String[] family;
    private Map map;
    private Pet pet;
    // setter and getter, toString ...
}

上述代码使用 @Component@ConfigurationProperties(prefix = “person”) 将配置文件中的每个属性映射到 person 类组件中。

使用 `@Value` 注入属性

@Value 注解是 Spring 框架提供的,用来读取配置文件中的属性值并逐个注入到 Bean 对象的对应属性中,Spring Boot 框架从 Spring 框架中对 @Value 注解进行了默认继承,所以在 Spring Boot 框架中还可以使用该注解读取和注入配置文件属性值。使用 @Value 注入属性的示例代码如下:

@Component
public class Student {
    @Value("${person.id}")
    private int number;
    // getter setter toString ...
}

上述代码中,使用 @Component@Value 注入 Person 实体类的id属性。其中,@Value 不仅可以将配置文件的属性注入 Person 的 id 属性,还可以直接给 id 属性赋值,这点是@ConfigurationProperties 不支持的。

1)在 com.renda.pojo 包下新创建一个实体类 Student,并使用 @Value 注解注入属性:

@Component
public class Student {

    @Value("${person.id}")
    private String number;

    @Value("${person.name}")
    private String name;

    // getter setter toString ...
}

Student 类使用 @Value 注解将配置文件的属性值读取和注入。

从上述示例代码可以看出,使用 @Value 注解方式需要对每一个属性注入设置,同时又免去了属性的 setXX() 方法。

2)再次打开测试类进行测试:

@RunWith(SpringRunner.class)
@SpringBootTest
class Springbootdemo2ApplicationTests {

    @Autowired
    private Student student;

    @Test
    public void showStudentInfo(){
        System.out.println(student);
    }

}

可以看出,测试方法 showStudentInfo() 运行成功,同时正确打印出了 Student 实体类对象。需要说明的是,本示例中只是使用 @Value 注解对实例中 Student 对象的普通类型属性进行了赋值演示,而 @Value 注解对于包含 Map 集合、对象以及 YAML 文件格式的行内式写法的配置文件的属性注入都不支持,如果赋值会出现错误。

自定义配置

Spring Boot 免除了项目中大部分的手动配置,对于一些特定情况,可以通过修改全局配置文件以适应具体生产环境,可以说,几乎所有的配置都可以写在 application.yml 文件中,Spring Boot 会自动加载全局配置文件从而免除手动加载的烦恼。但是,如果自定义配置文件,Spring Boot 是无法识别这些配置文件的,此时就需要手动加载。

使用 `@PropertySource` 加载配置文件

对于这种加载自定义配置文件的需求,可以使用 @PropertySource 注解来实现。@PropertySource 注解用于指定自定义配置文件的具体位置和名称。

如果需要将自定义配置文件中的属性值注入到对应类的属性中,可以使用 @ConfigurationProperties 或者 @Value 注解进行属性值注入。

1)打开 Spring Boot 项目的 resources 目录,在项目的类路径下新建一个 my.properties 自定义配置文件,在该配置文件中编写需要设置的配置属性。

# 对实体类对象 Product 进行属性配置
product.id=99
product.name=小米

2)在 com.renda.pojo 包下新创建一个配置类 Product,提供 my.properties 自定义配置文件中对应的属性,并根据 @PropertySource 注解的使用进行相关配置。

@Component
@PropertySource("classpath:my.properties")
@ConfigurationProperties(prefix = "product")
public class Product {
    private int id;
    private String name;
    // getter setter toString ...
}

主要是一个自定义配置类,通过相关注解引入了自定义的配置文件,并完成了自定义属性值的注入。针对示例中的几个注解,具体说明如下:

  • @PropertySource("classpath:my.properties") 注解指定了自定义配置文件的位置和名称,此示例表示自定义配置文件为 classpath 类路径下的 my.properties 文件。

  • @ConfigurationProperties(prefix = "product") 注解将上述自定义配置文件 my.properties 中以 product 开头的属性值注入到该配置类属性中。

3)进行测试。

@RunWith(SpringRunner.class)
@SpringBootTest
class Springbootdemo2ApplicationTests {

    @Autowired
    private Product product;

    @Test
    public void showProductInfo() {
        System.out.println(product);
    }

}
使用 `@Configuration` 编写自定义配置类

在 Spring Boot 框架中,推荐使用配置类的方式向容器中添加和配置组件。

在 Spring Boot 框架中,通常使用 @Configuration 注解定义一个配置类,Spring Boot 会自动扫描和识别配置类,从而替换传统 Spring 框架中的 XML 配置文件。

当定义一个配置类后,还需要在类中的方法上使用 @Bean 注解进行组件配置,将方法的返回对象注入到 Spring 容器中,并且组件名称默认使用的是方法名,当然也可以使用 @Bean 注解的 name 或 value 属性自定义组件的名称。

1)在项目下新建一个 com.renda.config 包,并在该包下新创建一个类 MyService,该类中不需要编写任何代码:

package com.renda.service;
public class MyService {
}

创建了一个空的 MyService 类,而该类目前没有添加任何配置和注解,因此还无法正常被 Spring Boot 扫描和识别。

2)在项目的 com.renda.config 包下,新建一个类 MyConfig,并使用 @Configuration 注解将该类声明一个配置类,内容如下:

@Configuration // 标识当前类是一个配置类,SpringBoot 会扫描该类,将所有标识 @Bean 注解的方法的返回值注入的容器中
public class MyConfig {

    @Bean // 注入的名称就是方法的名称,注入的类型就是返回值的类型
    public MyService myService(){
        return new MyService();
    }

    @Bean("service_")
    public MyService myService2(){
        return new MyService();
    }

}

MyConfig 是 @Configuration 注解声明的配置类(类似于声明了一个 XML 配置文件),该配置类会被 Spring Boot 自动扫描识别;使用 @Bean 注解的 myService() 方法,其返回值对象会作为组件添加到了 Spring 容器中(类似于 XML 配置文件中的标签配置),并且该组件的 id 默认是方法名 myService

3)测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
class Springbootdemo2ApplicationTests {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testConfig(){
        System.out.println(applicationContext.containsBean("myService"));
        System.out.println(applicationContext.containsBean("service_"));
    }

}

上述代码中,先通过 @Autowired 注解引入了 Spring 容器实例 ApplicationContext,然后在测试方法 testConfig() 中测试查看该容器中是否包括 id 为 myServiceservice_ 的组件。

从测试结果可以看出,测试方法 testConfig() 运行成功,显示运行结果为 true,表示 Spirng 的 IOC 容器中也已经包含了 id 为 myServiceservice_的实例对象组件,说明使用自定义配置类的形式完成了向 Spring 容器进行组件的添加和配置。

SpringBoot 原理深入及源码剖析

依赖管理

在 Spring Boot 入门程序中,项目 pom.xml 文件有两个核心依赖,分别是 spring-boot-starter-
parent 和 spring-boot-starter-web,关于这两个依赖的相关介绍具体如下:

spring-boot-starter-parent 依赖

    org.springframework.bootgroupId>
    spring-boot-starter-parentartifactId>
    2.2.2.RELEASEversion>
    
parent>

上述代码中,将 spring-boot-starter-parent 依赖作为 Spring Boot 项目的统一父项目依赖管理,并将项目版本号统一为 2.2.2.RELEASE,该版本号根据实际开发需求是可以修改的。

使用 “Ctrl+鼠标左键” 进入并查看 spring-boot-starter-parent 底层源文件,发现 spring-boot-
starter-parent 的底层有一个父依赖 spring-boot-dependencies,核心代码具体如下


    org.springframework.bootgroupId>
    spring-boot-dependenciesartifactId>
    2.2.2.RELEASEversion>
    ../../spring-boot-dependenciesrelativePath>
parent>

继续查看 spring-boot-dependencies 底层源文件,核心代码具体如下:


    5.15.11activemq.version>
    ...
    8.2.0solr.version>
    2.2.2.RELEASEspring-amqp.version>
    4.2.1.RELEASEspring-batch.version>
    2.0.7.RELEASEspring-cloud-connectors.version>
    Moore-SR3spring-data-releasetrain.version>
    5.2.2.RELEASEspring-framework.version>
    1.0.2.RELEASEspring-hateoas.version>
    5.2.2.RELEASEspring-integration.version>
    2.3.4.RELEASEspring-kafka.version>
    2.3.2.RELEASEspring-ldap.version>
    2.0.4.RELEASEspring-restdocs.version>
    1.2.4.RELEASEspring-retry.version>
    5.2.1.RELEASEspring-security.version>
    Corn-RELEASEspring-session-bom.version>
    3.0.8.RELEASEspring-ws.version>
    3.28.0sqlite-jdbc.version>
    ${jakarta-mail.version}sun-mail.version>
    3.0.11.RELEASEthymeleaf.version>
    2.0.1thymeleaf-extras-data-attribute.version>
    ...
properties>

从 spring-boot-dependencies 底层源文件可以看出,该文件通过标签对一些常用技术框架的依赖文件进行了统一版本号管理,例如 activemq、spring、tomcat 等,都有与 Spring Boot 2.2.2 版本相匹配的版本,这也是 pom.xml 引入依赖文件不需要标注依赖文件版本号的原因。

需要说明的是,如果 pom.xml 引入的依赖文件不是 spring-boot-starter-parent 管理的,那么在 pom.xml 引入依赖文件时,需要使用标签指定依赖文件的版本号。

spring-boot-starter-web 依赖

spring-boot-starter-parent 父依赖启动器的主要作用是进行版本统一管理;项目运行依赖的 JAR 包是从 Spring Boot Starters 中导入。

查看 spring-boot-starter-web 依赖文件源码,核心代码具体如下:


    
        org.springframework.bootgroupId>
        spring-boot-starterartifactId>
        2.2.2.RELEASEversion>
        compilescope>
    dependency>
    
        org.springframework.bootgroupId>
        spring-boot-starter-jsonartifactId>
        2.2.2.RELEASEversion>
        compilescope>
    dependency>
    
        org.springframework.bootgroupId>
        spring-boot-starter-tomcatartifactId>
        2.2.2.RELEASEversion>
        compilescope>
    dependency>
    
        org.springframework.bootgroupId>
        spring-boot-starter-validationartifactId>
        2.2.2.RELEASEversion>
        compilescope>
        
            
                tomcat-embed-elartifactId>
                org.apache.tomcat.embedgroupId>
            exclusion>
        exclusions>
    dependency>
    
        org.springframeworkgroupId>
        spring-webartifactId>
        5.2.2.RELEASEversion>
        compilescope>
    dependency>
    
        org.springframeworkgroupId>
        spring-webmvcartifactId>
        5.2.2.RELEASEversion>
        compilescope>
    dependency>
dependencies>

从上述代码可以发现,spring-boot-starter-web 依赖启动器的主要作用是提供 Web 开发场景所需的底层所有依赖。

正是如此,在 pom.xml 中引入 spring-boot-starter-web 依赖启动器时,就可以实现 Web 场景开发,而不需要额外导入 Tomcat 服务器以及其他 Web 依赖文件等。当然,这些引入的依赖文件的版本号还是由 spring-boot-starter-parent 父依赖进行的统一管理。

Spring Boot Starters:

https://github.com/spring-projects/spring-boot/tree/v2.1.0.RELEASE/spring-boot-project/spring-boot-starters

https://mvnrepository.com/search?q=starter

Spring Boot 除了提供有上述介绍的 Web 依赖启动器外,还提供了其他许多开发场景的相关依赖,可以打开 Spring Boot 官方文档,搜索 “Starters” 关键字查询场景依赖启动器。

需要说明的是,Spring Boot 官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据库操作框架 MyBatis、阿里巴巴的 Druid 数据源等,Spring Boot 官方就没有提供对应的依赖启动器。为了充分利用 Spring Boot 框架的优势,在 Spring Boot 官方没有整合这些技术框架的情况下,MyBatis、Druid 等技术框架所在的开发团队主动与 Spring Boot 框架进行了整合,实现了各自的依赖启动器,例如 mybatis-spring-boot-starter、druid-spring-boot-starter 等。在 pom.xml 文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号。

自动配置

概念:能够在添加 jar 包依赖的时候,自动配置一些组件的相关配置,无需手动配置或者只需要少量手动配置就能运行编写的项目。

Spring Boot 应用的启动入口是 @SpringBootApplication 注解标注类中的 main() 方法。

@SpringBootApplication:SpringBoot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的 main() 方法启动 SpringBoot 应用。

进入到 @SpringBootApplication 内:

// 注解的适用范围, Type 表示注解可以描述在类、接口、注解或枚举中
@Target(ElementType.TYPE)
// 表示注解的生命周期,Runtime 运行时
@Retention(RetentionPolicy.RUNTIME)
// 表示注解可以记录在 javadoc 中
@Documented
// 表示可以被子类继承该注解
@Inherited
// 标明该类为 Spring Boot 配置类
@SpringBootConfiguration
// 启动自动配置功能
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

    // 根据 class 来排除特定的类,使其不能加入 spring 容器,传入参数 value 类型是 class 类型。
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class>[] exclude() default {};

    // 根据 classname 来排除特定的类,使其不能加入 spring 容器,传入参数 value 类型是class的全类名字符串数组。
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    // 指定扫描包,参数是包名的字符串数组。
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    // 扫描特定的包,参数类似是 Class 类型数组。
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class>[] scanBasePackageClasses() default {};

    ...   
}

从上述源码可以看出,@SpringBootApplication 注解是一个组合注解,前面 4 个是注解的元数据信息, 主要看后面 3 个注解:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 三个核心注解。

@SpringBootConfiguration 注解

@SpringBootConfiguration: SpringBoot 的配置类,标注在某个类上,表示这是一个  SpringBoot 的配置类。

查看 @SpringBootConfiguration 注解源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    ...
}

从上述源码可以看出,@SpringBootConfiguration 注解内部有一个核心注解 @Configuration,该注解是 Spring 框架提供的,表示当前类为一个配置类(XML 配置文件的注解表现形式),并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration 注解的作用与 @Configuration 注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过 @SpringBootConfiguration 是被 Spring Boot 进行了重新封装命名而已。

`@EnableAutoConfiguration` 注解

@EnableAutoConfiguration:开启自动配置功能,以前需要手动配置的东西,现在由  SpringBoot 自动配置,这个注解就是 Springboot 能实现自动配置的关键。

查看该注解内部查看源码信息,核心代码具体如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包
@AutoConfigurationPackage
// Spring 的底层注解 @Import,给容器中导入一个组件;导入的组件是AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationImportSelector.class)
// 告诉 SpringBoot 开启自动配置功能,这样自动配置才能生效。
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    // 返回不会被导入到 Spring 容器中的类
    Class>[] exclude() default {};

    // 返回不会被导入到 Spring 容器中的类名
    String[] excludeName() default {};

}

可以发现它是一个组合注解,  Spring  中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的 Bean ,并加载到 IOC 容器。@EnableAutoConfiguration 就是借助 @Import 来收集所有符合自动配置条件的 bean 定义,并加载到 IoC 容器。

@AutoConfigurationPackage 注解

查看 @AutoConfigurationPackage 注解内部源码信息,核心代码具体如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

从上述源码可以看出,@AutoConfigurationPackage 注解的功能是由 @Import 注解实现的,它是 spring 框架的底层注解,它的作用就是给容器中导入某个组件类,例如 @Import(AutoConfigurationPackages.Registrar.class),它就是将 Registrar 这个组件类导入到容器中,可查看 Registrar 类中 registerBeanDefinitions 方法,这个方法就是导入组件类的具体实现:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }

    @Override
    public Set determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImport(metadata));
    }

}

从上述源码可以看出,在 Registrar 类中有一个 registerBeanDefinitions() 方法,使用 Debug 模式启动项目, 可以看到选中的部分就是 com.renda。也就是说,@AutoConfigurationPackage 注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到 spring 容器中。

因此在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫描。

@Import({AutoConfigurationImportSelector.class}) 注解

AutoConfigurationImportSelector 这个类导入到 Spring 容器中,AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器 ApplicationContext 中。

继续研究 AutoConfigurationImportSelector 这个类,通过源码分析这个类中是通过selectImports 这个方法告诉 springboot 都需要导入那些组件:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    // 获得自动配置元信息,需要传入 beanClassLoader 这个类加载器
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                                                                              annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

深入研究 loadMetadata 方法 :

// 文件中为需要加载的配置类的类路径
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

private AutoConfigurationMetadataLoader() {
}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    return loadMetadata(classLoader, PATH);
}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
    try {
        // 读取 spring-boot-autoconfigure-2.1.5.RELEASE.jar 包中的 spring-autoconfigure-metadata.properties 的信息生成 url
        Enumeration urls = (classLoader != null) ? classLoader.getResources(path)
            : ClassLoader.getSystemResources(path);
        Properties properties = new Properties();// 解析 urls 枚举对象中的信息封装成 properties 对象并加载while (urls.hasMoreElements()) {
            properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
        }// 根据封装好的 properties 对象生成 AutoConfigurationMetadata 对象并返回return loadMetadata(properties);
    }catch (IOException ex) { throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
    }
}

AutoConfigurationImportSelectorgetAutoConfigurationEntry 方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
      
    // 判断 EnabledAutoConfiguration 注解有没有开启, 默认开启
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获得注解的属性信息
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取默认支持的自动配置类列表
    List configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去重
    configurations = removeDuplicates(configurations);// 去除一些多余的配置类,根据 @EnabledAutoConfiguration 的 exclusions 属性进行排除
    Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);// 根据 pom 文件中加入的依赖文件筛选中最终符合当前项目运行环境对应的自动配置类
    configurations = filter(configurations, autoConfigurationMetadata);// 触发自动配置导入监听事件
    fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}

深入 getCandidateConfigurations 方法:

这个方法中有一个重要方法 loadFactoryNames,这个方法是让 SpringFactoryLoader 去加载一些组件的名字。

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      
    /**
     * loadFactoryNames:
     * 这个方法需要传入两个参数 getSpringFactoriesLoaderFactoryClass() 和 
     * getBeanClassLoader()。
     * getSpringFactoriesLoaderFactoryClass() 这个方法返回的是
     * @EnableAutoConfiguration.class。
     * getBeanClassLoader() 这个方法返回的是 beanClassLoader 类加载器
     */
    List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct.");return configurations;
}/**
 * Return the class used by { @link SpringFactoriesLoader} to load configuration
 * candidates.
 * @return the factory class
 */protected Class> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class;
}
...@Overridepublic void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader;
}protected ClassLoader getBeanClassLoader() { return this.beanClassLoader;
}

继续点开 loadFactoryNames 方法:

public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {
      
    // 获取出入的键
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap result = cache.get(classLoader);if (result != null) { return result;
    }try { // 如果类加载器不为 null,则加载类路径下 spring.factories 文件,将其中设置的配置类的全路径信息封装为 Enumeration 类对象
        Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();// 循环 Enumeration 类对象,根据相应的节点信息生成 Properties 对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为 Array,加入方法 result 集合中while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);return result;
    }catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

上面代码需要读取的 FACTORIES_RESOURCE_LOCATION 最终路径的长这样,而这个是 spring 提供的一个工具类:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

它其实是去加载一个外部的文件,而这文件是在 spring-boot-autoconfigure-2.2.2.RELEASE.jar 包下的 META-INF/spring.factories,打开 spring.factories 文件:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
...

@EnableAutoConfiguration 就是从 classpath 中搜寻 META-INF/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射 Java Refletion 实例化为对应的标注了 @Configuration 的 JavaConfig 形式的配置类,并加载到 IOC 容器中。

以入门项目为例,在项目中加入了 Web 环境依赖启动器,对应的 WebMvcAutoConfiguration 自动配置类就会生效,打开该自动配置类会发现,在该配置类中通过全注解配置类的方式对 Spring MVC 运行所需环境进行了默认配置,包括默认前缀、默认后缀、视图解析器、MVC 校验器等。而这些自动配置类的本质是传统 Spring MVC 框架中对应的 XML 配置文件,只不过在 Spring Boot 中以自动配置类的形式进行了预先配置。因此,在 Spring Boot 项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程序,当然,也可以对这些自动配置类中默认的配置进行更改。

总结:

因此 springboot 底层实现自动配置的步骤是:

1. springboot 应用启动。

2. @SpringBootApplication 起作用。

3. @EnableAutoConfiguration。

4. @AutoConfigurationPackage:这个组合注解主要是 @Import(AutoConfigurationPackages.Registrar.class),它通过将 Registrar 类导入到容器中,而 
Registrar 类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到 springboot 创建管理的容器中。

5. @Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector 类导入到容器中;AutoConfigurationImportSelector 类作用是通过 selectImports 方法执行的过程中,会使用内部工具类 SpringFactoriesLoader,查找 classpath 上所有 jar 包中的 META-INF/spring.factories 进行加载,实现将配置类信息交给 SpringFactory 加载器进行一系列的容器创建过程。
@ComponentScan 注解

@ComponentScan 注解具体扫描的包的根路径由 Spring Boot 项目主程序启动类所在包位置决定,在扫描过程中由前面介绍的 @AutoConfigurationPackage 注解进行解析,从而得到 Spring Boot 项目主程序启动类所在包的具体位置。

总结
@SpringBootApplication 的注解的功能简单来说就是 3 个注解的组合注解:

|- @SpringBootConfiguration
   // 通过 javaConfig 的方式来添加组件到 IOC 容器中
   |- @Configuration
|- @EnableAutoConfiguration
   // 自动配置包,与 @ComponentScan 扫描到的添加到 IOC
   |- @AutoConfigurationPackage
   // 到 META-INF/spring.factories 中定义的 bean 添加到 IOC 容器中
   |- @Import(AutoConfigurationImportSelector.class) 
// 包扫描
|- @ComponentScan

SpringBoot 数据访问

Spring Boot 整合 MyBatis

MyBatis 是一款优秀的持久层框架,Spring Boot 官方虽然没有对 MyBatis 进行整合,但是 MyBatis 团队自行适配了对应的启动器,进一步简化了使用 MyBatis 进行数据的操作。

因为 Spring Boot 框架开发的便利性,所以实现 Spring Boot 与数据访问层框架(例如 MyBatis)的整合非常简单,主要是引入对应的依赖启动器,并进行数据库相关参数设置即可。

基础环境搭建
1)数据准备

在 MySQL 中,先创建了一个数据库 springbootdata,然后创建了两个表 t_articlet_comment 并向表中插入数据。其中评论表 t_commenta_id 与文章表 t_article 的主键 id 相关联。

# 创建数据库
CREATE DATABASE IF NOT EXISTS springbootdata DEFAULT CHARACTER SET utf8;
# 选择使用数据库
USE springbootdata;

# 创建表 t_article 并插入相关数据
DROP TABLE IF EXISTS t_article;
CREATE TABLE t_article
(
    id      int(20) NOT NULL AUTO_INCREMENT COMMENT '文章id',
    title   varchar(200) DEFAULT NULL COMMENT '文章标题',
    content longtext COMMENT '文章内容',
    PRIMARY KEY (id)
) ENGINE = InnoDB AUTO_INCREMENT = 2 DEFAULT CHARSET=utf8;

INSERT INTO t_article VALUES (1, 'Spring Boot 基础入门', '从入门到精通讲解...');
INSERT INTO t_article VALUES (2, 'Spring Cloud 基础入门', '从入门到精通讲解...');

# 创建表 t_comment 并插入相关数据
DROP TABLE IF EXISTS t_comment;
CREATE TABLE t_comment
(
    id      int(20) NOT NULL AUTO_INCREMENT COMMENT '评论id',
    content longtext COMMENT '评论内容',
    author  varchar(200) DEFAULT NULL COMMENT '评论作者',
    a_id    int(20)      DEFAULT NULL COMMENT '关联的文章id',
    PRIMARY KEY (id)
) ENGINE = InnoDB AUTO_INCREMENT = 3 DEFAULT CHARSET=utf8;
INSERT INTO t_comment VALUES (1, '很全、很详细', 'lucy', 1);
INSERT INTO t_comment VALUES (2, '赞一个', 'tom', 1);
INSERT INTO t_comment VALUES (3, '很详细', 'eric', 1);
INSERT INTO t_comment VALUES (4, '很好,非常详细', '张三', 1);
INSERT INTO t_comment VALUES (5, '很不错', '李四', 2);
2)创建项目,引入相应的启动器

使用 Spring Initializr 来初始化项目。

项目名:springbootmybatis

包名:com.renda

启动器:SQL 的 MyBatis Framework、MySQL Driver,Web 的 Spring Web

3)编写与数据库表 t_comment 和 t_article 对应的实体类 Comment 和 Article

com.renda.pojo.Comment

public class Comment {
      
    private Integer id;
    private String content;
    private String author;
    private Integer aId;
    // getter setter toString ...
}

com.renda.pojo.Article

public class Article {
      
    private Integer id;
    private String title;
    private String content;
    // getter setter toString ...
}
4)编写配置文件

application.properties  更名为 application.yml。在 application.properties 配置文件中进行数据库连接配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: password
注解方式整合 Mybatis

需求:实现通过 ID 查询 Comment 信息。

1)创建一个对 t_comment 表数据操作的接口 CommentMapper

com.renda.mapper.CommentMapper

public interface CommentMapper {
      
    @Select("select * from t_comment where id = #{id}")
    Comment findById(Integer id);
}
2)在 Spring Boot 项目启动类上添加 @MapperScan("xxx") 注解

com.renda.SpringbootmybatisApplication

@SpringBootApplication
@MapperScan("com.renda.bootmybatis.mapper") //执行扫描mapper的包名
public class SpringbootmybatisApplication {

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

}
3)编写测试方法

导入 Junit 的依赖,增加测试方法:

com.renda.SpringbootmybatisApplicationTests

@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private CommentMapper commentMapper;

    @Test
    void findCommentById() {
        Comment comment = commentMapper.findById(1);
        System.out.println(comment);
    }

}

控制台中查询的 Comment 的 aId 属性值为 null,没有映射成功。这是因为编写的实体类 Comment 中使用了驼峰命名方式将 t_comment 表中的 a_id 字段设计成了 aId 属性,所以无法正确映射查询结果。

为了解决上述由于驼峰命名方式造成的表字段值无法正确映射到类属性的情况,可以在 Spring Boot 全局配置文件 application.yml 中添加开启驼峰命名匹配映射配置,示例代码如下:

mybatis:
  configuration:
    # 开启驼峰命名匹配映射
    map-underscore-to-camel-case: true
配置文件的方式整合 MyBatis

第一、二步骤使用 Free Mybatis plugin 插件生成:使用 IDEA 连接 Database,然后选中要自动生成代码的表,右键 -> mybatis-generator -> 按照需求输入信息,点击 ok。

1)创建一个用于对数据库表 t_article 数据操作的接口 ArticleMapper
public interface ArticleMapper {
      
    int deleteByPrimaryKey(Integer id);

    int insert(Article record);

    int insertSelective(Article record);

    Article selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Article record);

    int updateByPrimaryKey(Article record);
}
2)创建 XML 映射文件

resources 目录下创建一个统一管理映射文件的包 mapper,并在该包下编写与 ArticleMapper 接口方应的映射文件 ArticleMapper.xml


mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  
    
    
    
  resultMap>
  
    id, title, content
  sql>
  
    select 
    
    from t_article
    where id = #{id,jdbcType=INTEGER}
  select>
  
    delete from t_article
    where id = #{id,jdbcType=INTEGER}
  delete>
  
    insert into t_article (title, content)
    values (#{title,jdbcType=VARCHAR}, #{content,jdbcType=VARCHAR})
  insert>
  
    insert into t_article
    
      
        title,
      if>
      
        content,
      if>
    trim>
    
      
        #{title,jdbcType=VARCHAR},
      if>
      
        #{content,jdbcType=VARCHAR},
      if>
    trim>
  insert>
  
    update t_article
    
      
        title = #{title,jdbcType=VARCHAR},
      if>
      
        content = #{content,jdbcType=VARCHAR},
      if>
    set>
    where id = #{id,jdbcType=INTEGER}
  update>
  
    update t_article
    set title = #{title,jdbcType=VARCHAR},
      content = #{content,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  update>
mapper>
3)配置 XML 映射文件路径

在项目中编写的 XML 映射文件,Spring Boot 并无从知晓,所以无法扫描到该自定义编写的 XML 配置文件,还必须在全局配置文件 application.yml 中添加 MyBatis 映射文件路径的配置,同时需要添加实体类别名映射路径,示例代码如下:

mybatis:
  configuration:
    # 开启驼峰命名匹配映射
    map-underscore-to-camel-case: true
  # 加载 resources/mapper 文件夹下的所有的 xml 文件
  mapper-locations: classpath:mapper/*.xml
  # 配置 XML 映射文件中指定的实体类别名路径
  type-aliases-package: com.renda.pojo
4)编写单元测试进行接口方法测试
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {

    @Autowired
    private ArticleMapper articleMapper;

    @Test
    void findArticleById() {
        Article article = articleMapper.selectByPrimaryKey(1);
        System.out.println(article);
    }

}

Spring Boot 整合 Redis

1)添加Redis依赖包

在项目的 pom.xml 中添加如下:



    org.springframework.bootgroupId>
    spring-boot-starter-data-redisartifactId>
dependency>
2)配置 Redis 数据库连接

在 application.yml 中配置redis数据库连接信息,如下:

spring:
  redis:
    # Redis 服务器地址
    host: 192.168.186.128
    # Redis 服务器连接端口
    port: 6379
    jedis:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 18
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: 3000
        # 连接池中的最大空闲连接
        max-idle: 20
        # 连接池中的最小空闲连接
        min-idle: 2
    # 连接超时时间(毫秒)
    timeout: 3000
    # Redis 数据库索引(默认为 0)
    database: 0
3)编写 Redis 操作工具类

将 RedisTemplate 实例包装成一个工具类,便于对 redis 进行数据操作。

package com.renda.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author Renda Zhang
 * @since 2020-10-30 1:00
 */
@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 读取缓存
     */
    public Object get(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 写入缓存
     */
    public boolean set(String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value, 1, TimeUnit.DAYS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 更新缓存
     */
    public boolean getAndSet(final String key, String value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().getAndSet(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 删除缓存
     */
    public boolean delete(final String key) {
        boolean result = false;
        try {
            redisTemplate.delete(key);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
4)测试

写一个测试用例类来完成对 redis 的整合。

@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootmybatisApplicationTests {

    // 写入,key:1,value:mysql 数据库中 id 为 1 的 article 记录
    @Autowired
    private RedisUtils redisUtils;

    @Test
    void writeRedis() {
        redisUtils.set("1", articleMapper.selectByPrimaryKey(1));
        System.out.println("success");
    }

    @Test
    void readRedis() {
        Article article = (Article) redisUtils.get("1");
        System.out.println(article);
    }

}

SpringBoot 视图技术

支持的视图技术

前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复杂的问题、实现了前后端分离开发。Spring Boot 框架对很多常用的模板引擎技术(如:FreeMarker、Thymeleaf、Mustache 等)提供了整合支持。

Spring Boot 不太支持常用的 JSP 模板,并且没有提供对应的整合配置,这是因为使用嵌入式 Servlet 容器的 Spring Boot 应用程序对于 JSP 模板存在一些限制 :

  • 在 Jetty 和 Tomcat 容器中,Spring Boot 应用被打包成 war 文件可以支持 JSP。但 Spring Boot 默认使用嵌入式 Servlet 容器以 JAR 包方式进行项目打包部署,这种 JAR 包方式不支持 JSP。

  • 如果使用 Undertow 嵌入式容器部署 Spring Boot 项目,也不支持 JSP 模板。(Undertow  是红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器)

  • Spring Boot 默认提供了一个处理请求路径 “/error” 的统一错误处理器,返回具体的异常信息。使用 JSP 模板时,无法对默认的错误处理器进行覆盖,只能根据 Spring Boot 要求在指定位置定制错误页面。

Thymeleaf

Thymeleaf 是一种现代的基于服务器端的 Java 模板引擎技术,也是一个优秀的面向 Java 的 XML、XHTML、HTML5 页面模板,它具有丰富的标签语言、函数和表达式,在使用 Spring Boot 框架进行页面设计时,一般会选择 Thymeleaf 模板。

Thymeleaf 语法

在 HTML 页面上使用 Thymeleaf 标签,Thymeleaf 标签能够动态地替换掉静态内容,使页面动态展示。

为了更直观的认识 Thymeleaf,下面展示一个在 HTML 文件中嵌入了 Thymeleaf 的页面文件,示例代码如下:

html>

    
        
        
        Thymeleaftitle><br>    head><br>    <body><br>        <p th:text="${hello}">Hello Thymeleafp><br>    body><br>html><br></code></pre> <p>上述代码中,<code>“xmlns:th="http://www.thymeleaf.org"</code> 用于引入 Thymeleaf 模板引擎标签,使用关键字 <code>th</code> 标注标签是 Thymeleaf 模板提供的标签,其中,<code>th:href="@{/css/gtvg.css}"</code> 用于引入外联样式文件,<code>th:text="${hello}"</code> 用于动态显示标签文本内容。</p> <p>常用标签:</p> <ul> <li><p><code>th:insert</code> - 布局标签,替换内容到引入的文件</p></li> <li><p><code>th:replace</code> - 页面片段包含(类似 JSP 中的 include 标签)</p></li> <li><p><code>th:each</code> - 元素遍历(类似 JSP 中的 <code>c:forEach</code> 标签)</p></li> <li><p><code>th:if</code> - 条件判断,如果为真</p></li> <li><p><code>th:unless</code> - 条件判断,如果为假</p></li> <li><p><code>th:switch</code> - 条件判断,进行选择性匹配</p></li> <li><p><code>th:case</code> - 条件判断,进行选择性匹配</p></li> <li><p><code>th:value</code> - 属性值修改,指定标签属性值</p></li> <li><p><code>th:href</code> - 用于设定链接地址</p></li> <li><p><code>th:src</code> - 用于设定链接地址</p></li> <li><p><code>th:text</code> - 用于指定标签显示的文本内容</p></li> </ul> <p>标准表达式:</p> <ul> <li><p>变量表达式-  <code>${…}</code></p></li> <li><p>选择变量表达式 - <code>*{…}</code></p></li> <li><p>消息表达式 - <code>#{…}</code></p></li> <li><p>链接 URL 表达式 - <code>@{…}</code></p></li> <li><p>片段表达式 - <code>~{…}</code></p></li> </ul> <h6><span style="font-weight:bold;">变量表达式 `${…}`</span></h6> <p>变量表达式 <code>${...}</code> 主要用于获取上下文中的变量值,示例代码如下:</p> <pre class="has"><code><p th:text="${title}">这是标题p><br></code></pre> <p>示例使用了 Thymeleaf 模板的变量表达式 <code>${...}</code> 用来动态获取 P 标签中的内容,如果当前程序没有启动或者当前上下文中不存在 title 变量,该片段会显示标签默认值“这是标题”;如果当前上下文中存在 title 变量并且程序已经启动,当前 P 标签中的默认文本内容将会被 title 变量的值所替换,从而达到模板引擎页面数据动态替换的效果。</p> <p>同时,Thymeleaf 为变量所在域提供了一些内置对象,具体如下所示:</p> <pre class="has"><code>#ctx:上下文对象<br>#vars:上下文变量<br>#locale:上下文区域设置<br>#request:(仅限 Web Context)HttpServletRequest 对象<br>#response:(仅限 Web Context)HttpServletResponse 对象<br>#session:(仅限 Web Context)HttpSession 对象<br>#servletContext:(仅限 Web Context)ServletContext 对象<br></code></pre> <p>结合上述内置对象的说明,假设要在 Thymeleaf 模板引擎页面中动态获取当前国家信息,可以使用 <code>#locale</code> 内置对象,示例代码如下:</p> <pre class="has"><code>The locale country is: <span th:text="${#locale.country}">Chinaspan><br></code></pre> <p>上述代码中,使用 <code>th:text="${#locale.country}"</code> 动态获取当前用户所在国家信息,其中标签内默认内容为 China,程序启动后通过浏览器查看当前页面时,Thymeleaf 会通过浏览器语言设置来识别当前用户所在国家信息,从而实现动态替换。</p> <h6><span style="font-weight:bold;">选择变量表达式 `*{…}`</span></h6> <p>选择变量表达式和变量表达式用法类似,一般用于从被选定对象而不是上下文中获取属性值,如果没有选定对象,则和变量表达式一样,示例代码如下:</p> <pre class="has"><code><div th:object="${book}"><br>    <p>titile: <span th:text="*{title}">标题span>.p><br>div><br></code></pre> <p><code>*{title}</code> 选择变量表达式获取当前指定对象 book 的 title 属性值。</p> <h6><span style="font-weight:bold;">消息表达式 `#{…}`</span></h6> <p>消息表达式 <code>#{...}</code> 主要用于 Thymeleaf 模板页面国际化内容的动态替换和展示,使用消息表达式 <code>#{...}</code> 进行国际化设置时,还需要提供一些国际化配置文件。</p> <h6><span style="font-weight:bold;">链接表达式 `@{…}`</span></h6> <p>链接表达式 <code>@{...}</code> 一般用于页面跳转或者资源的引入,在 Web 开发中占据着非常重要的地位,并且使用也非常频繁,示例代码如下:</p> <pre class="has"><code><a th:href="@{http://localhost:8080/order/details(orderId=${o.id})}">viewa> <br><a th:href="@{/order/details(orderId=${o.id},pid=${p.id})}">viewa><br></code></pre> <p>上述代码中,链接表达式 <code>@{...}</code> 分别编写了绝对链接地址和相对链接地址。在有参表达式中,需要按照 <code>@{路径(参数名称=参数值,参数名称=参数值...)}</code> 的形式编写,同时该参数的值可以使用变量表达式来传递动态参数值。</p> <h6><span style="font-weight:bold;">片段表达式 `~{…}`</span></h6> <p>片段表达式 <code>~{...}</code> 用来标记一个片段模板,并根据需要移动或传递给其他模板。其中,最常见的用法是使用 <code>th:insert</code> 或 <code>th:replace</code> 属性插入片段,示例代码如下:</p> <pre class="has"><code><div th:insert="~{thymeleafDemo::title}">div><br></code></pre> <p>上述代码中,使用 <code>th:insert</code> 属性将 <code>title</code> 片段模板引用到该标签中。<code>thymeleafDemo</code> 为模板名称,Thymeleaf 会自动查找 <code>/resources/templates/</code> 目录下的 <code>thymeleafDemo</code> 模板,title 为片段名称。</p> <h5><span style="font-weight:bold;">基本使用</span></h5> <p>1) Thymeleaf 模板基本配置</p> <p>首先在 Springbootdemo2 项目中使用 Thymeleaf 模板,首先必须保证引入 Thymeleaf 依赖,示例代码如下:</p> <pre class="has"><code><dependency><br>    <groupId>org.springframework.bootgroupId><br>    <artifactId>spring-boot-starter-thymeleafartifactId><br>dependency><br></code></pre> <p>其次,在全局配置文件中配置 Thymeleaf 模板的一些参数。一般 Web 项目都会使用下列配置,示例代码如:</p> <pre class="has"><code>spring:<br>  thymeleaf:<br>    # 在开发阶段,为了验证建议关闭缓存<br>    cache: true<br>    # 模板编码<br>    encoding: UTF-8<br>    # 应用于模板的模板模式<br>    mode: HTML5<br>    # 指定模板页面存放路径<br>    prefix: classpath:/templates/<br>    # 指定模板页面名称的后缀<br>    suffix: .html<br></code></pre> <p>上述配置中,<code>spring.thymeleaf.cache</code> 表示是否开启 Thymeleaf 模板缓存,默认为 true,在开发过程中通常会关闭缓存,保证项目调试过程中数据能够及时响应;<code>spring.thymeleaf.prefix</code> 指定了 Thymeleaf 模板页面的存放路径,默认为<code>classpath:/templates/</code>;<code>spring.thymeleaf.suffix</code> 指定了 Thymeleaf 模板页面的名称后缀,默认为 <code>.html</code>。</p> <p>2)静态资源的访问</p> <p>开发 Web 应用时,难免需要使用静态资源。Spring boot 默认设置了静态资源的访问路径。</p> <p>使用 Spring Initializr 方式创建的 Spring Boot 项目,默认生成了一个 resources 目录,在 resources 目录中的 public、resources、static 三个子目录下,Spring boot 默认会挨个从public、resources、static 里面查找静态资源。</p> <h5><span style="font-weight:bold;">完成数据的页面展示</span></h5> <p>1)创建 Spring Boot 项目,引入 Thymeleaf 依赖。</p> <p>2)编写配置文件。</p> <p>编辑 application.yml 全局配置文件,在该文件中对 Thymeleaf 模板页面的数据缓存进行设置。</p> <pre class="has"><code># thymeleaf 页面缓存设置(默认为 true),开发中方便调试应设置为 false,上线稳定后应保持默认 true<br>spring:<br>  thymeleaf:<br>    cache: false<br></code></pre> <p>使用 <code>spring.thymeleaf.cache=false</code> 将 Thymeleaf 默认开启的缓存设置为了 false,用来关闭模板页面缓存。</p> <p>3)创建 web 控制类</p> <p>在项目中创建名为 <code>com.renda.controller</code> 的包,并在该包下创建一个用于前端模板页面动态数据替换效果测试的访问实体类 <code>LoginController</code>。</p> <pre class="has"><code>@Controller<br>public class LoginController { <br><br>    @RequestMapping("/toLogin")<br>    public String toLoginView(Model model){ <br>        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));<br>        return "login"; // resources/templates/login.html<br>    }<br><br>}<br></code></pre> <p><code>toLoginView()</code> 方法用于向登录页面 <code>login.html</code> 跳转,同时携带了当前年份信息 <code>currentYear</code>。</p> <p>4)创建模板页面并引入静态资源文件。</p> <p>在 <code>classpath:/templates/</code> 目录下引入一个用户登录的模板页面 <code>login.html</code>。</p> <pre class="has"><code>html><br><html lang="en" xmlns:th="http://www.thymeleaf.org"><br><head><br>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><br>    <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no"><br>    <title>用户登录界面title><br>    <link th:href="@{../static/css/bootstrap.min.css}" rel="stylesheet"><br>    <link th:href="@{../static/css/signin.css}" rel="stylesheet"><br>head><br><body class="text-center"><br><br><form class="form-signin"><br>    <img class="mb-4" th:src="@{../static/img/login.png}" width="72" height="72"><br>    <h1 class="h3 mb-3 font-weight-normal">请登录h1><br>    <input type="text" class="form-control"th:placeholder="用户名" required="" autofocus=""><br>    <input type="password" class="form-control"th:placeholder="密码" required=""><br>    <div class="checkbox mb-3"><br>        <label><br>            <input type="checkbox" value="remember-me"> 记住我<br>        label><br>    div><br>    <button class="btn btn-lg btn-primary btn-block" type="submit" >登录button><br>    <p class="mt-5 mb-3 text-muted">© <span th:text="${currentYear}">2019span>-<span th:text="${currentYear}+1">2020span>p><br>form><br>body><br>html><br></code></pre> <p>通过 <code>xmlns:th="http://www.thymeleaf.org</code> 引入了 Thymeleaf 模板标签;</p> <p>使用 <code>th:href</code> 和 <code>th:src</code> 分别引入了两个外联的样式文件和一个图片;</p> <p>使用 <code>th:text</code> 引入了后台动态传递过来的当前年份 <code>currentYear</code>。</p> <p>5)效果测试</p> <p>可以看出,登录页面 <code>login.html</code> 显示正常,在页面底部动态显示了当前日期 <code>2020-2021</code>,而不是文件中的静态数字 <code>2019-2020</code>。这进一步说明了 Spring Boot 与 Thymeleaf 整合成功,完成了静态资源的引入和动态数据的显示。</p> <h3><span style="font-weight:bold;">SpringBoot 实战演练</span></h3> <p>实战技能补充:lombok</p> <pre class="has"><code><dependency><br>    <groupId>org.projectlombokgroupId><br>    <artifactId>lombokartifactId><br>    <version>1.18.12version><br>    <br>    <scope>providedscope><br>dependency><br></code></pre> <p>需求:实现用户的 CRUD 功能</p> <p>初始化数据库信息:</p> <pre class="has"><code>DROP TABLE IF EXISTS `user`;<br>CREATE TABLE `user`<br>(<br>    id int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',<br>    username varchar(100) DEFAULT NULL COMMENT '用户名',<br>    password varchar(100) DEFAULT NULL COMMENT '密码',<br>    birthday varchar(100) DEFAULT NULL COMMENT '生日',<br>    PRIMARY KEY (id)<br>) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8;<br>INSERT INTO `user` VALUES (1, 'zhangsan', '123', '2020-10-1');<br>INSERT INTO `user` VALUES (2, 'lisi', '123', '2020-10-2');<br>INSERT INTO `user` VALUES (3, 'wangwu', '123', '2020-10-10');<br>INSERT INTO `user` VALUES (4, 'yuanjing', '123', '2020-10-11');<br></code></pre> <h4><span style="font-weight:bold;">1)创建 springboot 工程</span></h4> <p>使用 Spring Initializr 新建一个工程 <code>springbootuser</code>,选择依赖:Developer Tools -> Lombok,Web -> Spring Web,SQL -> [MyBatis Framework、MySQL Driver]。</p> <h4><span style="font-weight:bold;">2)编辑 pom.xml</span></h4> <pre class="has"><code><br><dependency><br>    <groupId>com.alibabagroupId><br>    <artifactId>druidartifactId><br>    <version>1.1.3version><br>dependency><br></code></pre> <h4><span style="font-weight:bold;">3)User 实体类编写</span></h4> <p>使用 FreeMyBatis 生成实体类。</p> <p>使用 FreeMyBatis 生成 UserMapper 相关的代码。</p> <p><code>com.renda.pojo.User</code></p> <pre class="has"><code>@Data // Lombok 自动生成 getter 和 setter<br>public class User implements Serializable { <br>    /**<br>     * 用户id<br>     */<br>    private Integer id;<br><br>    /**<br>     * 用户名<br>     */<br>    private String username;<br><br>    /**<br>     * 密码<br>     */<br>    private String password;<br><br>    /**<br>     * 生日<br>     */<br>    private String birthday;<br><br>    private static final long serialVersionUID = 1L;<br>}<br></code></pre> <h4><span style="font-weight:bold;">4)UserMapper 编写及 xml 文件</span></h4> <p><code>com.renda.mapper.UserMapper</code></p> <pre class="has"><code>public interface UserMapper { <br>    int deleteByPrimaryKey(Integer id);<br><br>    int insert(User record);<br><br>    int insertSelective(User record);<br><br>    User selectByPrimaryKey(Integer id);<br><br>    int updateByPrimaryKeySelective(User record);<br><br>    int updateByPrimaryKey(User record);<br>}<br></code></pre> <p><code>src\main\resources\mapper\UserMapper.xml</code></p> <pre class="has"><code><?xml  version="1.0" encoding="UTF-8"?><br>mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><br><mapper namespace="com.renda.mapper.UserMapper"><br>  <resultMap id="BaseResultMap" type="com.renda.pojo.User"><br>    <id column="id" jdbcType="INTEGER" property="id" /><br>    <result column="username" jdbcType="VARCHAR" property="username" /><br>    <result column="password" jdbcType="VARCHAR" property="password" /><br>    <result column="birthday" jdbcType="VARCHAR" property="birthday" /><br>  resultMap><br>  <sql id="Base_Column_List"><br>    id, username, `password`, birthday<br>  sql><br>  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"><br>    select <br>    <include refid="Base_Column_List" /><br>    from user<br>    where id = #{id,jdbcType=INTEGER}<br>  select><br>  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"><br>    delete from user<br>    where id = #{id,jdbcType=INTEGER}<br>  delete><br>  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true"><br>    insert into user (username, `password`, birthday<br>      )<br>    values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{birthday,jdbcType=VARCHAR}<br>      )<br>  insert><br>  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true"><br>    insert into user<br>    <trim prefix="(" suffix=")" suffixOverrides=","><br>      <if test="username != null"><br>        username,<br>      if><br>      <if test="password != null"><br>        `password`,<br>      if><br>      <if test="birthday != null"><br>        birthday,<br>      if><br>    trim><br>    <trim prefix="values (" suffix=")" suffixOverrides=","><br>      <if test="username != null"><br>        #{username,jdbcType=VARCHAR},<br>      if><br>      <if test="password != null"><br>        #{password,jdbcType=VARCHAR},<br>      if><br>      <if test="birthday != null"><br>        #{birthday,jdbcType=VARCHAR},<br>      if><br>    trim><br>  insert><br>  <update id="updateByPrimaryKeySelective" parameterType="com.renda.pojo.User"><br>    update user<br>    <set><br>      <if test="username != null"><br>        username = #{username,jdbcType=VARCHAR},<br>      if><br>      <if test="password != null"><br>        `password` = #{password,jdbcType=VARCHAR},<br>      if><br>      <if test="birthday != null"><br>        birthday = #{birthday,jdbcType=VARCHAR},<br>      if><br>    set><br>    where id = #{id,jdbcType=INTEGER}<br>  update><br>  <update id="updateByPrimaryKey" parameterType="com.renda.pojo.User"><br>    update user<br>    set username = #{username,jdbcType=VARCHAR},<br>      `password` = #{password,jdbcType=VARCHAR},<br>      birthday = #{birthday,jdbcType=VARCHAR}<br>    where id = #{id,jdbcType=INTEGER}<br>  update><br>mapper><br></code></pre> <h4><span style="font-weight:bold;">5)UserService 接口及实现类编写</span></h4> <p><code>com.renda.service.UserService</code></p> <pre class="has"><code>public interface UserService { <br><br>    /**<br>     * 查询所有<br>     */<br>    List queryAll();<br><br>    /**<br>     * 通过 ID 查询<br>     */<br>    User findById(Integer id);<br><br>    /**<br>     * 新增<br>     */<br>    void insert(User user);<br><br>    /**<br>     * 通过 ID 删除<br>     */<br>    void deleteById(Integer id);<br><br>    /**<br>     * 修改<br>     */<br>    void update(User user);<br><br>}<br></code></pre> <p><code>com.renda.service.impl.UserServiceImpl</code></p> <pre class="has"><code>@Service<br>public class UserServiceImpl implements UserService { <br><br>    @Autowired<br>    private UserMapper userMapper;<br><br>    @Override<br>    public List queryAll() { <br>        return userMapper.queryAll();<br>    }<br><br>    @Override<br>    public User findById(Integer id) { <br>        return userMapper.selectByPrimaryKey(id);<br>    }<br><br>    @Override<br>    public void insert(User user) { <br>        // 将除 id 外所有的列都拼接入 SQL 语句<br>        // userMapper.insert(user);<br>        // 只将不为空的列才拼接入 SQL 语句(优先使用,减少高并发下数据传输)<br>        userMapper.insertSelective(user);<br>    }<br><br>    @Override<br>    public void deleteById(Integer id) { <br>        userMapper.deleteByPrimaryKey(id);<br>    }<br><br>    @Override<br>    public void update(User user) { <br>        userMapper.updateByPrimaryKeySelective(user);<br>    }<br><br>}<br></code></pre> <h4><span style="font-weight:bold;">6)UserController 编写</span></h4> <p><code>com.renda.controller.UserController</code></p> <pre class="has"><code>/**<br> * restful 格式进行访问<br> * 查询:GET<br> * 新增: POST<br> * 更新:PUT<br> * 删除: DELETE<br> *<br> * @author Renda Zhang<br> * @since 2020-10-31 1:36<br> */<br>@RestController<br>@RequestMapping("/user")<br>public class UserController { <br><br>    @Autowired<br>    private UserService userService;<br><br>    /**<br>     * 查询所有<br>     */<br>    @GetMapping("/query")<br>    public List queryAll(){ <br>        return userService.queryAll();<br>    }<br><br>    /**<br>     * 通过 ID 查询<br>     */<br>    @GetMapping("/query/{id}")<br>    public User queryById(@PathVariable Integer id){ <br>        return userService.findById(id);<br>    }<br><br>    /**<br>     * 删除<br>     */<br>    @DeleteMapping("/delete/{id}")<br>    public String delete(@PathVariable Integer id){ <br>        userService.deleteById(id);<br>        return "删除成功";<br>    }<br><br>    /**<br>     * 新增<br>     */<br>    @PostMapping("/insert")<br>    public String insert(User user){ <br>        userService.insert(user);<br>        return "新增成功";<br>    }<br><br>    /**<br>     * 修改<br>     */<br>    @PutMapping("/update")<br>    public String update(User user){ <br>        userService.update(user);<br>        return "修改成功";<br>    }<br><br>}<br></code></pre> <h4><span style="font-weight:bold;">7)全局配置文件 application.yml</span></h4> <p>重命名 <code>application.properties</code> 为 <code>application.yml</code></p> <p><code>src\main\resources\application.yml</code></p> <pre class="has"><code># 服务器配置<br>server:<br>  port: 8090<br><br>spring:<br>  # 数据源配置<br>  datasource:<br>    name: druid<br>    type: com.alibaba.druid.pool.DruidDataSource<br>    url: jdbc:mysql://localhost:3306/springbootdata?characterEncoding=utf-8&serverTimezone=UTC<br>    username: root<br>    password: password<br><br># 整合 MyBatis<br>mybatis:<br>  # 声明 MyBatis 文件所在的位置<br>  mapper-locations: classpath:mapper/*Mapper.xml<br></code></pre> <h4><span style="font-weight:bold;">8)启动类</span></h4> <p><code>com.renda.SpringbootuserApplication</code></p> <pre class="has"><code>@SpringBootApplication<br>// 使用的 Mybatis, 扫描 com.renda.mapper<br>@MapperScan("com.renda.mapper")<br>public class SpringbootuserApplication { <br><br>    public static void main(String[] args) { <br>        SpringApplication.run(SpringbootuserApplication.class, args);<br>    }<br><br>}<br></code></pre> <h4><span style="font-weight:bold;">10)使用 Postman 测试</span></h4> <ul> <li><p>GET <code>http://localhost:8090/user/query</code></p></li> <li><p>GET <code>http://localhost:8090/user/query/1</code></p></li> <li><p>POST <code>http://localhost:8090/user/insert?username=renda&password=123456&birthday=1995-12-27</code></p></li> <li><p>PUT <code>http://localhost:8090/user/update?username=RendaZhang&password=00000&birthday=1997-12-27&id=5</code></p></li> <li><p>DELETE <code>http://localhost:8090/user/delete/5</code></p></li> </ul> <h3><span style="font-weight:bold;">Spring Boot 项目部署</span></h3> <p>需求:将 Spring Boot 项目使用 maven 指令打成 jar 包并运行测试。</p> <h4><span style="font-weight:bold;">分析</span></h4> <p>1)添加打包组件将项目中的资源、配置、依赖包打到一个 jar 包中;可以使用 maven 的 package 命令。</p> <p>2)部署:<code>java -jar 包名</code></p> <h4><span style="font-weight:bold;">步骤实现</span></h4> <p>确保 pom.xml 文件中有如下的打包组件:</p> <pre class="has"><code><build><br>    <plugins><br>        <br>        <plugin><br>            <groupId>org.springframework.bootgroupId><br>            <artifactId>spring-boot-maven-pluginartifactId><br>        plugin><br>    plugins><br>build><br></code></pre> <p>部署运行:</p> <pre class="has"><code>java -jar springbootuser-0.0.1-SNAPSHOT.jar<br></code></pre> </div> </div> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1384698068938608640"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(springboot,pom文件指定jdk)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1903018111292993536.htm" title="使用Python轻松拆分PDF,每页独立成文件" target="_blank">使用Python轻松拆分PDF,每页独立成文件</a> <span class="text-muted">AI航海家(Ethan)</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/pdf/1.htm">pdf</a> <div>使用Python轻松拆分PDF,每页独立成文件嗨,各位PDF爱好者!如果你曾经有想要拆分一个大PDF文件的想法,让每一页都成为独立的文件,那么这篇博客就是为你准备的!我们将使用Python中的一个非常强大的库–PyPDF2,把这些需求变得简单易行。PyPDF2登场首先,我们需要安装PyPDF2库。如果你还没有安装,别担心,只需要在终端运行以下命令:pipinstallPyPDF2安装好了吗?下面我</div> </li> <li><a href="/article/1903010923061243904.htm" title="Python Textract库:文本提取" target="_blank">Python Textract库:文本提取</a> <span class="text-muted">程序员喵哥</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>更多Python学习内容:ipengtao.comTextract是一个强大的Python库,用于从各种文件格式中提取文本。无论是PDF、Word文档、Excel电子表格、HTML页面还是图像,Textract都能有效地提取其中的文本内容。Textract通过集成多种开源工具和库,实现了对多种文件格式的支持,使得文本提取变得简单而高效。本文将详细介绍Textract库的安装、主要功能、基本操作、高</div> </li> <li><a href="/article/1903006763213451264.htm" title="springboot 整合swagger2" target="_blank">springboot 整合swagger2</a> <span class="text-muted">兮川</span> <a class="tag" taget="_blank" href="/search/SpringBoot/1.htm">SpringBoot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/springboot/1.htm">springboot</a><a class="tag" taget="_blank" href="/search/swagger2/1.htm">swagger2</a> <div>相信很多人都用过postman,使用postman其实可以很简便的进行接口调试,但是呢,每次还要写url,以及要添加参数名字(很容易写错)。所以啊,swagger2优势就体现出来了,它只需要添加少量注解即可在项目下调试接口,并且可以根据项目是否是测试还是生产环境,可以显示或禁止页面接口调试,介绍就到这里,开始写整合部分。一.maven添加依赖此处使用的是2.7.0版本,下面的ui二选一即可,spr</div> </li> <li><a href="/article/1903006636931346432.htm" title="计算机二级c语言知识点6" target="_blank">计算机二级c语言知识点6</a> <span class="text-muted">xu_hhh_</span> <a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%BA%8C%E7%BA%A7c%E8%AF%AD%E8%A8%80%E9%80%89%E6%8B%A9%E9%A2%98/1.htm">计算机二级c语言选择题</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>函数形参的值,不会改变对应实参的值函数可以返回地址值&x不可以给指针变量赋一个整数作为地址值当在程序的开头包含头文件stdio.h时,可以给指针变量赋NULLfun(char*a,char*b){while((*b=*a)!=‘\0’){a++;b++;}}这个函数实现的功能是将a所指的字符串赋给b所指的空间,此函数也会将\0赋给b,因为括号里的表达式(*b=*a)先执行,后判断是否=\0若有定义</div> </li> <li><a href="/article/1903005627584671744.htm" title="Centos7_安装" target="_blank">Centos7_安装</a> <span class="text-muted">爱喝兽奶</span> <a class="tag" taget="_blank" href="/search/Linux%E5%9F%BA%E7%A1%80/1.htm">Linux基础</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a> <div>一.Linux哲学思想一切都是一个文件(包括硬件)小型,单一用途的程序链接程序,共同完成复杂的任务避免令人困惑的用户界面配置数据存储在文本中二.Linux生产主流版本Linux各种版本CentOS各版本介绍https://zh.wikipedia.org/wiki/CentOSRHEL各版本介绍https://zh.wikipedia.org/wiki/Red_Hat_Enterprise_Lin</div> </li> <li><a href="/article/1903004090133180416.htm" title="对数据库的总结" target="_blank">对数据库的总结</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>一、数据库基础1.数据库是一个用于存储和操作数据的文件系统2.关系型数据库:是基于二维表存储的,每个表格由列和行组成,列代表属性,行代表约束,数据的组织和查询更加方便和高效。3.库表操作结构:MySQL和Oracle,通用工具Navicat4.SQL语句的库表操作:createtable:创建表altertable:修改表droptable:删除表truncatetable:删除表中的所有数据,但</div> </li> <li><a href="/article/1903002067534934016.htm" title="ios如何像android那样不上架安装" target="_blank">ios如何像android那样不上架安装</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/ios%E4%B8%8A%E6%9E%B6/1.htm">ios上架</a> <div>大家都知道像鸿蒙、安卓的安装是比较简单的,可以直接在android或华为手机上安装,不一定通过在线商店安装。但是ios手机不一样,假如你是使用普通苹果账号打包的appstore类型的ipa包,是无法直接安装在ios手机的,必须通过上架才能安装。不过,ios想直接安装,还是有通道的,就是注册苹果企业开发者账号,通过企业开发者账号,生成inhouse类型的证书和描述文件(证书profile文件),通过</div> </li> <li><a href="/article/1903001716853370880.htm" title="Springboot整合Swagger+mybatis-plus" target="_blank">Springboot整合Swagger+mybatis-plus</a> <span class="text-muted">cccy001</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/mybatis/1.htm">mybatis</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>目录1.springboot整合swagger接口文档1.1什么是swagger21.2为什么需要使用swagger2的api文档1.3如何使用swagger22.springboot整合mybatis-plus2.1mp的简介2.2如何使用1.springboot整合swagger接口文档1.1什么是swagger2它就是可以api接口,它可以对你书写的接口进行说明。并以文档的形式存在。、1.2</div> </li> <li><a href="/article/1903001718484955136.htm" title="SpringBoot整合Swagger和Mybatis-Plus" target="_blank">SpringBoot整合Swagger和Mybatis-Plus</a> <span class="text-muted">_拾柒_</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/mybatis/1.htm">mybatis</a> <div>一、Swagger(一)、Swagger简介Swagger是一种用于描述、设计、构建和使用RESTful风格的Web服务的工具集和规范。它提供了一种标准的方式来定义API,并生成交互式文档,使得开发人员、测试人员和其他利益相关者可以快速了解和使用API。(二)、Swagger特点自动生成文档:基于API定义,Swagger可以自动生成交互式文档,其中包含了API的详细说明、请求示例、响应示例等信息</div> </li> <li><a href="/article/1903001087128956928.htm" title="Java使用FFmpegFrameGrabber进行视频拆帧,结合Thumbnails压缩图片保存到文件夹" target="_blank">Java使用FFmpegFrameGrabber进行视频拆帧,结合Thumbnails压缩图片保存到文件夹</a> <span class="text-muted">Acmen-zym</span> <a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/IO/1.htm">IO</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>引入依赖net.coobirdthumbnailator0.4.17org.bytedecojavacv1.5.7org.bytedecojavacv-platform1.5.7视频拆帧处理publicstaticvoidmain(String[]args){Filefile=newFile("C:\\Users\\EDY\\Desktop\\frame");File[]files=file.li</div> </li> <li><a href="/article/1903000203154223104.htm" title="SpringBoot集成Swagger3.0" target="_blank">SpringBoot集成Swagger3.0</a> <span class="text-muted">邪神大叔</span> <a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/Mac/1.htm">Mac</a><a class="tag" taget="_blank" href="/search/SpringBoot/1.htm">SpringBoot</a><a class="tag" taget="_blank" href="/search/swagger3.0/1.htm">swagger3.0</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/springboot/1.htm">springboot</a> <div>pom.xmlio.springfoxspringfox-boot-starter3.0.0SwaggerConfig/***@author:zek*@desc:swagger*/@EnableOpenApi@ConfigurationpublicclassSwaggerConfigimplementsWebMvcConfigurer{@BeanpublicDocketcreateRestApi(</div> </li> <li><a href="/article/1902998815984644096.htm" title="docker compose部署dragonfly" target="_blank">docker compose部署dragonfly</a> <span class="text-muted">java初学者分享</span> <a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a><a class="tag" taget="_blank" href="/search/%E5%AE%B9%E5%99%A8/1.htm">容器</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a> <div>整个工具的代码都在Gitee或者Github地址内gitee:solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodbgithub:GitHub-ZeroNing/solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、rei</div> </li> <li><a href="/article/1902998563961499648.htm" title="SpringBoot整合通用xxl-job,自动注册任务" target="_blank">SpringBoot整合通用xxl-job,自动注册任务</a> <span class="text-muted">java初学者分享</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>整个工具的代码都在Gitee或者Github地址内gitee:solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodbgithub:GitHub-ZeroNing/solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、rei</div> </li> <li><a href="/article/1902998436681150464.htm" title="SpringBoot整合阿里云、腾讯云、minio、百度云、华为云、天翼云、金山云、七牛云、移动云、网易数帆等等有关于S3协议下文分布式对象存储接口" target="_blank">SpringBoot整合阿里云、腾讯云、minio、百度云、华为云、天翼云、金山云、七牛云、移动云、网易数帆等等有关于S3协议下文分布式对象存储接口</a> <span class="text-muted">java初学者分享</span> <a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E4%BA%91/1.htm">阿里云</a><a class="tag" taget="_blank" href="/search/%E8%85%BE%E8%AE%AF%E4%BA%91/1.htm">腾讯云</a><a class="tag" taget="_blank" href="/search/%E5%8D%8E%E4%B8%BA%E4%BA%91/1.htm">华为云</a> <div>前提:在可运行的SpringBoot的项目内引用以下JAR包整个工具的代码都在Gitee或者Github地址内gitee:solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodbgithub:GitHub-ZeroNing/solomon-parent:这个项目主要是总结了工作上遇到</div> </li> <li><a href="/article/1902998437507428352.htm" title="SpringBoot整合多租户MongoBD" target="_blank">SpringBoot整合多租户MongoBD</a> <span class="text-muted">java初学者分享</span> <a class="tag" taget="_blank" href="/search/SaaS%E5%A4%9A%E7%A7%9F%E6%88%B7%E4%B8%93%E6%A0%8F/1.htm">SaaS多租户专栏</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a> <div>前提:在可运行的SpringBoot的项目内引用以下JAR包整个工具的代码都在Gitee或者Github地址内gitee:solomon-parent:这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodbgithub:GitHub-ZeroNing/solomon-parent:这个项目主要是总结了工作上遇到</div> </li> <li><a href="/article/1902995664602722304.htm" title="docker-compose笔记" target="_blank">docker-compose笔记</a> <span class="text-muted">Re_Virtual</span> <a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a><a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/%E5%AE%B9%E5%99%A8/1.htm">容器</a> <div>docker目前docker官网已经无法登录,但是还可以从清华镜像站(https://mirrors.tuna.tsinghua.edu.cn/docker-ce/)下载。使用方法可以参考早期文章《docker笔记》docker-compose可以从Github下载不同版本的二进制文件,例如docker-compose-linux-x86_64。下载完成后,将二进制文件复制入路径,例如/usr/l</div> </li> <li><a href="/article/1902995537775357952.htm" title="springboot基于java的企业档案管理信息系统" target="_blank">springboot基于java的企业档案管理信息系统</a> <span class="text-muted">QQ80213251</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>收藏关注不迷路!!文末获取源码+数据库感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题),项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人文章目录前言详细视频演示一、项目介绍二、功能介绍三、核心代码数据库参考四、效果图五、文章目录六、源码获取前言企业档案管理信息系统是一种旨在提高文件资料归档、检索和利用效率的信息化解决方案。该系统通过电子化手段对企业的各类文档和档案进行归</div> </li> <li><a href="/article/1902994025263198208.htm" title="2. 猜数字游戏" target="_blank">2. 猜数字游戏</a> <span class="text-muted">老实人y</span> <a class="tag" taget="_blank" href="/search/Rust%E5%B0%8F%E7%99%BD%E5%88%9D%E5%AD%A6/1.htm">Rust小白初学</a><a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>目录创建一个新项目处理一次猜测生成一个秘密数字使用crate来增加更多功能Cargo.lock文件确保构建是可重现的更新crate到一个新版本生成一个随机数比较猜测的数字和神秘数字使用循环来允许多次猜测猜测正确后退出处理无效输入最终代码程序会随机生成一个1到100之间的整数。接着它会提示玩家猜一个数并输入,然后指出猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。创建一个新项目project</div> </li> <li><a href="/article/1902982171946184704.htm" title="FastStone Image Viewer v7 注册码" target="_blank">FastStone Image Viewer v7 注册码</a> <span class="text-muted">mediapub</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/%E7%94%B5%E8%84%91/1.htm">电脑</a> <div>FastStoneImageViewer是一款快速、小巧、功能强大的综合图像浏览软件。它提供使用者方便的操作界面,让使用者可以通过它的操作界面来浏览图片,且还支持了幻灯播放的功能,让使用者能够轻松的浏览目录中的所有图片。该版本已内置注册码,可以使用全部功能。操作说明:1、将压缩文件解压到固定位置,不要随意移动。2、解压后,双击start_FSViewer.bat来运行软件下载地址:https://</div> </li> <li><a href="/article/1902978516962570240.htm" title="文件夹加密原理 [转]" target="_blank">文件夹加密原理 [转]</a> <span class="text-muted">chengg0769</span> <a class="tag" taget="_blank" href="/search/%E5%8A%A0%E5%AF%86/1.htm">加密</a><a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/%E8%A7%A3%E5%AF%86/1.htm">解密</a><a class="tag" taget="_blank" href="/search/dreamweaver/1.htm">dreamweaver</a><a class="tag" taget="_blank" href="/search/dos/1.htm">dos</a><a class="tag" taget="_blank" href="/search/microsoft/1.htm">microsoft</a> <div>谈文件夹的加密一、文件夹加密的方式有几种?在Windows平台下,文件夹加密的方式归纳起来有两种:一种是简单地对文件夹进行各种方式的隐藏,甚至利用Windows的漏洞进行隐藏,这种软件根本就没有对数据进行任何加密处理,所以才宣称“加密速度极快,上百G的数据仅需1秒钟完成。没有大小限制。”但是,这种文件夹加密方式的加密效果极其脆弱,只能防范一些电脑菜鸟偷看你的资料。因为软件编写者利用了Windows</div> </li> <li><a href="/article/1902976625499238400.htm" title="为什么阿里Java规范不建议使用@Autowired" target="_blank">为什么阿里Java规范不建议使用@Autowired</a> <span class="text-muted">AWen_X</span> <a class="tag" taget="_blank" href="/search/%E8%A8%80%E7%AE%80%E6%84%8F%E8%B5%85%E7%B3%BB%E5%88%97%E4%B9%8BSpring/1.htm">言简意赅系列之Spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>Spring中@Autowired和@Resource的区别1.基本区别特性@Autowired@Resource来源Spring框架提供JSR-250规范提供,Java标准装配顺序优先按类型装配优先按名称装配默认匹配规则默认按类型匹配,可以使用@Qualifier指定名称默认按名称匹配,如果无法匹配则按类型匹配属性required属性可以设置是否必须注入成功name属性可以显式指定bean名称适</div> </li> <li><a href="/article/1902971201618243584.htm" title="Docker 存储" target="_blank">Docker 存储</a> <span class="text-muted">Psycho_MrZhang</span> <a class="tag" taget="_blank" href="/search/Docker/1.htm">Docker</a><a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%AE%B9%E5%99%A8/1.htm">容器</a> <div>目录挂载在执行run时设置参数-v即可实现目录映射,实现原理会在宿主机器创建一个空文件夹#挂载宿主机的/data目录到容器的/app目录dockerrun-d-v/data:/app--namemy-appmy-image#挂载docker内的/usr/share/nginx/html目录到本地机的/app/nghtmldockerrun-d-v/app/nghtml:/usr/share/ngi</div> </li> <li><a href="/article/1902970694585610240.htm" title="git clone 指定目录" target="_blank">git clone 指定目录</a> <span class="text-muted">波格斯特</span> <a class="tag" taget="_blank" href="/search/%E9%97%AE%E9%A2%98%E5%A4%87%E5%BF%98/1.htm">问题备忘</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a> <div>GitClone指定目录详解Git是一个强大的版本控制系统,它允许用户克隆远程仓库到本地,同时可以指定克隆的目录。在Git中,gitclone命令是用来复制远程仓库到本地的常用命令。默认情况下,gitclone会克隆整个仓库,但有时我们可能只需要仓库的一部分或者特定的文件或目录。这时,我们可以通过一些参数来指定克隆的目标目录。基本用法要克隆一个仓库到一个特定的目录,可以使用如下命令:gitclon</div> </li> <li><a href="/article/1902968172810661888.htm" title="python+flask计算机毕业设计基于Android平台的景区移动端旅游软件系统(程序+开题+论文)" target="_blank">python+flask计算机毕业设计基于Android平台的景区移动端旅游软件系统(程序+开题+论文)</a> <span class="text-muted">Node.js彤彤 程序</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/flask/1.htm">flask</a><a class="tag" taget="_blank" href="/search/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/1.htm">课程设计</a> <div>本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着移动互联网技术的飞速发展,智能手机已成为人们日常生活中不可或缺的一部分,特别是在旅游领域,移动端应用以其便捷性、实时性和个性化服务的特点,极大地改变了人们的旅游体验方式。当前,旅游市场日益繁荣,游客对于旅游信息获取、行程规划、景点导航、票务预订及个性化服务的需</div> </li> <li><a href="/article/1902966911939964928.htm" title="一键掌控海量文件!Shell的find命令终极指南 + 高阶组合技" target="_blank">一键掌控海量文件!Shell的find命令终极指南 + 高阶组合技</a> <span class="text-muted">芯有所享</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/1.htm">经验分享</a> <div>你是否经历过这些崩溃瞬间?想清理3个月前的日志却无从下手要在10万张图片里找出某个版本突然发现服务器被临时文件塞爆…今天介绍的Linux三剑客之find命令,就是你的超级救星!不仅能精准定位文件,结合其他命令更能玩出自动化运维的花样!一、Find基础三连击(新手必看)按图索骥-名称搜索查找当前目录所有.txt文件(精准匹配)find.-name“*.txt”忽略大小写找配置文件(模糊匹配)find</div> </li> <li><a href="/article/1902964894295191552.htm" title="数据结构之单链表(C语言)" target="_blank">数据结构之单链表(C语言)</a> <span class="text-muted">渴望脱下狼皮的羊</span> <a class="tag" taget="_blank" href="/search/%E5%88%9D%E9%98%B6%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%AD%A6%E4%B9%A0%EF%BC%88C%E8%AF%AD%E8%A8%80%E5%AE%9E%E7%8E%B0%EF%BC%89/1.htm">初阶数据结构学习(C语言实现)</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>数据结构之单链表(C语言)1链表的概念2节点创建函数与链表打印函数2.1节点创建函数2.2链表打印函数3单链表尾插法与头插法3.1尾插函数3.2头插函数4单链表尾删法与头删法4.1尾删函数4.2头删函数5指定位置的插入与删除5.1在指定位置之前插入数据5.2在指定位置之后插入数据5.3删除指定位置节点5.4删除指定位置之后节点6链表数据的查找与链表的销毁6.1链表数据的查找6.2链表的销毁7单链表</div> </li> <li><a href="/article/1902961744603574272.htm" title="Centos7软件包管理(rpm、yum)" target="_blank">Centos7软件包管理(rpm、yum)</a> <span class="text-muted">Bulut0907</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a><a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E5%8C%85%E7%AE%A1%E7%90%86/1.htm">软件包管理</a><a class="tag" taget="_blank" href="/search/rpm/1.htm">rpm</a><a class="tag" taget="_blank" href="/search/yum/1.htm">yum</a><a class="tag" taget="_blank" href="/search/yum%E6%BA%90%E4%BF%AE%E6%94%B9/1.htm">yum源修改</a> <div>目录1.rpm2.yum2.1修改yum源1.rpmRPM(RedHatPackageManager),redhat系列操作系统里面的打包安装工具查询命令:查询安装的所有rpm软件包:rpm-qa查询指定rpm软件包,并显示详细信息:rpm-qipython3卸载命令:卸载软件包,不管是否有其它软件包依赖该软件包:rpm-e--nodeps软件包名称安装命令:安装rpm包,并显示详细信息和进度条(</div> </li> <li><a href="/article/1902961741793390592.htm" title="常用的数据结构有哪些?在Go语言中如何定义其实例?" target="_blank">常用的数据结构有哪些?在Go语言中如何定义其实例?</a> <span class="text-muted">开心码农1号</span> <a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">算法与数据结构</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/go/1.htm">go</a><a class="tag" taget="_blank" href="/search/%E9%93%BE%E8%A1%A8/1.htm">链表</a> <div>常见的数据结构有:数组、链表、栈、队列、图、哈希表;1、数组用于存储和处理一组固定大小、相同类型的数据,如存储学生成绩、数组排序等。Go语言中的数组长度是固定的,在声明时需要指定长度。特点:数据元素类型相同:数组中的所有元素都具有相同的数据类型;内存地址连续:数组在内存中是连续存储的;随机访问高效:由于数组的内存地址连续,并且元素类型相同,因此可以通过索引快速访问数组中的任意元素。无论要访问数组中</div> </li> <li><a href="/article/1902961489757663232.htm" title="Qt 自定义标题栏——QtWidgets" target="_blank">Qt 自定义标题栏——QtWidgets</a> <span class="text-muted">十年之少</span> <a class="tag" taget="_blank" href="/search/Qt/1.htm">Qt</a><a class="tag" taget="_blank" href="/search/Widgets/1.htm">Widgets</a><a class="tag" taget="_blank" href="/search/Qt/1.htm">Qt</a><a class="tag" taget="_blank" href="/search/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%87%E9%A2%98%E6%A0%8F/1.htm">自定义标题栏</a> <div>1.去掉标题栏,实现窗体拖动(常用)以上转载方法实现窗体拖动是通过Windows事件的;但是有时候为了跨平台这种的话就得重写,比较麻烦,所以还是采用最原始的方法:通过QWidgets的鼠标事件来实现。.h文件classQRCodeDialog:publicQDialog{Q_OBJECT...protected:voidmouseMoveEvent(QMouseEvent*event)overri</div> </li> <li><a href="/article/1902956566387159040.htm" title="taosdump备份多个数据库近10天的数据" target="_blank">taosdump备份多个数据库近10天的数据</a> <span class="text-muted">会飞的土拨鼠呀</span> <a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">运维学习笔记</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a> <div>以下是使用taosdump备份多个指定数据库近10天数据的详细步骤:方法1:直接指定多个数据库名称通过--databases参数直接指定多个数据库,逗号分隔:#!/bin/bash#定义备份目录和时间范围BACKUP_DIR="./backup"START_TIME=$(date-d"10daysago""+%Y-%m-%d%H:%M:%S")END_TIME=$(date"+%Y-%m-%d%H</div> </li> <li><a href="/article/78.htm" title="桌面上有多个球在同时运动,怎么实现球之间不交叉,即碰撞?" target="_blank">桌面上有多个球在同时运动,怎么实现球之间不交叉,即碰撞?</a> <span class="text-muted">换个号韩国红果果</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%90%83%E7%A2%B0%E6%92%9E/1.htm">小球碰撞</a> <div>稍微想了一下,然后解决了很多bug,最后终于把它实现了。其实原理很简单。在每改变一个小球的x y坐标后,遍历整个在dom树中的其他小球,看一下它们与当前小球的距离是否小于球半径的两倍?若小于说明下一次绘制该小球(设为a)前要把他的方向变为原来相反方向(与a要碰撞的小球设为b),即假如当前小球的距离小于球半径的两倍的话,马上改变当前小球方向。那么下一次绘制也是先绘制b,再绘制a,由于a的方向已经改变</div> </li> <li><a href="/article/205.htm" title="《高性能HTML5》读后整理的Web性能优化内容" target="_blank">《高性能HTML5》读后整理的Web性能优化内容</a> <span class="text-muted">白糖_</span> <a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a> <div> 读后感         先说说《高性能HTML5》这本书的读后感吧,个人觉得这本书前两章跟书的标题完全搭不上关系,或者说只能算是讲解了“高性能”这三个字,HTML5完全不见踪影。个人觉得作者应该首先把HTML5的大菜拿出来讲一讲,再去分析性能优化的内容,这样才会有吸引力。因为只是在线试读,没有机会看后面的内容,所以不胡乱评价了。   </div> </li> <li><a href="/article/332.htm" title="[JShop]Spring MVC的RequestContextHolder使用误区" target="_blank">[JShop]Spring MVC的RequestContextHolder使用误区</a> <span class="text-muted">dinguangx</span> <a class="tag" taget="_blank" href="/search/jeeshop/1.htm">jeeshop</a><a class="tag" taget="_blank" href="/search/%E5%95%86%E5%9F%8E%E7%B3%BB%E7%BB%9F/1.htm">商城系统</a><a class="tag" taget="_blank" href="/search/jshop/1.htm">jshop</a><a class="tag" taget="_blank" href="/search/%E7%94%B5%E5%95%86%E7%B3%BB%E7%BB%9F/1.htm">电商系统</a> <div>    在spring mvc中,为了随时都能取到当前请求的request对象,可以通过RequestContextHolder的静态方法getRequestAttributes()获取Request相关的变量,如request, response等。         在jshop中,对RequestContextHolder的</div> </li> <li><a href="/article/459.htm" title="算法之时间复杂度" target="_blank">算法之时间复杂度</a> <span class="text-muted">周凡杨</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/1.htm">时间复杂度</a><a class="tag" taget="_blank" href="/search/%E6%95%88%E7%8E%87/1.htm">效率</a> <div>      在 计算机科学 中, 算法 的时间复杂度是一个 函数 ,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的 字符串 的长度的函数。时间复杂度常用 大O符号 表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是 渐近 的,它考察当输入值大小趋近无穷时的情况。 这样用大写O()来体现算法时间复杂度的记法,</div> </li> <li><a href="/article/586.htm" title="Java事务处理" target="_blank">Java事务处理</a> <span class="text-muted">g21121</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>一、什么是Java事务 通常的观念认为,事务仅与数据库相关。 事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩写。事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。一致性表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状</div> </li> <li><a href="/article/713.htm" title="Linux awk命令详解" target="_blank">Linux awk命令详解</a> <span class="text-muted">510888780</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>一.  AWK 说明   awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。    awk的处理文本和数据的方式:它逐行扫描文件,从第一行到</div> </li> <li><a href="/article/840.htm" title="android permission" target="_blank">android permission</a> <span class="text-muted">布衣凌宇</span> <a class="tag" taget="_blank" href="/search/Permission/1.htm">Permission</a> <div><uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES" ></uses-permission>允许读写访问"properties"表在checkin数据库中,改值可以修改上传 <uses-permission android:na</div> </li> <li><a href="/article/967.htm" title="Oracle和谷歌Java Android官司将推迟" target="_blank">Oracle和谷歌Java Android官司将推迟</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a> <div>北京时间 10 月 7 日,据国外媒体报道,Oracle 和谷歌之间一场等待已久的官司可能会推迟至 10 月 17 日以后进行,这场官司的内容是 Android 操作系统所谓的 Java 专利权之争。本案法官 William Alsup 称根据专利权专家 Florian Mueller 的预测,谷歌 Oracle 案很可能会被推迟。  该案中的第二波辩护被安排在 10 月 17 日出庭,从目前看来</div> </li> <li><a href="/article/1094.htm" title="linux shell 常用命令" target="_blank">linux shell 常用命令</a> <span class="text-muted">antlove</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a><a class="tag" taget="_blank" href="/search/command/1.htm">command</a> <div>grep [options] [regex] [files] /var/root # grep -n "o" * hello.c:1:/* This C source can be compiled with: </div> </li> <li><a href="/article/1221.htm" title="Java解析XML配置数据库连接(DOM技术连接 SAX技术连接)" target="_blank">Java解析XML配置数据库连接(DOM技术连接 SAX技术连接)</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/sax%E6%8A%80%E6%9C%AF/1.htm">sax技术</a><a class="tag" taget="_blank" href="/search/Java%E8%A7%A3%E6%9E%90xml%E6%96%87%E6%A1%A3/1.htm">Java解析xml文档</a><a class="tag" taget="_blank" href="/search/dom%E6%8A%80%E6%9C%AF/1.htm">dom技术</a><a class="tag" taget="_blank" href="/search/XML%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5/1.htm">XML配置数据库连接</a> <div>    XML配置数据库文件的连接其实是个很简单的问题,为什么到现在才写出来主要是昨天在网上看了别人写的,然后一直陷入其中,最后发现不能自拔 所以今天决定自己完成 ,,,,现将代码与思路贴出来供大家一起学习   XML配置数据库的连接主要技术点的博客; JDBC编程 : JDBC连接数据库 DOM解析XML:  DOM解析XML文件 SA</div> </li> <li><a href="/article/1348.htm" title="underscore.js 学习(二)" target="_blank">underscore.js 学习(二)</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/underscore/1.htm">underscore</a> <div>        Array Functions 所有数组函数对参数对象一样适用。1.first   _.first(array, [n])   别名: head, take       返回array的第一个元素,设置了参数n,就</div> </li> <li><a href="/article/1475.htm" title="plSql介绍" target="_blank">plSql介绍</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/plsql/1.htm">plsql</a> <div>/* * PL/SQL 程序设计学习笔记 * 学习plSql介绍.pdf * 时间:2010-10-05 */ --创建DEPT表 create table DEPT ( DEPTNO NUMBER(10), DNAME NVARCHAR2(255), LOC NVARCHAR2(255) ) delete dept; select </div> </li> <li><a href="/article/1602.htm" title="【Nginx一】Nginx安装与总体介绍" target="_blank">【Nginx一】Nginx安装与总体介绍</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a> <div>启动、停止、重新加载Nginx nginx 启动Nginx服务器,不需要任何参数u nginx -s stop 快速(强制)关系Nginx服务器 nginx -s quit 优雅的关闭Nginx服务器 nginx -s reload 重新加载Nginx服务器的配置文件 nginx -s reopen 重新打开Nginx日志文件   </div> </li> <li><a href="/article/1729.htm" title="spring mvc开发中浏览器兼容的奇怪问题" target="_blank">spring mvc开发中浏览器兼容的奇怪问题</a> <span class="text-muted">bitray</span> <a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/springMVC/1.htm">springMVC</a><a class="tag" taget="_blank" href="/search/%E6%B5%8F%E8%A7%88%E5%99%A8/1.htm">浏览器</a><a class="tag" taget="_blank" href="/search/%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6/1.htm">上传文件</a> <div>    最近个人开发一个小的OA项目,属于复习阶段.使用的技术主要是spring mvc作为前端框架,mybatis作为数据库持久化技术.前台使用jquery和一些jquery的插件.     在开发到中间阶段时候发现自己好像忽略了一个小问题,整个项目一直在firefox下测试,没有在IE下测试,不确定是否会出现兼容问题.由于jquer</div> </li> <li><a href="/article/1856.htm" title="Lua的io库函数列表" target="_blank">Lua的io库函数列表</a> <span class="text-muted">ronin47</span> <a class="tag" taget="_blank" href="/search/lua+io/1.htm">lua io</a> <div>1、io表调用方式:使用io表,io.open将返回指定文件的描述,并且所有的操作将围绕这个文件描述   io表同样提供三种预定义的文件描述io.stdin,io.stdout,io.stderr   2、文件句柄直接调用方式,即使用file:XXX()函数方式进行操作,其中file为io.open()返回的文件句柄   多数I/O函数调用失败时返回nil加错误信息,有些函数成功时返回nil</div> </li> <li><a href="/article/1983.htm" title="java-26-左旋转字符串" target="_blank">java-26-左旋转字符串</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> public class LeftRotateString { /** * Q 26 左旋转字符串 * 题目:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。 * 如把字符串abcdef左旋转2位得到字符串cdefab。 * 请实现字符串左旋转的函数。要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。 */ pu</div> </li> <li><a href="/article/2110.htm" title="《vi中的替换艺术》-linux命令五分钟系列之十一" target="_blank">《vi中的替换艺术》-linux命令五分钟系列之十一</a> <span class="text-muted">cfyme</span> <a class="tag" taget="_blank" href="/search/linux%E5%91%BD%E4%BB%A4/1.htm">linux命令</a> <div>vi方面的内容不知道分类到哪里好,就放到《Linux命令五分钟系列》里吧! 今天编程,关于栈的一个小例子,其间我需要把”S.”替换为”S->”(替换不包括双引号)。 其实这个不难,不过我觉得应该总结一下vi里的替换技术了,以备以后查阅。   1 所有替换方案都要在冒号“:”状态下书写。 2 如果想将abc替换为xyz,那么就这样 :s/abc/xyz/ 不过要特别</div> </li> <li><a href="/article/2237.htm" title="[轨道与计算]新的并行计算架构" target="_blank">[轨道与计算]新的并行计算架构</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E5%B9%B6%E8%A1%8C%E8%AE%A1%E7%AE%97/1.htm">并行计算</a> <div>      我在进行流程引擎循环反馈试验的过程中,发现一个有趣的事情。。。如果我们在流程图的每个节点中嵌入一个双向循环代码段,而整个流程中又充满着很多并行路由,每个并行路由中又包含着一些并行节点,那么当整个流程图开始循环反馈过程的时候,这个流程图的运行过程是否变成一个并行计算的架构呢?      </div> </li> <li><a href="/article/2364.htm" title="重复执行某段代码" target="_blank">重复执行某段代码</a> <span class="text-muted">dai_lm</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>用handler就可以了 private Handler handler = new Handler(); private Runnable runnable = new Runnable() { public void run() { update(); handler.postDelayed(this, 5000); } }; 开始计时 h</div> </li> <li><a href="/article/2491.htm" title="Java实现堆栈(list实现)" target="_blank">Java实现堆栈(list实现)</a> <span class="text-muted">datageek</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E2%80%94%E2%80%94%E5%A0%86%E6%A0%88/1.htm">数据结构——堆栈</a> <div>public interface IStack<T> { //元素出栈,并返回出栈元素 public T pop(); //元素入栈 public void push(T element); //获取栈顶元素 public T peek(); //判断栈是否为空 public boolean isEmpty</div> </li> <li><a href="/article/2618.htm" title="四大备份MySql数据库方法及可能遇到的问题" target="_blank">四大备份MySql数据库方法及可能遇到的问题</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/DB/1.htm">DB</a><a class="tag" taget="_blank" href="/search/backup/1.htm">backup</a> <div>一:通过备份王等软件进行备份前台进不去? 用备份王等软件进行备份是大多老站长的选择,这种方法方便快捷,只要上传备份软件到空间一步步操作就可以,但是许多刚接触备份王软件的客用户来说还原后会出现一个问题:因为新老空间数据库用户名和密码不统一,网站文件打包过来后因没有修改连接文件,还原数据库是好了,可是前台会提示数据库连接错误,网站从而出现打不开的情况。 解决方法:学会修改网站配置文件,大多是由co</div> </li> <li><a href="/article/2745.htm" title="github做webhooks:[1]钩子触发是否成功测试" target="_blank">github做webhooks:[1]钩子触发是否成功测试</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/github/1.htm">github</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/webhook/1.htm">webhook</a> <div>转自: http://jingyan.baidu.com/article/5d6edee228c88899ebdeec47.html github和svn一样有钩子的功能,而且更加强大。例如我做的是最常见的push操作触发的钩子操作,则每次更新之后的钩子操作记录都会在github的控制板可以看到! 工具/原料 github 方法/步骤 </div> </li> <li><a href="/article/2872.htm" title="JSP中<base href="<%=basePath%>">的作用" target="_blank">JSP中<base href="<%=basePath%>">的作用</a> <span class="text-muted">蕃薯耀</span> <div>JSP中<base href="<%=basePath%>">的作用 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> </div> </li> <li><a href="/article/2999.htm" title="linux下SAMBA服务安装与配置" target="_blank">linux下SAMBA服务安装与配置</a> <span class="text-muted">hanqunfeng</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>局域网使用的文件共享服务。 一.安装包: rpm -qa | grep samba samba-3.6.9-151.el6.x86_64 samba-common-3.6.9-151.el6.x86_64 samba-winbind-3.6.9-151.el6.x86_64 samba-client-3.6.9-151.el6.x86_64 samba-winbind-clients</div> </li> <li><a href="/article/3126.htm" title="guava cache" target="_blank">guava cache</a> <span class="text-muted">IXHONG</span> <a class="tag" taget="_blank" href="/search/cache/1.htm">cache</a> <div>缓存,在我们日常开发中是必不可少的一种解决性能问题的方法。简单的说,cache 就是为了提升系统性能而开辟的一块内存空间。   缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日常开发的很多场合,由于受限于硬盘IO的性能或者我们自身业务系统的数据处理和获取可能非常费时,当我们发现我们的系统这个数据请求量很大的时候,频繁的IO和频繁的逻辑处理会导致硬盘和CPU资源的</div> </li> <li><a href="/article/3253.htm" title="Query的开始--全局变量,noconflict和兼容各种js的初始化方法" target="_blank">Query的开始--全局变量,noconflict和兼容各种js的初始化方法</a> <span class="text-muted">kvhur</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a> <div>这个是整个jQuery代码的开始,里面包含了对不同环境的js进行的处理,例如普通环境,Nodejs,和requiredJs的处理方法。 还有jQuery生成$, jQuery全局变量的代码和noConflict代码详解  完整资源: http://www.gbtags.com/gb/share/5640.htm jQuery 源码:   (</div> </li> <li><a href="/article/3380.htm" title="美国人的福利和中国人的储蓄" target="_blank">美国人的福利和中国人的储蓄</a> <span class="text-muted">nannan408</span> <div>   今天看了篇文章,震动很大,说的是美国的福利。    美国医院的无偿入院真的是个好措施。小小的改善,对于社会是大大的信心。小孩,税费等,政府不收反补,真的体现了人文主义。    美国这么高的社会保障会不会使人变懒?答案是否定的。正因为政府解决了后顾之忧,人们才得以倾尽精力去做一些有创造力,更造福社会的事情,这竟成了美国社会思想、人</div> </li> <li><a href="/article/3507.htm" title="N阶行列式计算(JAVA)" target="_blank">N阶行列式计算(JAVA)</a> <span class="text-muted">qiuwanchi</span> <a class="tag" taget="_blank" href="/search/N%E9%98%B6%E8%A1%8C%E5%88%97%E5%BC%8F%E8%AE%A1%E7%AE%97/1.htm">N阶行列式计算</a> <div>package gaodai; import java.util.List; /** * N阶行列式计算 * @author 邱万迟 * */ public class DeterminantCalculation { public DeterminantCalculation(List<List<Double>> determina</div> </li> <li><a href="/article/3634.htm" title="C语言算法之打渔晒网问题" target="_blank">C语言算法之打渔晒网问题</a> <span class="text-muted">qiufeihu</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>如果一个渔夫从2011年1月1日开始每三天打一次渔,两天晒一次网,编程实现当输入2011年1月1日以后任意一天,输出该渔夫是在打渔还是在晒网。 代码如下:   #include <stdio.h> int leap(int a) /*自定义函数leap()用来指定输入的年份是否为闰年*/ { if((a%4 == 0 && a%100 != 0</div> </li> <li><a href="/article/3761.htm" title="XML中DOCTYPE字段的解析" target="_blank">XML中DOCTYPE字段的解析</a> <span class="text-muted">wyzuomumu</span> <a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a> <div>DTD声明始终以!DOCTYPE开头,空一格后跟着文档根元素的名称,如果是内部DTD,则再空一格出现[],在中括号中是文档类型定义的内容. 而对于外部DTD,则又分为私有DTD与公共DTD,私有DTD使用SYSTEM表示,接着是外部DTD的URL. 而公共DTD则使用PUBLIC,接着是DTD公共名称,接着是DTD的URL.   私有DTD <!DOCTYPErootSYST</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>