0%

在wsl系统中执行sytemctl会报如下的错误:System has not been booted with systemd as init system (PID 1). Can‘t operate,这是因为安装systemctl的原因,可以通过如下的步骤进行配置

  • 安装daemonize和fontconfig:sudo apt install -y daemonize fontconfig

  • 编辑/etc/profile文件,添加如下的脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')

    if [ -z "$SYSTEMD_PID" ]; then
    sudo /usr/bin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
    SYSTEMD_PID=$(ps -ef | grep '/lib/systemd/systemd --system-unit=basic.target$' | grep -v unshare | awk '{print $2}')
    fi

    if [ -n "$SYSTEMD_PID" ] && [ "$SYSTEMD_PID" != "1" ]; then
    exec sudo /usr/bin/nsenter -t $SYSTEMD_PID -a su - $LOGNAME
    fi
  • 编辑 /etc/sudoers,添加如下的脚本

    1
    2
    %sudo ALL=(ALL) NOPASSWD: /usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit=basic.target
    %sudo ALL=(ALL) NOPASSWD: /usr/bin/nsenter -t [0-9]* -a su - [a-zA-Z0-9]*
  • 执行命令:sudo source /etc/profile或者重启 wsl2

  • 执行systemctl命令

image-20220621104910471

可以看到执行成功!

前言

mybatis可以简单地理解为一款持久层的框架,支持定制化SQL,存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录

示例

下面通过一个简单的示例体验一下mybatis,是如何操作数据库的,假设有一张表:dept

image-20220621223017860

  • 创建一个目录:mkdir mybatis_demo,添加pom.xml文件

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sherman</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOP</version>

<dependencies>
<!-- 引入mybatis持久层-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>

<!-- 连接mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- 引入lombok,简化代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>

</dependencies>
</project>

