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/1835507626276909056.htm" title="水平垂直居中的几种方法(总结)" target="_blank">水平垂直居中的几种方法(总结)</a> <span class="text-muted">LJ小番茄</span> <a class="tag" taget="_blank" href="/search/CSS_%E7%8E%84%E5%AD%A6%E8%AF%AD%E8%A8%80/1.htm">CSS_玄学语言</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/css3/1.htm">css3</a> <div>1.使用flexbox的justify-content和align-items.parent{display:flex;justify-content:center;/*水平居中*/align-items:center;/*垂直居中*/height:100vh;/*需要指定高度*/}2.使用grid的place-items:center.parent{display:grid;place-item</div> </li> <li><a href="/article/1835506237316362240.htm" title="WPF中的ComboBox控件几种数据绑定的方式" target="_blank">WPF中的ComboBox控件几种数据绑定的方式</a> <span class="text-muted">互联网打工人no1</span> <a class="tag" taget="_blank" href="/search/wpf/1.htm">wpf</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a> <div>一、用字典给ItemsSource赋值(此绑定用的地方很多,建议熟练掌握)在XMAL中:在CS文件中privatevoidBindData(){DictionarydicItem=newDictionary();dicItem.add(1,"北京");dicItem.add(2,"上海");dicItem.add(3,"广州");cmb_list.ItemsSource=dicItem;cmb_l</div> </li> <li><a href="/article/1835502704827396096.htm" title="将cmd中命令输出保存为txt文本文件" target="_blank">将cmd中命令输出保存为txt文本文件</a> <span class="text-muted">落难Coder</span> <a class="tag" taget="_blank" href="/search/Windows/1.htm">Windows</a><a class="tag" taget="_blank" href="/search/cmd/1.htm">cmd</a><a class="tag" taget="_blank" href="/search/window/1.htm">window</a> <div>最近深度学习本地的训练中我们常常要在命令行中运行自己的代码,无可厚非,我们有必要保存我们的炼丹结果,但是复制命令行输出到txt是非常麻烦的,其实Windows下的命令行为我们提供了相应的操作。其基本的调用格式就是:运行指令>输出到的文件名称或者具体保存路径测试下,我打开cmd并且ping一下百度:pingwww.baidu.com>./data.txt看下相同目录下data.txt的输出:如果你再</div> </li> <li><a href="/article/1835502578511736832.htm" title="下载github patch到本地" target="_blank">下载github patch到本地</a> <span class="text-muted">小米人er</span> <a class="tag" taget="_blank" href="/search/%E6%88%91%E7%9A%84%E5%8D%9A%E5%AE%A2/1.htm">我的博客</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/patch/1.htm">patch</a> <div>以下是几种从GitHub上下载以.patch结尾的补丁文件的方法:通过浏览器直接下载打开包含该.patch文件的GitHub仓库。在仓库的文件列表中找到对应的.patch文件。点击该文件,浏览器会显示文件的内容,在页面的右上角通常会有一个“Raw”按钮,点击它可以获取原始文件内容。然后在浏览器中使用快捷键(如Ctrl+S或者Command+S)将原始文件保存到本地,选择保存的文件名并确保后缀为.p</div> </li> <li><a href="/article/1835501948011376640.htm" title="使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)" target="_blank">使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)</a> <span class="text-muted">编程经验分享</span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/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/ssh/1.htm">ssh</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>目录前言基本使用教程新建远程连接连接主机自定义命令路由追踪前言后端开发,必然需要和服务器打交道,部署应用,排查问题,查看运行日志等等。一般服务器都是集中部署在机房中,也有一些直接是云服务器,总而言之,程序员不可能直接和服务器直接操作,一般都是通过ssh连接来登录服务器。刚接触远程连接时,使用的是XSHELL来远程连接服务器,连接上就能够操作远程服务器了,但是仅用XSHELL并没有上传下载文件的功能</div> </li> <li><a href="/article/1835498547785592832.htm" title="【华为OD机试真题2023B卷 JAVA&JS】We Are A Team" target="_blank">【华为OD机试真题2023B卷 JAVA&JS】We Are A Team</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/%E5%8D%8E%E4%B8%BA/1.htm">华为</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>华为OD2023(B卷)机试题库全覆盖,刷题指南点这里WeAreATeam时间限制:1秒|内存限制:32768K|语言限制:不限题目描述:总共有n个人在机房,每个人有一个标号(1<=标号<=n),他们分成了多个团队,需要你根据收到的m条消息判定指定的两个人是否在一个团队中,具体的:1、消息构成为:abc,整数a、b分别代</div> </li> <li><a href="/article/1835494762388484096.htm" title="【目标检测数据集】卡车数据集1073张VOC+YOLO格式" target="_blank">【目标检测数据集】卡车数据集1073张VOC+YOLO格式</a> <span class="text-muted">熬夜写代码的平头哥∰</span> <a class="tag" taget="_blank" href="/search/%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B/1.htm">目标检测</a><a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a> <div>数据集格式:PascalVOC格式+YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):1073标注数量(xml文件个数):1073标注数量(txt文件个数):1073标注类别数:1标注类别名称:["truck"]每个类别标注的框数:truck框数=1120总框数:1120使用标注工具:labelImg标注</div> </li> <li><a href="/article/1835493267907637248.htm" title="webpack图片等资源的处理" target="_blank">webpack图片等资源的处理</a> <span class="text-muted">dmengmeng</span> <div>需要的loaderfile-loader(让我们可以引入这些资源文件)url-loader(其实是file-loader的二次封装)img-loader(处理图片所需要的)在没有使用任何处理图片的loader之前,比如说css中用到了背景图片,那么最后打包会报错的,因为他没办法处理图片。其实你只想能够使用图片的话。只加一个file-loader就可以,打开网页能准确看到图片。{test:/\.(p</div> </li> <li><a href="/article/1835492741895778304.htm" title="番茄西红柿叶子病害分类数据集12882张11类别" target="_blank">番茄西红柿叶子病害分类数据集12882张11类别</a> <span class="text-muted">futureflsl</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E9%9B%86/1.htm">数据集</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E7%B1%BB/1.htm">分类</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98/1.htm">数据挖掘</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a> <div>数据集类型:图像分类用,不可用于目标检测无标注文件数据集格式:仅仅包含jpg图片,每个类别文件夹下面存放着对应图片图片数量(jpg文件个数):12882分类类别数:11类别名称:["Bacterial_Spot_Bacteria","Early_Blight_Fungus","Healthy","Late_Blight_Water_Mold","Leaf_Mold_Fungus","Powdery</div> </li> <li><a href="/article/1835492742713667584.htm" title="钢筋长度超限检测检数据集VOC+YOLO格式215张1类别" target="_blank">钢筋长度超限检测检数据集VOC+YOLO格式215张1类别</a> <span class="text-muted">futureflsl</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E9%9B%86/1.htm">数据集</a><a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a> <div>数据集格式:PascalVOC格式+YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):215标注数量(xml文件个数):215标注数量(txt文件个数):215标注类别数:1标注类别名称:["iron"]每个类别标注的框数:iron框数=215总框数:215使用标注工具:labelImg标注规则:对类别进</div> </li> <li><a href="/article/1835491353451130880.htm" title="【华为OD技术面试真题 - 技术面】- python八股文真题题库(4)" target="_blank">【华为OD技术面试真题 - 技术面】- python八股文真题题库(4)</a> <span class="text-muted">算法大师</span> <a class="tag" taget="_blank" href="/search/%E5%8D%8E%E4%B8%BAod/1.htm">华为od</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>华为OD面试真题精选专栏:华为OD面试真题精选目录:2024华为OD面试手撕代码真题目录以及八股文真题目录文章目录华为OD面试真题精选**1.Python中的`with`**用途和功能自动资源管理示例:文件操作上下文管理协议示例代码工作流程解析优点2.\_\_new\_\_和**\_\_init\_\_**区别__new____init__区别总结3.**切片(Slicing)操作**基本切片语法</div> </li> <li><a href="/article/1835490712716668928.htm" title="第六集如何安装CentOS7.0,3分钟学会centos7安装教程" target="_blank">第六集如何安装CentOS7.0,3分钟学会centos7安装教程</a> <span class="text-muted">date分享</span> <div>从光盘引导系统按回车键继续进入引导程序安装界面,选择语言这里选择简体中文版点击继续选择桌面安装下面给系统分区选择磁盘,点击完成选择基本分区,点击加号swap分区,大小填内存的两倍在选择根分区,使用所有可用的磁盘空间选择文件系统ext4点击完成,点击开始安装设置root密码,点击完成设置普通用户和密码,点击完成整个过程持续八分钟左右根据个人配置不同,时间长短不同好,现在点击重启系统进入重启状态点击本</div> </li> <li><a href="/article/1835490471032483840.htm" title="高级 ECharts 技巧:自定义图表主题与样式" target="_blank">高级 ECharts 技巧:自定义图表主题与样式</a> <span class="text-muted">SnowMan1993</span> <a class="tag" taget="_blank" href="/search/echarts/1.htm">echarts</a><a class="tag" taget="_blank" href="/search/%E4%BF%A1%E6%81%AF%E5%8F%AF%E8%A7%86%E5%8C%96/1.htm">信息可视化</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a> <div>ECharts是一个强大的数据可视化库,提供了多种内置主题和样式,但你也可以根据项目的设计需求,自定义图表的主题与样式。本文将介绍如何使用ECharts自定义图表主题,以提升数据可视化的吸引力和一致性。1.什么是ECharts主题?ECharts的主题是指定义图表样式的配置项,包括颜色、字体、线条样式等。通过预设主题,你可以快速更改图表的整体风格,而自定义主题则允许你在此基础上进行个性化设置。2.</div> </li> <li><a href="/article/1835489587498151936.htm" title="ARM驱动学习之基础小知识" target="_blank">ARM驱动学习之基础小知识</a> <span class="text-muted">JT灬新一</span> <a class="tag" taget="_blank" href="/search/ARM/1.htm">ARM</a><a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F/1.htm">嵌入式</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a> <div>ARM驱动学习之基础小知识•sch原理图工程师工作内容–方案–元器件选型–采购(能不能买到,价格)–原理图(涉及到稳定性)•layout画板工程师–layout(封装、布局,布线,log)(涉及到稳定性)–焊接的一部分工作(调试阶段板子的焊接)•驱动工程师–驱动,原理图,layout三部分的交集容易发生矛盾•PCB研发流程介绍–方案,原理图(网表)–layout工程师(gerber文件)–PCB板</div> </li> <li><a href="/article/1835489588169240576.htm" title="ARM驱动学习之5 LEDS驱动" target="_blank">ARM驱动学习之5 LEDS驱动</a> <span class="text-muted">JT灬新一</span> <a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F/1.htm">嵌入式</a><a class="tag" taget="_blank" href="/search/C/1.htm">C</a><a class="tag" taget="_blank" href="/search/%E5%BA%95%E5%B1%82/1.htm">底层</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</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%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a> <div>ARM驱动学习之5LEDS驱动知识点:•linuxGPIO申请函数和赋值函数–gpio_request–gpio_set_value•三星平台配置GPIO函数–s3c_gpio_cfgpin•GPIO配置输出模式的宏变量–S3C_GPIO_OUTPUT注意点:DRIVER_NAME和DEVICE_NAME匹配。实现步骤:1.加入需要的头文件://Linux平台的gpio头文件#include//三</div> </li> <li><a href="/article/1835489208152715264.htm" title="Rust基础知识" target="_blank">Rust基础知识</a> <span class="text-muted">GRKF15</span> <a class="tag" taget="_blank" href="/search/rust/1.htm">rust</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>1.Rust语言简介1.1基础语法变量声明:let关键字用于声明变量,可以指定或不指定类型,如leta=10;和letmutc=30i32;。函数定义:使用fn关键字定义函数,并指定参数类型及返回类型,如fnadd(i:i32,j:i32)->i32{i+j}。控制流:包括if、else等,控制语句后需要使用;来结束语句。1.2数据类型整数类型:i8、i16、i32、i64、i128,以及无符号的</div> </li> <li><a href="/article/1835485429059645440.htm" title="docker" target="_blank">docker</a> <span class="text-muted">igotyback</span> <a class="tag" taget="_blank" href="/search/eureka/1.htm">eureka</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E5%8E%9F%E7%94%9F/1.htm">云原生</a> <div>Docker容器的文件系统是隔离的,但是可以通过挂载卷(Volumes)或绑定挂载(BindMounts)将宿主机的文件系统目录映射到容器内部。要查看Docker容器的映射路径,可以使用以下方法:查看容器配置:使用dockerinspect命令可以查看容器的详细配置信息,包括挂载的卷。例如:bashdockerinspect在输出的JSON格式中,查找"Mounts"部分,这里会列出所有的挂载信息</div> </li> <li><a href="/article/1835484293607026688.htm" title="【Git】常见命令(仅笔记)" target="_blank">【Git】常见命令(仅笔记)</a> <span class="text-muted">好想有猫猫</span> <a class="tag" taget="_blank" href="/search/Git/1.htm">Git</a><a class="tag" taget="_blank" href="/search/Linux%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">Linux学习笔记</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/elasticsearch/1.htm">elasticsearch</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>文章目录创建/初始化本地仓库添加本地仓库配置项提交文件查看仓库状态回退仓库查看日志分支删除文件暂存工作区代码远程仓库使用`.gitigore`文件让git不追踪一些文件标签创建/初始化本地仓库gitinit添加本地仓库配置项gitconfig-l#以列表形式显示配置项gitconfiguser.name"ljh"#配置user.namegitconfiguser.email"123123@qq.c</div> </li> <li><a href="/article/1835480639814594560.htm" title="在Ubuntu中编译含有JSON的文件出现报错" target="_blank">在Ubuntu中编译含有JSON的文件出现报错</a> <span class="text-muted">芝麻糊76</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/kill_bug/1.htm">kill_bug</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a> <div>在ubuntu中进行JSON相关学习的时候,我发现了一些小问题,决定与大家进行分享,减少踩坑时候出现不必要的时间耗费截取部分含有JSON部分的代码进行展示char*str="{\"title\":\"JSONExample\",\"author\":{\"name\":\"JohnDoe\",\"age\":35,\"isVerified\":true},\"tags\":[\"json\",\"</div> </li> <li><a href="/article/1835479000600899584.htm" title="Linux MariaDB使用OpenSSL安装SSL证书" target="_blank">Linux MariaDB使用OpenSSL安装SSL证书</a> <span class="text-muted">Meta39</span> <a class="tag" taget="_blank" href="/search/MySQL/1.htm">MySQL</a><a class="tag" taget="_blank" href="/search/Oracle/1.htm">Oracle</a><a class="tag" taget="_blank" href="/search/MariaDB/1.htm">MariaDB</a><a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/Windows/1.htm">Windows</a><a class="tag" taget="_blank" href="/search/ssl/1.htm">ssl</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/mariadb/1.htm">mariadb</a> <div>进入到证书存放目录,批量删除.pem证书警告:确保已经进入到证书存放目录find.-typef-iname\*.pem-delete查看是否安装OpenSSLopensslversion没有则安装yuminstallopensslopenssl-devel开启SSL编辑/etc/my.cnf文件(没有的话就创建,但是要注意,在/etc/my.cnf.d/server.cnf配置了datadir的,</div> </li> <li><a href="/article/1835476984034062336.htm" title="【六】阿伟开始搭建Kafka学习环境" target="_blank">【六】阿伟开始搭建Kafka学习环境</a> <span class="text-muted">能源恒观</span> <a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a> <div>阿伟开始搭建Kafka学习环境概述上一篇文章阿伟学习了Kafka的核心概念,并且把市面上流行的消息中间件特性进行了梳理和对比,方便大家在学习过程中进行对比学习,最后梳理了一些Kafka使用中经常遇到的Kafka难题以及解决思路,经过上一篇的学习我相信大家对Kafka有了初步的认识,本篇将继续学习Kafka。一、安装和配置学习一项技术首先要搭建一套服务,而Kafka的运行主要需要部署jdk、zook</div> </li> <li><a href="/article/1835475582138281984.htm" title="ios GCD" target="_blank">ios GCD</a> <span class="text-muted">_Waiting_</span> <div>1.GCD任务和队列学习GCD之前,先来了解GCD中两个核心概念:任务和队列。任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。同步执行(sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等</div> </li> <li><a href="/article/1835475216080400384.htm" title="openssl+keepalived安装部署" target="_blank">openssl+keepalived安装部署</a> <span class="text-muted">_小亦_</span> <a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2/1.htm">项目部署</a><a class="tag" taget="_blank" href="/search/keepalived/1.htm">keepalived</a><a class="tag" taget="_blank" href="/search/openssl/1.htm">openssl</a> <div>文章目录OpenSSL安装下载地址编译安装修改系统配置版本Keepalived安装下载地址安装遇到问题安装完成配置文件keepalived运行检查运行状态查看系统日志修改服务service重新加载systemd检查配置文件语法错误OpenSSL安装下载地址考虑到后面设备可能没法连接到外网,所以采用安装包的方式进行部署,下载地址:https://www.openssl.org/source/old/</div> </li> <li><a href="/article/1835471185589137408.htm" title="一文掌握python常用的list(列表)操作" target="_blank">一文掌握python常用的list(列表)操作</a> <span class="text-muted">程序员neil</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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>目录一、创建列表1.直接创建列表:2.使用list()构造器3.使用列表推导式4.创建空列表二、访问列表元素1.列表支持通过索引访问元素,索引从0开始:2.还可以使用切片操作访问列表的一部分:三、修改列表元素四、添加元素1.append():在末尾添加元素2.insert():在指定位置插入元素五、删除元素1.del:删除指定位置的元素2.remove():删除指定值的第一个匹配项3.pop():</div> </li> <li><a href="/article/1835468916290318336.htm" title="JVM、JRE和 JDK:理解Java开发的三大核心组件" target="_blank">JVM、JRE和 JDK:理解Java开发的三大核心组件</a> <span class="text-muted">Y雨何时停T</span> <a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>Java是一门跨平台的编程语言,它的成功离不开背后强大的运行环境与开发工具的支持。在Java的生态中,JVM(Java虚拟机)、JRE(Java运行时环境)和JDK(Java开发工具包)是三个至关重要的核心组件。本文将探讨JVM、JDK和JRE的区别,帮助你更好地理解Java的运行机制。1.JVM:Java虚拟机(JavaVirtualMachine)什么是JVM?JVM,即Java虚拟机,是Ja</div> </li> <li><a href="/article/1835466921470947328.htm" title="Some jenkins settings" target="_blank">Some jenkins settings</a> <span class="text-muted">SnC_</span> <div>Jenkins连接到特定gitlabproject的特定branch我采用的方法是在pipeline的script中使用git命令来指定branch。如下:stage('Clonerepository'){steps{gitbranch:'develop',credentialsId:'gitlab-credential-id',url:'http://gitlab.com/repo.git'}}</div> </li> <li><a href="/article/1835466523163062272.htm" title="Linux sh命令" target="_blank">Linux sh命令</a> <span class="text-muted">fengyehongWorld</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>目录一.基本语法二.选项2.1-c字符串中读取内容,并执行2.1.1基本用法2.1.2获取当前目录下失效的超链接2.2-x每个命令执行之前,将其打印出来2.3结合Here文档使用一.基本语法⏹Linux和Unix系统中用于执行shell脚本或运行命令的命令。sh[选项][脚本文件][参数...]⏹选项-c:从字符串中读取内容,并执行。-x:在每个命令执行之前,将其打印出来。-s:从标准流中读取内容</div> </li> <li><a href="/article/1835466270955368448.htm" title="Linux vi常用命令" target="_blank">Linux vi常用命令</a> <span class="text-muted">fengyehongWorld</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>参考资料viコマンド(vimコマンド)リファレンス目录一.保存系命令二.删除系命令三.移动系命令四.复制粘贴系命令一.保存系命令⏹保存并退出:wq⏹强制保存并退出:wq!⏹退出(文件未编辑):q⏹强制退出(忽略已编辑内容):q!⏹另存为:w新文件名二.删除系命令⏹删除当前行dd⏹清空整个文档gg:移动到文档顶部dG:删除到最后一行ggdG三.移动系命令⏹移动到文档顶部gg⏹移动到文档底部#方式1G</div> </li> <li><a href="/article/1835460655675699200.htm" title="张芝华49天共修 - 草稿" target="_blank">张芝华49天共修 - 草稿</a> <span class="text-muted">李娟AINI</span> <div>祈禱、靜心、源代碼編程、觀想發願四根支柱,運用靈性能量的助力,讓夢想和渴望在最大向度中輕鬆實現。共修群指定书籍:1.能断金刚麦克格西2.新世界:灵性的觉醒埃克哈特·托尔3.爱是一切的答案芭芭拉迪安吉莉思4.完美的爱,不完美的关系约翰•威尔伍德5.爱的业力法则麦克格西6.漫画《金刚经》蔡志忠7.蔡志忠典藏国学漫画系列(套装共6册)作业:全部在共修群里完成,并请保存好自己的作业。l一周三次共修觉察作业</div> </li> <li><a href="/article/1835458199755517952.htm" title="spring如何整合druid连接池?" target="_blank">spring如何整合druid连接池?</a> <span class="text-muted">惜.己</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/junit/1.htm">junit</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/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/idea/1.htm">idea</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a> <div>目录spring整合druid连接池1.新建maven项目2.新建mavenModule3.导入相关依赖4.配置log4j2.xml5.配置druid.xml1)xml中如何引入properties2)下面是配置文件6.准备jdbc.propertiesJDBC配置项解释7.配置druid8.测试spring整合druid连接池1.新建maven项目打开IDE(比如IntelliJIDEA,Ecl</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>