Java操作git实战

542 阅读3分钟

1. 实战

第一步,JGit jar包的引入 可以在下面仓库搜索对应的jar包

central.sonatype.dev/artifact/or…

我使用的是一个非常老的版本,可以换成新一些的版本,注意版本的兼容性。

<dependency>
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>org.eclipse.jgit</artifactId>
   <version>5.0.3.201809091024-r</version>
</dependency>

第二步,API调用啦 这个和正常的git命令操作没有啥区别。 下面这段代码就表示了怎么用Java代码操作git clone

private static synchronized BaseResponse<Git> gitClone( GitConf gitConf, String remoteBranch, File localFile){
    CloneCommand cloneCommand = Git.cloneRepository()
            .setURI(gitConf.getRemoteUrl())
            .setBranch(remoteBranch)
            .setCredentialsProvider(new UsernamePasswordCredentialsProvider(
                    gitConf.getUserName(), gitConf.getPassword()))
            .setDirectory(localFile);
    try {
        return BaseResponse.success(cloneCommand.call());
    }catch (GitAPIException gitAPIException){
        return BaseResponse.fail(gitAPIException.getMessage());
    }
}

操作的时候有四个对象需要注意: 第一个, Repository 类,该类表示GIT仓库。它是一个抽象类,这个类有多个实现。需要注意的是线程安全这块,FileRepository 是线程安全的(通过synchronized(lock)的方式保证线程安全)。

image.png

第二个,GIT类,该类提供跟GIT仓库交互的方式。需要注意的是这个类有点类似工厂类,它的作用是用来构造与GIT交互所需要的命令类(例如CloneCommand、InitCommand等等)。

image.png

第三个,GitCommand类。它是所有命令类(例如CloneCommand、InitCommand等等)的父类。 它对所有GIT命令类做了一些约束,最重要的是下面几个:

  1. 所有GIT命令类都需要有对应的GIT仓库
  2. 所有GIT命令类都需要实现Callable接口
  3. 所有GIT命令类都需要call方法,这个方法表示对应GIT命令的执行

第四个,Transport 类,该类用来链接两个仓库。比如从远程仓库FETCH到本地仓库,或者从本地仓库PUSH到远程仓库。它的实现类有非常多,比如 TransportHttp(使用HTTP连接)、TransportSftp(使用SFTP连接)。

image.png

2. 知识点

2.1 AutoCloseable 的使用

AutoCloseable是一个接口,表示一种约束:在一些资源需要释放的类中,可以实现这个类来完成资源的释放。如果实现了这个类,在一个try-with-resources模块中,当程序执行完模块中的内容时,就会自动调用AutoCloseable定义的close方法来完成资源的释放。

什么是try-with-resources模块? 举个例子,以前写读取文件内容的方法我们会这样写,需要主动去调用 BufferedReader 的 close方法进行资源的释放(不释放,如果短时间大量的连接过来,但是资源不释放可能会导致内存被占满、线程池被用完的情况)

public static String readFirstLineFromFile(String path) throws IOException {
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader(path));
        return br.readLine();
    }catch (IOException e){
        e.printStackTrace();
    }finally {
        if(null != br){
            br.close();
        }
    }
    return null;
}

但是Java7之后,我们不需要这么麻烦去写finally模块,只需要使用下面的try-with-resource模板。它会在模板内部代码执行完成之后,自动去调用close方法。

public static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
      }
}

try-with-resource的方式其实类似语法糖,上面的代码进行一下反编译,最后其实也是使用try-catch-finally这种方式来实现的

image.png

2.2 WeakReference 的使用

在Transport类中,有对WeakReference的使用。使用WeakReference来包裹TransportProtocol对象,表示这个是一个弱引用的对象。

private static final List<WeakReference<TransportProtocol>> protocols =
		new CopyOnWriteArrayList<>();

引用就是类似C语言中的指针,这个跟垃圾回收有非常大的关系,垃圾回收机制中判断一块内存区域是否需要被回收的依据就是这块内存区域是否有引用存在,如果有就表示内存区域还在用,不能回收。 在Java 1.2 之前,并没有对引用做类型的区分,但是在1.2之后,就开始区分强引用、软引用、弱引用和虚引用。做区分的原因,我觉得是非常可以理解的,毕竟现实生活都不是非黑即白的,程序是生活的映射,引用也是有强弱之分的。

强引用:正常我们声明的对象都是强引用。只要一块区域被强引用引用,即使内存不足的情况下,垃圾回收器也不会回收强引用引用的区域。

Object c = new Object();

软引用:比强引用更加弱的引用。当内存不够的时候,垃圾回收机制会考虑回收软引用引用的内存区域。软引用的使用场景通常是一些缓存的情况:比如做一些图片缓存、网页缓存的情况。

弱引用:比软引用更加弱的引用。不管内存是否充足,只要垃圾回收发起,弱引用引用的区域都会被回收。当时因为垃圾回收不是每时每刻都在进行,所以弱引用引用的区域也会有一定长度的生命周期(两次垃圾回收的间隔期)。上面Transport类中 protocols 这个列表内的对象就是弱引用对象,表示这个list中的对象随时可以被回收。 注意下面的注释,它说,如果需要保证弱引用不被垃圾回收,需要使用静态单例模式来保证它的生命周期。

image.png

image.png

还有一个虚引用,这个几乎引用比弱引用还有弱,使用场景我还不太清楚

比较强>软>弱的引用的区别,可以跑一下下面的测试代码

package com.lkb.channel.integrate.test;


import java.lang.ref.SoftReference;

class MyObject{
    @Override
    protected void finalize() throws Throwable {
        System.out.println("MyObject finalize...");
    }
}

/**
 * -Xms3m -Xmx3m
 */
public class MyTest {

    public static void main(String[] args) throws InterruptedException {
        // 强引用,即使内存不够也不会进行强引用引用对象的回收
//        MyObject myObject = new MyObject();
//        System.out.println("内存够用的情况," + myObject);
//
//        try{
//            byte[] bytes = new byte[1024*1024*2];
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            System.gc();
//            Thread.sleep(1000);
//            System.out.println("内存不够用的情况," + myObject);
//        }

        // 软引用,在内存不够的情况下,会进行软引用引用对象的回收
        SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
        System.out.println("内存够用的情况," + softReference.get());

        try {
            System.gc();
            Thread.sleep(100);
            System.out.println("内存够用的情况," + softReference.get());
        }catch (Exception e){
            e.printStackTrace();
        }

        try{
            byte[] bytes = new byte[1024*1024*2];
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.gc();
            Thread.sleep(100);
            System.out.println("内存不够用的情况," + softReference.get());
        }


        // 弱引用,每次GC都会对弱引用对象进行垃圾回收
//        WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
//        System.out.println("内存够用的情况," + weakReference.get());
//
//        try{
//            System.gc();
//            System.out.println("内存够用的情况," + weakReference.get());
//        }catch (Exception e){
//            e.printStackTrace();
//        }
    }
}

2.3 工厂模式

就是创建对象不用我们亲自创建,而是通过一个工厂类来做创建对象这个事情,我们只需要从工厂类中就可以获取所需要的对象。具体参考菜鸟教程

www.runoob.com/design-patt…

上面的Git类就是一个典型的工厂类,从里面我们可以获取所有跟git仓库交互的命令类。