先指定一些项目的基本信息和依赖项

  • 创建实体类:src/main/java/com/sherman/demo/Dept.java(没有相关的目录,先创建)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.sherman.demo.entity;
    import lombok.Data;
    /**
    * Dept
    */
    @Data
    public class Dept {
    private int deptNo;
    private String deptName;
    private String dbSource;
    }
  • 创建mapper类:src/main/java/com/sherman/demo/DeptMapper.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.sherman.demo.mapper;
    import org.apache.ibatis.annotations.Mapper;
    import com.sherman.demo.entity.Dept;

    @Mapper
    public interface DeptMapper {
    /*
    * 根据Id获取部门信息
    */
    public Dept getDeptByDeptNo(int deptNo);
    }
  • 添加项目配置文件:src/main/resources/application.yml(配置数据库连接)

    1
    2
    3
    4
    driver: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://myubuntu.wsl:3306/spring_cloud_demo
    username: root
    password: 123456
  • 添加mybatis的配置文件:src/main/resources/mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <!-- 读取外部的配置-->
    <properties resource="application.yml" />
    <settings>
    <!-- 打印mybatis,执行sql的日志-->
    <setting name="logImpl" value="STDOUT_LOGGING" />
    <!-- 自动兼容mysql数据库字段中带下划线-->
    <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="mybatis/mapper/DeptMapper.xml"/>
    </mappers>
    </configuration>
  • 添加mybatis中的sql映射文件:src/main/resources/mybatis/mapper/DeptMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.sherman.demo.mapper.DeptMapper">

    <select id="getDeptByDeptNo" resultType="com.sherman.demo.entity.Dept">
    select * from dept
    where dept_no=#{deptNo,jdbcType=INTEGER}
    </select>
    </mapper>
  • 创建启动类:src/main/java/com/sherman/demo/App.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.sherman.demo;

    import java.io.InputStream;

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;

    import com.sherman.demo.entity.Dept;
    import com.sherman.demo.mapper.DeptMapper;

    /**
    * App
    */
    public class App {

    public static void main(String[] args) {
    try {
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = deptMapper.getDeptByDeptNo(1);
    System.out.println(dept.toString());
    sqlSession.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    输出结果:

    image-20220621234419693

前言

IOC全程是Inversion of Control,控制反转。它是一种设计思想,对象的生命周期都是由容器控制。IOC的目的就是为了让程序更加松耦合。IOC的背后原来就是利用反射和工厂模式,下面我们通过几种方式来阐述一下spring中IOC是怎么使用的。

示例

下面我们通过一个示例,来看看,怎么样让对象交给spring IOC容器进行管理

  • 创建文件夹:mkdir springioc_demo

  • 在springioc_demo目录中添加pom.xml文件,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sherman</groupId>
    <artifactId>springioc_demo</artifactId>
    <version>0.0.1-SNAPSHOP</version>
    <name>springioc_demo</name>

    <dependencies>
    <!-- Spring容器的核心部分-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.21</version>
    </dependency>
    <!-- 简化代码-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    </dependency>
    <!-- 使用logback记录日志-->
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha16</version>
    </dependency>

    </dependencies>
    </project>
  • 添加logback的配置文件:src/main/resources/logback.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <?xml version="1.0" encoding="UTF-8" ?>
    <configuration>
    <logger name="org.springframework" level="ERROR"/>
    <logger name="com.sherman" level="INFO"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
    <Pattern>
    %d %blue(%-5level) %magenta(%logger{36}) -%msg %n
    </Pattern>
    </encoder>
    </appender>

    <root>
    <level value="INFO"/>
    <appender-ref ref="consoleAppender"/>
    </root>
    </configuration>
  • 添加项目启动文件:src/main/java/com/sherman/demo/App.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.sherman.demo;

    import lombok.extern.slf4j.Slf4j;

    @Slf4j
    public class App {
    public static void main(String[] args) {
    log.info("hello world");
    System.out.println("hello world");
    }
    }

    之后,项目就可以正常运行了。

    方式一:@Configuration+@Bean注解的方式

    • 添加服务:src/main/java/com/sherman/demo/service/MessageService.java,代码如下:

      1
      2
      3
      4
      5
      6
      7
      package com.sherman.demo.service;

      public class MessageService {
      public void sendSms(){
      System.out.println("send mobile msg!");
      }
      }
    • 添加配置:src/main/java/com/sherman/demo/config/AppConfig.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      package com.sherman.demo.config;

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;

      import com.sherman.demo.service.MessageService;

      @Configuration
      public class AppConfig {

      @Bean
      public MessageService getMessageService(){
      return new MessageService();
      }
      }
    • 修改src/main/java/com/sherman/demo/App.java中的代码:通过AnnotationConfigApplicationContext实现基于Java的配置类加载Spring的应用上下文。避免使用xml文件进行配置,相比xml配置,更加便捷。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      package com.sherman.demo;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      import com.sherman.demo.service.MessageService;
      import lombok.extern.slf4j.Slf4j;

      @Slf4j
      public class App {
      public static void main(String[] args) {
      log.info("hello world");
      System.out.println("hello world");

      AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
      "com.sherman");//AnnotationConfigApplicationContext有3种使用方式,这只是其中一种,可以参考构造函数
      MessageService messageService = annotationConfigApplicationContext.getBean(MessageService.class);
      messageService.sendSms();
      }
      }

      输出:

      image-20220623105949794

    方式二:@Service等注解的方式

    • 添加服务:src/main/java/com/sherman/demo/service/HelloWorldService.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      package com.sherman.demo.service;

      import org.springframework.stereotype.Service;

      @Service //添加@Service注解
      public class HelloWorldService {
      public void sayHello(){
      System.out.println("hello sherman!");
      }
      }
    • 修改src/main/java/com/sherman/demo/App.java中的代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      package com.sherman.demo;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      import com.sherman.demo.service.HelloWorldService;
      import lombok.extern.slf4j.Slf4j;

      @Slf4j
      public class App {
      public static void main(String[] args) {
      log.info("hello world");
      System.out.println("hello world");

      AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
      "com.sherman");
      HelloWorldService helloWorldService = annotationConfigApplicationContext.getBean(HelloWorldService.class);
      helloWorldService.sayHello();
      }
      }

    方式三:@Import注解的方式

    @Import直接有三种使用方式,下面分别举例说明一下

    普通的类
    • 添加服务:src/main/java/com/sherman/demo/service/ComputerService.java

      1
      2
      3
      4
      5
      6
      7
      package com.sherman.demo.service;

      public class ComputerService {
      public void getSystemName(){
      System.out.println(System.getProperty("os.name"));
      }
      }
    实现ImportSelector接口
    • 添加服务:src/main/java/com/sherman/demo/service/DateService.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      package com.sherman.demo.service;

      import java.util.Date;

      public class DateService {
      public void printDate(){
      System.out.println(new Date());
      }
      }
    • 实现接口ImportSelector

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      package com.sherman.demo.config;

      import org.springframework.context.annotation.ImportSelector;
      import org.springframework.core.type.AnnotationMetadata;

      public class MyImportSelector implements ImportSelector {

      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      return new String[]{"com.sherman.demo.service.DateService"};
      }
      }
    实现ImportBeanDefinitionRegistrar接口
    • 添加服务:src/main/java/com/sherman/demo/service/CalculationService.java

      1
      2
      3
      4
      5
      6
      7
      package com.sherman.demo.service;

      public class CalculationService {
      public int add(int a, int b) {
      return a + b;
      }
      }
    • 实现ImportBeanDefinitionRegistrar接口

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      package com.sherman.demo.config;

      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
      import org.springframework.beans.factory.support.RootBeanDefinition;
      import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
      import org.springframework.core.type.AnnotationMetadata;

      import com.sherman.demo.service.CalculationService;

      /**
      * MyImportBeanDefinitionRegister
      */
      public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
      RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(CalculationService.class);
      registry.registerBeanDefinition("calcuationService", rootBeanDefinition);
      }
      }

    修改:src/main/java/com/sherman/demo/config/AppConfig.java,通过import导入需要的类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.sherman.demo.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;

    import com.sherman.demo.service.ComputerService;
    import com.sherman.demo.service.MessageService;

    @Configuration
    @Import({ComputerService.class, MyImportSelector.class, MyImportBeanDefinitionRegister.class})
    public class AppConfig {

    @Bean
    public MessageService getMessageService(){
    return new MessageService();
    }
    }

    验证是否能够从IOC容器中获取到注入的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.sherman.demo;

    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    import com.sherman.demo.service.CalculationService;
    import com.sherman.demo.service.ComputerService;
    import com.sherman.demo.service.DateService;

    import lombok.extern.slf4j.Slf4j;

    @Slf4j
    public class App {
    public static void main(String[] args) {
    log.info("hello world");
    System.out.println("hello world");

    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(
    "com.sherman");
    ComputerService computerService = annotationConfigApplicationContext.getBean(ComputerService.class);
    computerService.getSystemName();

    DateService dateService = annotationConfigApplicationContext.getBean(DateService.class);
    dateService.printDate();

    CalculationService calculationService = annotationConfigApplicationContext.getBean(CalculationService.class);
    System.out.println(calculationService.add(1, 2));
    }
    }

    通过输出结果可以看到,也能从IOC容器中获取到对象:

    image-20220623163812795

