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/1896046392963559424.htm" title="Docker运行nginx镜像并挂载目录到宿主机(宿主机即运行docker的这台电脑或服务器)" target="_blank">Docker运行nginx镜像并挂载目录到宿主机(宿主机即运行docker的这台电脑或服务器)</a> <span class="text-muted">qifengle2014</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</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/Python%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB%E5%90%88%E9%9B%86/1.htm">Python技术分享合集</a><a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>Docker运行nginx镜像并挂载目录到宿主机(宿主机即运行docker的这台电脑或服务器)1、拉取nginx镜像dockerpullnginx2、查看是否拉取成功dockerimages3、创建文件夹用于运行的时候挂载mkdir-p/usr/local/nginx/confmkdir-p/usr/local/nginx/logsmkdir-p</div> </li> <li><a href="/article/1896045257791959040.htm" title="Springboot项目中线程池使用整理" target="_blank">Springboot项目中线程池使用整理</a> <span class="text-muted">m0_74823715</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</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> <div>文章目录Springboot项目中线程池使用整理学习目标线程池类型及特点Java基础线程池SpringThreadPoolTaskExecutorThreadPoolTaskExecutorvsThreadPoolExecutor主要区别@Async注解使用AsyncConfigurer接口CompletableFuture线程池实现方式比较1.Spring@Bean方式2.AsyncConfig</div> </li> <li><a href="/article/1896045003906543616.htm" title="大白话解释静态资源 阿里云OSS是什么 有什么用 怎么用" target="_blank">大白话解释静态资源 阿里云OSS是什么 有什么用 怎么用</a> <span class="text-muted">心心祥蓉</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/%E4%BA%91%E8%AE%A1%E7%AE%97/1.htm">云计算</a> <div>阿里云OSS是什么?阿里云OSS(对象存储服务)就像一个“云上的巨型仓库”,专门帮你存各种文件,比如图片、视频、文档、日志等。它不像电脑硬盘那样需要自己买设备,而是直接通过网络存取,按实际用量付费。你可以把它想象成一个“无限容量的网盘”,但更安全、更稳定,还能和企业级应用无缝对接。OSS有什么用?1.存海量文件场景:电商网站存商品图片和视频(比如淘宝的百万级商品图);短视频平台存用户上传的视频;企</div> </li> <li><a href="/article/1896042857945427968.htm" title="spring boot面试笔记" target="_blank">spring boot面试笔记</a> <span class="text-muted">表面矿工</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">面试题</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/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a> <div>starterSpringBoot、SpringMVC和Spring有什么区别?Spring和SpringMVC:1、Spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案;2、springMvc是spring基础之上的一个MVC框架</div> </li> <li><a href="/article/1896041976101400576.htm" title="META-INF 文件夹用途" target="_blank">META-INF 文件夹用途</a> <span class="text-muted">杏花春雨江南</span> <a class="tag" taget="_blank" href="/search/java%E5%9F%BA%E7%A1%80/1.htm">java基础</a><a class="tag" taget="_blank" href="/search/pycharm/1.htm">pycharm</a><a class="tag" taget="_blank" href="/search/ide/1.htm">ide</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>META-INF文件夹是Java应用程序和库中一个特殊的目录,通常用于存放元数据(Metadata)和配置文件。它是Java标准的一部分,Java虚拟机和相关工具会识别并处理该目录中的特定文件。以下是META-INF文件夹的常用使用场景:1.存放Manifest文件(MANIFEST.MF)作用:MANIFEST.MF是JavaJAR文件的元数据文件,用于描述JAR文件的内容和属性。常用场景:指定</div> </li> <li><a href="/article/1896041723293921280.htm" title="Android Studio中,设置页面布局有几种方式" target="_blank">Android Studio中,设置页面布局有几种方式</a> <span class="text-muted">杏花春雨江南</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/studio/1.htm">studio</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/ide/1.htm">ide</a> <div>在AndroidStudio中,设置页面布局主要有以下几种方式:1.使用XML布局文件这是最常见的方式,通过编写XML文件来定义界面布局。XML文件位于res/layout目录下。你可以在XML中使用各种布局控件(如LinearLayout、RelativeLayout、ConstraintLayout等)来组织界面元素。2.使用ConstraintLayoutConstraintLayout是一</div> </li> <li><a href="/article/1896041597028593664.htm" title="最新版studio android 项目中 没有 activity_main.xml 文件,怎么回事" target="_blank">最新版studio android 项目中 没有 activity_main.xml 文件,怎么回事</a> <span class="text-muted">杏花春雨江南</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a> <div>因为下载的androidstudio是最新版本,新建项目形式都为新的(kotlin版本),但是在搜索相关文章时,很多文章都是基于(xml声明配置或java相关的),所以在摸索期间遇到很多问题。也在逐个遇见逐个击破。以上。在AndroidStudio项目中,activity_main.xml是默认创建的一个Layout文件,通常作为应用程序的主活动布局。如果你的项目中没有这个文件,可能有以下几种原因</div> </li> <li><a href="/article/1896035167881981952.htm" title="编程入门难?Python 十大核心知识点,手把手教你!" target="_blank">编程入门难?Python 十大核心知识点,手把手教你!</a> <span class="text-muted">编程咕咕gu-</span> <a class="tag" taget="_blank" href="/search/Python%E5%9F%BA%E7%A1%80/1.htm">Python基础</a><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><a class="tag" taget="_blank" href="/search/Python%E6%95%99%E7%A8%8B/1.htm">Python教程</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/1.htm">网络爬虫</a> <div>如果你对编程感兴趣,但又不知道从哪里开始,那么Python会是你的好选择。它简单易学,是初学者学习编程的好帮手。这篇文章会用轻松的方式,带你了解Python的十个重要概念,一步步帮助你从零开始!1.Python文件与扩展名学习Python的第一步是创建一个Python文件。Python文件的名字后面都会加上.py作为扩展名。例如,你可以创建一个名为main.py的文件,这就是一个Python脚本。</div> </li> <li><a href="/article/1896034160477270016.htm" title="十、Spring Boot:Spring Security(用户认证与授权深度解析)" target="_blank">十、Spring Boot:Spring Security(用户认证与授权深度解析)</a> <span class="text-muted">伯牙碎琴</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Spring%E6%A1%86%E6%9E%B6/1.htm">Spring框架</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/java/1.htm">java</a> <div>SpringSecurity:用户认证与授权深度解析在现代Web应用中,安全性是至关重要的。SpringSecurity作为Spring生态系统中的核心安全框架,提供了强大的用户认证和授权功能,能够有效保护应用免受各种安全威胁。本文将深入探讨SpringSecurity的用户认证和授权机制,包括其核心概念、实现方式以及在SpringBoot项目中的集成方法。一、SpringSecurity概述1.</div> </li> <li><a href="/article/1896032270179954688.htm" title="网络安全清单" target="_blank">网络安全清单</a> <span class="text-muted">网络安全Max</span> <a class="tag" taget="_blank" href="/search/web%E5%AE%89%E5%85%A8/1.htm">web安全</a><a class="tag" taget="_blank" href="/search/%E6%99%BA%E8%83%BD%E8%B7%AF%E7%94%B1%E5%99%A8/1.htm">智能路由器</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a> <div>点击文末小卡片,免费获取网络安全全套资料,资料在手,涨薪更快移除(Deprovisioning)移除(Deprovisioning)是一个除去现存用户帐户的过程,其包括用户帐户登记和设备失效。分布式编码规则(DER:DistinguishedEncodingRules)分布式编码规则(DER)是一个基本编码规则的子集,其提供一个严密的方法来描述任何ASN.1值来作为一个8位字节的字符串。指定网桥(</div> </li> <li><a href="/article/1896029243532439552.htm" title="前端架构 —— 脚手架的开发流程" target="_blank">前端架构 —— 脚手架的开发流程</a> <span class="text-muted">mask-li</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>一、脚手架开发流程详解开发流程创建npm项目创建脚手架入口文件,最上方添加:#!/usr/bin/envnode配置package.json,添加bin属性编写脚手架代码将脚手架发布到npm使用流程安装脚手架npminstall-gyour-own-cli使用脚手架your-own-cli二、脚手架开发难点解析分包:将复杂的系统拆分成若干个模块命令注册:vuecreatevueaddvueinvo</div> </li> <li><a href="/article/1896028864522547200.htm" title="材料仿真软件:VASP_(6).VASP输入文件详解:KPOINTS" target="_blank">材料仿真软件:VASP_(6).VASP输入文件详解:KPOINTS</a> <span class="text-muted">kkchenjj</span> <a class="tag" taget="_blank" href="/search/%E5%88%86%E5%AD%90%E5%8A%A8%E5%8A%9B%E5%AD%A6%E4%BB%BF%E7%9C%9F/1.htm">分子动力学仿真</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">性能优化</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%AD%90%E5%8A%A8%E5%8A%9B%E5%AD%A6/1.htm">分子动力学</a><a class="tag" taget="_blank" href="/search/%E4%BB%BF%E7%9C%9F%E6%A8%A1%E6%8B%9F/1.htm">仿真模拟</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%8B%9F%E4%BB%BF%E7%9C%9F/1.htm">模拟仿真</a> <div>VASP输入文件详解:KPOINTS在材料仿真软件中,KPOINTS文件是一个非常重要的输入文件,用于定义布里渊区的k点网格。k点的选择直接影响计算的精度和计算时间。在VASP中,KPOINTS文件的格式和内容决定了k点的分布和数量,从而影响电子结构计算的收敛性和准确性。k点的基本概念在固体物理学中,布里渊区是倒格子空间中的一个基本区域,用于描述晶体的电子状态。k点是布里渊区中的点,用于进行电子结</div> </li> <li><a href="/article/1896027603450523648.htm" title="ODX(Open Diagnostic Data Exchange)简介" target="_blank">ODX(Open Diagnostic Data Exchange)简介</a> <span class="text-muted">aFakeProgramer</span> <a class="tag" taget="_blank" href="/search/AP/1.htm">AP</a><a class="tag" taget="_blank" href="/search/AUTOSAR/1.htm">AUTOSAR</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/ODX/1.htm">ODX</a> <div>ODX(OpenDiagnosticDataExchange)是一种由ASAM制定的开放标准,用于描述和交换ECU(电子控制单元)诊断数据,广泛应用于车辆诊断。ODX文件采用XML格式,包含通讯参数,如ISO15765-2/3时间参数。ASAM(AssociationforStandardisationofAutomationandMeasuringSystems)ODX文件的结构ODX文件的结构</div> </li> <li><a href="/article/1896025709957148672.htm" title="SpringBoot整合easy-es" target="_blank">SpringBoot整合easy-es</a> <span class="text-muted">rkmhr_sef</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</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/elasticsearch/1.htm">elasticsearch</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>一、easy-es简介EasyES是一款基于Elasticsearch官方提供的RestHighLevelClient开发的ORM框架,旨在简化开发流程并提高效率。EasyES在保持RestHighLevelClient原有功能的基础上进行增强,而不做任何改变。它采用与Mybatis-Plus相似的语法,使得开发者可以无缝迁移至EasyES,无需额外学习成本。EasyES的核心理念是将简单、易用性</div> </li> <li><a href="/article/1896024950045732864.htm" title="Zookeeper(81)Zookeeper的日志清理机制是什么?" target="_blank">Zookeeper(81)Zookeeper的日志清理机制是什么?</a> <span class="text-muted">辞暮尔尔-烟火年年</span> <a class="tag" taget="_blank" href="/search/%E5%BE%AE%E6%9C%8D%E5%8A%A1/1.htm">微服务</a><a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a><a class="tag" taget="_blank" href="/search/debian/1.htm">debian</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a> <div>Zookeeper的日志清理机制主要是为了防止事务日志和快照文件占用过多磁盘空间。Zookeeper提供了自动清理机制,可以定期删除旧的事务日志和快照文件。以下是详细的介绍和代码示例,展示如何配置和管理Zookeeper的日志清理机制。自动清理机制配置Zookeeper的自动清理机制通过autopurge参数进行配置,主要包括以下两个参数:autopurge.snapRetainCount:保留的</div> </li> <li><a href="/article/1896024194454450176.htm" title="学习dify第二天-web下篇" target="_blank">学习dify第二天-web下篇</a> <span class="text-muted">一直走下去-明</span> <a class="tag" taget="_blank" href="/search/next.js/1.htm">next.js</a><a class="tag" taget="_blank" href="/search/dify%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/1.htm">dify源码学习</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a> <div>学习dify第二天-web下篇引言web目录结构配置入口文件分析下怎么封装的请求最后总结:参考阅读:React基础用next.js写个页面跳转的应用seo搜索引擎优化引言react:ReactFoundations:AboutReactandNext.js|Next.js如果不会next.js可以先看看这个,不看其实也没关系,学下思路也可以。next_web小demoweb目录结构*mocks*:</div> </li> <li><a href="/article/1896023941865074688.htm" title="在nodejs中使用ElasticSearch(一)安装,使用" target="_blank">在nodejs中使用ElasticSearch(一)安装,使用</a> <span class="text-muted">konglong127</span> <a class="tag" taget="_blank" href="/search/nodejs/1.htm">nodejs</a><a class="tag" taget="_blank" href="/search/elasticsearch/1.htm">elasticsearch</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a> <div>使用docker安装ElasticSearch和Kibana1)创建相应的data文件夹和子文件夹用来持久化ElasticSearch和kibana数据2)提前创建好elasticsearch配置文件data/elasticsearch/config/elasticsearch.yml文件#========================ElasticsearchConfiguration====</div> </li> <li><a href="/article/1896023815608135680.htm" title="在nodejs中使用ElasticSearch(二)核心概念,应用" target="_blank">在nodejs中使用ElasticSearch(二)核心概念,应用</a> <span class="text-muted">konglong127</span> <a class="tag" taget="_blank" href="/search/nodejs/1.htm">nodejs</a><a class="tag" taget="_blank" href="/search/elasticsearch/1.htm">elasticsearch</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a> <div>核心概念1、索引(index)索引在Elasticsearch7之后,相当于关系型数据库中的表,之前相当于关系型数据库中的数据库。#创建索引PUT/my_index#获取指定索引GET/my_index#获取所有索引GET/_cat/indices?v#模糊匹配索引POST*index*/_search#删除索引DELETE/my_index#创建索引,同时指定索引结构#number_of_sha</div> </li> <li><a href="/article/1896022808249561088.htm" title="Spring Boot 经典九设计模式全览" target="_blank">Spring Boot 经典九设计模式全览</a> <span class="text-muted">m0_74824552</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</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/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>在SpringBoot中,设计模式的应用广泛且重要,它们有助于提高代码的可维护性、可扩展性和复用性。以下是SpringBoot中经典的9种设计模式及其代码案例:1.单例模式(SingletonPattern)在Spring中,bean默认就是单例模式。Spring通过单例注册表的方式来实现单例,即维护一个Map来存储单例类的实例。//单例模式示例publicclassSingletonServic</div> </li> <li><a href="/article/1896018644857581568.htm" title="JDK活化石复活:setStream()抢救指南,看完想给Applet开追悼会" target="_blank">JDK活化石复活:setStream()抢救指南,看完想给Applet开追悼会</a> <span class="text-muted">筱涵哥</span> <a class="tag" taget="_blank" href="/search/Java%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8/1.htm">Java基础入门</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>一、时空错乱现场:当我试图用Applet传2024年的数据1.1来自侏罗纪的SOS"把这个2003年的数据采集Applet改造成能对接新系统!"——看着要传输的JSON数据,我仿佛听到硬盘在哀嚎:"臣妾做不到啊!"1.2现代程序员的降维打击//试图传输JSON数据时try{InputStreamjsonStream=newByteArrayInputStream("{\"data\":1}".ge</div> </li> <li><a href="/article/1896015361099231232.htm" title="网络编程中的数据传输函数" target="_blank">网络编程中的数据传输函数</a> <span class="text-muted">C嘎嘎嵌入式开发</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a> <div>虽然send、recv、sendmsg和recvmsg都是用于在网络编程中进行数据传输的函数。但是它们各自的功能和应用场景有所不同send和recvsend功能:send用于将数据发送到连接的套接字。原型ssize_tsend(intsockfd,constvoid*buf,size_tlen,intflags);参数sockfd:套接字文件描述符。buf:指向要发送的数据的缓冲区。len:要发送</div> </li> <li><a href="/article/1896011702110056448.htm" title="Linux 程序员必修课:命令、脚本、程序、进程与线程的全貌" target="_blank">Linux 程序员必修课:命令、脚本、程序、进程与线程的全貌</a> <span class="text-muted">内核程序员kevin</span> <a class="tag" taget="_blank" href="/search/Linux%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">Linux程序员</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%BF%9B%E7%A8%8B/1.htm">进程</a><a class="tag" taget="_blank" href="/search/%E8%84%9A%E6%9C%AC/1.htm">脚本</a><a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B/1.htm">线程</a> <div>Linux程序员必修课:命令、脚本、程序、进程与线程的全貌1.Linux命令、脚本、程序、进程与线程的关系对比表概念定义存储形式执行方式是否需要进程是否支持并发示例命令用户输入给shell的指令,可以是内置命令或外部命令直接输入在shell中(无固定存储)由shell解析并执行仅外部命令需要取决于命令,如&可后台运行ls、cd、echo脚本一组命令的集合,存储在文件中,通常是shell脚本文本文件</div> </li> <li><a href="/article/1896011450003025920.htm" title="VS code 之 c++远程开发环境搭建" target="_blank">VS code 之 c++远程开发环境搭建</a> <span class="text-muted">汝何秀</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/vscode/1.htm">vscode</a> <div>文章目录VScode之c/c++远程开发环境搭建1.基本环境2.远程主机的开发环境搭建3.本地vscode的配置配置ssh选项4远程开发教程VScode之c/c++远程开发环境搭建1.基本环境本地主机:VisualStudioCode远程主机:CentOS72.远程主机的开发环境搭建安装环境的压缩包:dev_env_install.tar.gz解压文件夹:repo自动安装脚本为:dev_env.s</div> </li> <li><a href="/article/1896011072146567168.htm" title="如何解析配置文件" target="_blank">如何解析配置文件</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/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a> <div>如何解析配置文件什么是配置文件?在计算机科学领域,配置文件(英语:configurationfile)是一种计算机文件,可以为一些计算机程序配置参数和初始设置。一个项目中,为什么要设置一个配置文件呢?方便修改参数设置和逻辑代码分离(解耦)(高内聚,低耦合)常见的配置文件格式:aaa.inixxx.xmlxxxxx.jsonxxfada.yaml什么是json文本、字符串、有固定的格式,格式长得像p</div> </li> <li><a href="/article/1896008802210213888.htm" title="mysql8.0.12安装_mysql 8.0.12 安装配置图文教程" target="_blank">mysql8.0.12安装_mysql 8.0.12 安装配置图文教程</a> <span class="text-muted">梦醒马亡</span> <div>记录了mysql8.0.12下载安装教程,分享给大家。下载如图下载以后将安装包解压到任意文件夹,我这里解压到E盘。安装1、解压以后有E:\mysql\mysql-8.0.12-winx64,里面建立一个空文件夹data,如果已经有这个文件夹就不用进行这一步2、建立一个my.ini文件,用记事本打开,复制以下代码进去[mysqld]#设置3306端口port=3306#设置mysql的安装目录bas</div> </li> <li><a href="/article/1896008297903878144.htm" title="springboot中配置logback-spring.xml" target="_blank">springboot中配置logback-spring.xml</a> <span class="text-muted">web13688565871</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/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/logback/1.htm">logback</a> <div>一、在src/main/resources目录下,也就是在classpath路径下创建logback-spring.xml注:springboot框架自动配置,如果更换名称,可在配置文件指定该文件即可-->%d{yyyy-MM-ddHH:mm:ss}[%thread]%-5level%logger{80}-%msg%n${log_dir}/info.logINFOACCEPTDENY${log_d</div> </li> <li><a href="/article/1896003507652194304.htm" title="SpringBoot项目中替换指定版本的tomcat" target="_blank">SpringBoot项目中替换指定版本的tomcat</a> <span class="text-muted">m0_54804970</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</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/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>在SpringBoot项目中替换指定版本的Tomcat,可以通过修改项目的`pom.xml`文件来实现。具体步骤如下:首先,查看当前SpringBoot项目中使用的Tomcat版本。可以通过查看`pom.xml`文件中的依赖项来确定。在`pom.xml`文件中,找到SpringBoot的依赖项,并添加或修改Tomcat的版本属性。例如,如果要将Tomcat版本修改为7.0.59,可以按照以下步骤操</div> </li> <li><a href="/article/1896002245967802368.htm" title="Tomcat 8.0.12 资源下载" target="_blank">Tomcat 8.0.12 资源下载</a> <span class="text-muted">洪岭瑶</span> <div>Tomcat8.0.12资源下载【下载地址】Tomcat8.0.12资源下载本仓库提供Tomcat8.0.12版本的资源文件下载。Tomcat是一个开源的JavaServlet容器,广泛用于部署和运行JavaWeb应用程序项目地址:https://gitcode.com/open-source-toolkit/dde6e简介本仓库提供Tomcat8.0.12版本的资源文件下载。Tomcat是一个开</div> </li> <li><a href="/article/1895999975951757312.htm" title="利用Python实现企业微信发送文件消息" target="_blank">利用Python实现企业微信发送文件消息</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/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1/1.htm">企业微信</a><a class="tag" taget="_blank" href="/search/%E8%87%AA%E5%8A%A8%E5%8C%96/1.htm">自动化</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E4%BB%B6/1.htm">文件</a><a class="tag" taget="_blank" href="/search/OA/1.htm">OA</a> <div>最近领导希望做一个小的招标信息爬取系统,每日以邮件以及企业微信的方式进行推送。所以记录一下如何使用Python实现企业微信发送文件。第一步:登录企业微信后台,需要用有管理员权限的微信进行扫码登录。第二步:获取AgentId和Secret,获取路径:应用管理-创建应用,创建应用后即可看到。第三步:确认需要接收信息的userid,获取路径:通讯录-选择需要发送的人员-账号。第四步:获取公司ID,获取路</div> </li> <li><a href="/article/1895997705138794496.htm" title="‌Tomcat 8.0.12安装流程" target="_blank">‌Tomcat 8.0.12安装流程</a> <span class="text-muted">小魚資源大雜燴</span> <a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>需要确保系统已经安装了JavaDevelopmentKit(JDK)7或更高版本。可以通过以下命令检查Java是否安装以及版本信息:shjava-version如果未安装Java,需要先下载并安装适合你操作系统的JDK。Windows系统安装流程步骤1:下载Tomcat8.0.12打开浏览器,访问ApacheTomcat的存档页面:https://archive.apache.org/dist/t</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>