前言

windows系统的linux,在每一次启动的时候ip总是变动的,导致在windows系统中连接linux下的mysql服务总是失败,那是否可以在启动linux系统的时候就将ip地址绑定到windows的hosts文件中呢?这样通过本地的域名就能连接到linux系统的服务了。

编写脚本

编写shell脚本:modify_hosts.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash

params[1]=$1
# get ip address
ip_addr=$(ip addr|grep eth0|grep inet|awk '{print $2}'|cut -d / -f 1)
# ifconfig eth0|sed -n '2p'|awk '{print $2}' #该命令获取ip地址更为简洁
# 判断参数是否为空
if [ -z ${params[1]} ]
then
#为空,则获取系统的名称
sys_name=$(cat /etc/lsb-release|grep ID|cut -d = -f 2)
else
#不为空,则取第一个参数名
sys_name=${params[1]}
fi

host_name=$sys_name".wsl"

win_host_path=/mnt/c/Windows/System32/drivers/etc/HOSTS
#获取行号
line_no=$(nl -b a $win_host_path|grep $host_name|awk '{print $1}')

for line in $line_no
do
#删除该行的内容
sed -i $line'd' $win_host_path
done

#追加ip的映射
echo $ip_addr' '$host_name >> $win_host_path

[ -f "$win_host_path" ] && echo "windows host:"$(nl $win_host_path|grep $host_name) && echo 'linux ip addr:'$ip_addr
exit 0

自动执行

为了能让脚本每次启动的时候自动运行,修改~/.bashrc文件,并在最后添加如下代码:

1
2
3
4
5
6
7
bash /root/shell_scripts/modify_host.sh myubuntu
# 下面的代码是想每次启动的时候确保mysql服务也启动了
service mysql status|grep -w stopped
if [ $? -eq 0 ]
then
service mysql start
fi

这样,只需要在windows下使用myubuntu.wsl域名就能连接到linux的服务器

image-20220614165923164

前言

最近在windows系统下安装了一个Ubuntu18.04.5的Linux系统,然后直接使用Ubuntu包管理工具装了MySql数据库,装的过程中没有提示任何有关root密码的信息(也许是我没有仔细观察,这不重要),这就需要能通过某种方式可以找到MySql的初始密码,或者修改原有的root密码。

安装MySql

可以先通过sudo apt search mysql-server看看包源提供了那些MySql的版本

image-20220603224711038

执行:sudo apt install mysql-server等待安装完成,查看服务的状态:sudo service mysql status发现服务未启动,执行命令sudo service mysql start启动MySql服务,查看MySql的服务状态已经启动,如果在启动的过程中发现如下的错误信息:mkdir: cannot create directory ‘//.cache’: Permission denied,可以通过命令sudo cat /etc/passwd查看MySql用户的相关信息如下:

image-20220603225158252

说明MySql用户的home目录不存在,需要将其改成如下:

image-20220603225255353

修改完成之后重新启动MySql服务sudo service mysql restart,错误消失,服务也正常启动了,接下就该登录MySql服务器,看看相关的数据库和表了,但是由于不知道root的密码,也就没有办法登录了

修改密码

  • 修改 /etc/mysql/mysql.conf.d/mysqld.cnf文件,在[mysqld]下面添加一行代码:skip-grant-tables=1

    image-20220603225818282

  • 重启启动MySql服务:sudo service mysql restart

  • 登录MySql:mysql -u root -p(不需要输入密码,直接enter进入)

  • 执行语句:alter user 'root'@'localhost' IDENTIFIED WITH my_native_password BY '123456';,可能会提示:he MySQL server is running with the –skip-grant-tables option so it cannot execute this statement的错误信息,执行flush privileges;即可

  • 退出,将文件/etc/mysql/mysql.conf.d/mysqld.cnf中skip-grant-tables=1删除或者注释掉,重启MySql服务

  • 重新登录MySql:mysql -u root -p 输入密码:123456,就可以正常登录了

至此,root的密码也就修改成功了。在初始化安装的时候,会有一个初始化的密码,也可以通过查看/var/log/mysqld.log文件的记录能查到初始的root密码,sudo grep 'temporary password' /var/log/mysqld.log,但是我执行了该命令,发现文件/var/log/mysqld.log不存在,估计是我的环境问题,如果大家也有遇到这种情况,可以参考一下,或许有点帮助。

前言

在研读《深入理解Java虚拟机》这本书时,看到Java虚拟机运行时数据区中有关本地方法栈(Native Method Stack)的概念,本地方法栈就是为调用Native方法服务的,被Native修饰的方法不是由Java语言实现的,可以是C/C++实现,然后通过JNI(Java Native Interface)实现调用。当然也有Java虚拟机栈,是为Java方法服务的。本篇主要是想熟悉怎么样去调用Native方法。

通过JNI调用C++方法

  • 通过java代码调用C++的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.io.File;

    public class Main {

    static {
    System.load("E:" + File.separator + "test.dll");
    }

    public native static void TestOne();
    public static void main(String[] args) {
    TestOne();
    }
    }
  • 通过javac Main.java编译生成Main.class文件,然后执行javah Main会生成Main.h文件,目的就是为指定的类中的Native方式生成.h文件

  • 通过visual studio 2022创建一个C++的项目test,生成的dll名称需要和上述Java代码中加载的类库名称要一致

    image-20220531233445499

  • 将上面生成的Main.h,以及%JAVA_HOME/include/jni.h%和%JAVA_HOME/include/win32/jni_md.h%,三个文件复制到test目录下,如图所示:

    image-20220531233855312

  • 将上面三个文件添加到头文件中

    image-20220601093337374

  • 修改一下Main.h中的#include <jni.h>改成#include "jni.h"

  • 添加C++源文件Hello.cpp并添加如下的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include "pch.h"
    #include <iostream>
    #include "Main.h"
    using namespace std;

    JNIEXPORT void JNICALL Java_Main_TestOne
    (JNIEnv*, jclass)
    {
    cout << "hello sherman" << endl;
    }
  • 生成项目,复制test.dll,到E:\下(我这里是64位)

    image-20220601101338011

  • 直接运行java程序可以看到输出结果:”hello sherman”

前言

一般我们的配置信息默认都是会配置在/src/main/resources/application.properties(或者application.yml)文件中,当然,也可以在resources文件夹下添加自己的配置文件,甚至子目录中添加自己的配置文件,那么我们又该如何读取自己添加的配置文件中的内容呢?

准备

我们先定义一个公共的输出配置信息的方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static void getProperties(InputStream inputStream) {
Properties properties = new Properties();
if (inputStream == null) {
return;
}
try {
properties.load(inputStream);
properties.list(System.out);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

这里是通过java.util下的Properties类来获取配置文件中的属性

添加自定义的配置文件,在resources目录下添加子目录config并添加配置文件db.properties

image-20220530225539228

内容如下:

1
jdbc.driver=com.mysql.cj.jdbc.Driver

在java中,resources文件夹下的文件在编译后,都是为根目录(classpath)。接下来,准备采用以下的6种方式进行配置内容的读取

六种方式

方法一

1
2
URL path = this.getClass().getClassLoader().getResource("config/db.properties"); // 注意路径不带/开头
getProperties(path.openStream());

方法二

1
2
URL path = this.getClass().getResource("/config/db.properties"); //路径需要以/开头
getProperties(path.openStream());

方法三

在springboot项目我还可以使用如下的方式:

1
2
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config/db.properties");//与方法一类似,只不过直接返回了InputStream类型
getProperties(inputStream);

方法四

springboot项目中使用

1
2
inputStream = this.getClass().getResourceAsStream("/config/db.properties");//与方法二类似,只不过返回了InputStream类型了
getProperties(inputStream);

方法五

springboot项目中使用

1
2
ClassPathResource classPathResource = new ClassPathResource("config/db.properties");
getProperties(classPathResource.getInputStream());

方法六

springboot项目中使用,通过@Value注解,但是我们还需要通过@PropertySource(“classpath:config/db.properties”)

注解指定配置文件的路径,如果是默认的配置文件,如:application.properties(.yml)就不需要指定路径

1
2
3
@SpringBootApplication
@PropertySource("classpath:config/db.properties")
public class App implements CommandLineRunner{...}
1
2
@Value("${jdbc.driver}")
private String driver;

通过上述6种方法都可以成功获取到自定义配置文件中的配置信息,如果大家还有更好的方式,可以评论区留言。

需求背景

在项目开发过程中,总会有一些公共的代码,会被抽取到一个单独的模块当中,其他的项通过引入该项目的jar包,达到代码复用的目的,近期在学习springboot相关知识,正好也遇到这种场景,但是引入公共的模块时,打包项目总是提示找不到公共项目中的相关类,下面就说说问题产生的原因和解决的办法。

代码示例

首先,创建一个公共的maven项目,artifactId:common,在其中添加一些公共的类,执行 mvn clean、mvn install命令

image-20220525212436417

然后再创建一个maven项目,比如:springmvc(名字大家随便取),引入common的依赖

1
2
3
4
5
6
<dependency>
<groupId>com.sherman</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>

编译springmvc项目,结果提示如下的错误信息:

image-20220525214158858

截图中的错误提示都是我在common模块中定义的类,但是在springmvc项目中却提示找不到,这个问题是由于我在common项目中指定了spring-boot-maven-plugin这个插件生成jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.sherman.common.CommonApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

然后我把这个插件去掉,重新对common项目执行命令:mvn clean、mvn install,接着再重新编译springmvc项目,问题解决。

如果运行springmvc项目提示“Consider defining a bean of type ‘xxxx’ 类似的错误信息,需要在springmvc项目的启动中添加@ComponentScan注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.springmvc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.sherman.common"})
public class SpringmvcApplication {

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

打包区别

通过上面的描述,是由于使用了spring-boot-maven-plugin进行打包,那通过该插件打包的程序一般是作为独立运行的jar,直接可以通过java -jar xxx.jar命令运行,而去掉这个插件之后就是生成了普通的jar包,只是包含模块中的代码,不包含依赖项,我们也可以通过观察生成的jar包文件的大小,来确认是否和描述的一致。

使用spring-boot-maven-plugin插件生成jar的大小如下图所示:

image-20220525215649240

去掉spring-boot-maven-plugin之后生成的jar的大小如下图所示:

image-20220525220045455

希望此文能帮助那些遇到和我一样问题的同学。

依赖区别

swagger2需要引入如下两个依赖项:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

但是到了swagger3中只需要引入一个依赖即可:

1
2
3
4
5
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

swagger配置区别

swagger2的配置代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.example.springboot_demo.util;

import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2 //与swagger3不同的地方,swagger3使用@EnableOpenApi
public class Swagger2Config{

@Value("${swagger2.enabled}")
private boolean swagger2Enabled;//读取配置,控制swagger是否启用

@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2) //与swagger3不同的地方,swagger3使用OAS_30
.apiInfo(apiInfo())
.enable(swagger2Enabled)
.groupName("SwaggerGroupOneApi")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("牛逼工具平台API接口文档")
.termsOfServiceUrl("https://likeshan168.github.io")
.contact(new Contact("Sherman", "https://likeshan168.github.io", "likeshan168@163.com"))
.version("1.0")
.description("系统API描述")
.build();
}
}

swagger2使用@EnableSwagger2进行注解,还有DocumentationType是SWAGGER_2

swagger3的配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.example.springmvc.util;

import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
@EnableOpenApi //与swagger2不一样的地方,swagger2使用@EnableSwagger2
public class Swagger3Config {

/**
* 读取配置
*/
@Value("${swagger.enabled}")
Boolean swaggerEnabled;

@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)//与swagger2不一样的地方,swagger2使用SWAGGER_2
.apiInfo(apiInfo())
.enable(swaggerEnabled)
.groupName("SwaggerGroupOneApi")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("牛逼工具平台API接口文档")
.termsOfServiceUrl("https://likeshan168.github.io")
.contact(new Contact("Sherman", "https://likeshan168.github.io", "likeshan168@163.com"))
.version("1.0")
.description("系统API描述")
.build();
}
}

访问地址的区别

swagger2的访问地址为:http://ip:port/swagger-ui.html

swagger3的访问地址为:http://ip:port/swagger-ui/ 或者 http://ip:port/swagger-ui/index.html

其他问题

在配置swagger的过程,碰到了404的情况,也是网上一通寻找答案,未能解决我的问题,主要的原因在于我下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.springboot_demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.sherman.common"})
@Slf4j
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}

由于,我引入了另一个common的模块,所以我就通过@ComponentScan注解指定了需要扫描的包名称,但是却没有添加本项目的包在里面,导致发现不了本项目中的componet,service,controller等bean,只需要作如下修改即可:

1
@ComponentScan(basePackages = {"com.sherman.common","com.example.springboot_demo"})

至此,问题得以解决。

源码下载

访问https://hg.openjdk.java.net/jdk/jdk12/,点击左侧的browser,如果所示:

image-20220524110301618

然后点击zip格式,下载压缩包的文件:

image-20220524110403637

环境准备

准备一台linux服务器,例如:Ubuntu 18.04 LTS,安装编译工具GCC:

1
sudo apt-get install build-essential

还需要安装openjdk编译需要的依赖库FeeType,CUPS等第三方库:

1
2
3
4
5
sudo apt-get install libfreetype6-dev # FreeType
sudo apt-get install libcups2-dev # CUPS
sudo apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev # X11
sudo apt-get install libasound2-dev # ALSA
sudo apt-get install autoconf # Autoconf

我们还需要一个地本版的openjdk,例如,需要编译的openjdk为12,那么需要准备一个11的openjdk,因为openjdk有多个部分组成(HotSpot,JDK类库,JAXWS,JAXP…),其中HotSpot部分代码是由C、C++编写,而更多的是用java编写的,因此要编译这些java代码的话,需要编译期间可用的JDK,官方称这个JDK为“Bootstrap JDK”。编译OpenJDK 12时,Bootstrap JDK必须使用JDK 11及之后的版本。

1
sudo apt-get install openjdk-11-jdk

上述环境都准备完成之后,接下来就可以进行openjdk的编译

编译

我们可以通过默认的配置进行编译,也可以通过定制化的方式进行编译,进入源码所在的目录,可以通过命令:bash configure –help 查看有哪些配置项可以指定,例如:bash configure –enable-debug -with-jvm-variants=server(可选值server,client,minimal,core,zero,custom),其中–enable-debug等效于:-with-debug-level=fastdebug(可选值为:release,fastdebug,showdebug),configure命令会检查依赖项,参数配置和输出目录结构,如果编译过程中需要的工具链或者依赖项有缺失,将会得到明确的提示信息,如下所示:

image-20220524181511854

可以看到提示已经很清晰了,只需要执行如下命令即可:

1
sudo apt-get install libfontconfig1-dev