JAVA通过注解实现文件监听

定义注解

package jelly.command.watcher.annotions;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FileWatcherListener {
    String[] value();
}

注解信息实体类

package jelly.command.watcher.bean;

import java.lang.reflect.Method;

public class WatcherInfo {
    private Object clazz;
    private Method method;

    public WatcherInfo(Object clazz, Method member) {
        this.clazz = clazz;
        this.method = member;
    }

    public Object getClazz() {
        return clazz;
    }

    public void setClazz(Object clazz) {
        this.clazz = clazz;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "WatcherInfo{" +
                "bean=" + clazz +
                ", method=" + method +
                '}';
    }
}

注册监听

package jelly.command.watcher.core;

import jelly.command.watcher.bean.WatcherInfo;

import java.util.*;

public class FileWatcherRegister {

    private static final Map<String, List<WatcherInfo>> watcherInfoMap = new HashMap<>();

    public static void register(String path, WatcherInfo watcherInfo) {
        List<WatcherInfo> watcherInfoList = watcherInfoMap.get(path);
        if (watcherInfoList == null) {
            watcherInfoList = new ArrayList<>();
        }
        watcherInfoList.add(watcherInfo);
        watcherInfoMap.put(path,watcherInfoList);
    }

    public static Map<String, List<WatcherInfo>> getWatcherInfoMap() {
        return watcherInfoMap;
    }
}

监听核心类

package jelly.command.watcher.core;

import jelly.command.api.util.CyclicTask;
import jelly.command.watcher.observe.FileSubject;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;

/**
 * 文件监听任务,当监听到有新的文件创建时,通过主题通知所有已注册的观察者
 */
public class FileWatcherTask extends CyclicTask {
    private static final Logger LOG = LoggerFactory.getLogger(FileWatcherTask.class);
    private final String parentDirPath;
    private WatchService watchService;
    private final FileSubject observable;

    public FileWatcherTask(String parentDirPath, FileSubject observable) {
        super(1);
        this.parentDirPath = parentDirPath;
        this.observable = observable;
        try {
            this.watchService = FileSystems.getDefault().newWatchService();
            Paths.get(parentDirPath).register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
        } catch (IOException e) {
            LOG.error("Failed to init watchService: " + e.getMessage());
        }
    }

    @Override
    public void runActualTask() {
        WatchKey key;
        try {
            LOG.debug("watchService  is waiting for {}", parentDirPath);
            key = watchService.take();
            LOG.debug("watchService take from {} :{} ", parentDirPath, key.toString());
            for (WatchEvent<?> event : key.pollEvents()) {
                String filePath = parentDirPath + File.separator + event.context();
                LOG.debug("Found new File:{}", filePath);
                File file = FileUtils.getFile(filePath);
                observable.makeChanged(file);
            }
            boolean isKeyValid = key.reset();
            if (!isKeyValid) {
                LOG.error("key is inValid");
                terminate();
            }
        } catch (InterruptedException e) {
            LOG.error("runActualTask error : " + e.getMessage());
        }
    }
}

获取监听对象及方法并完成注册

package jelly.command.watcher.load;

import jelly.command.watcher.annotions.FileWatcherListener;
import jelly.command.watcher.bean.WatcherInfo;
import jelly.command.watcher.core.FileWatcherRegister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Set;

@Component
public class WatcherLoader implements BeanPostProcessor {
    private static final Logger LOG = LoggerFactory.getLogger(WatcherLoader.class);

    private final Environment environment;

    public WatcherLoader(Environment environment) {
        this.environment = environment;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        Set<Method> configMembers = MethodIntrospector.selectMethods(targetClass, (ReflectionUtils.MethodFilter) method -> method.getAnnotation(FileWatcherListener.class) != null);
        for (Method member : configMembers) {
            FileWatcherListener centerConfig = member.getAnnotation(FileWatcherListener.class);
            String[] value = centerConfig.value();
            for (String path : value) {
                path = isDynamicProperties(path);
                LOG.debug("FileWatcher register : path 【{}】, beanName 【{}】, method 【{}】", path, bean.getClass().getName(), member.getName());
                FileWatcherRegister.register(path, new WatcherInfo(bean, member));
            }
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    private String isDynamicProperties(String path) {
        if (path.startsWith("{") && path.endsWith("}")) {
            path = environment.getProperty(path.replace("{", "").replace("}", ""));
        }
        return path;
    }
}

通过观察者模式通知

package jelly.command.watcher.observe;

import jelly.command.watcher.bean.WatcherInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

/**
 * 当监听的文件夹中有新创建文件时,被通知;
 * 通过反射执行每个监听方法,且均为独立子线程,互不阻塞。
 */
public class FileObserver implements Observer {
    private static final Logger LOG = LoggerFactory.getLogger(FileObserver.class);
    private final List<WatcherInfo> watcherInfoList;

    public FileObserver(List<WatcherInfo> watcherInfoList) {
        this.watcherInfoList = watcherInfoList;
    }

    @Override
    public void update(Observable observable, Object o) {
        if (!(o instanceof File)) {
            return;
        }
        File file = (File) o;
        for (WatcherInfo watcherInfo : watcherInfoList) {
            Object bean = watcherInfo.getClazz();
            Method method = watcherInfo.getMethod();

            new Thread(() -> {
                try {
                    method.invoke(bean, file);
                    LOG.debug("exe method : {}#{}",bean.getClass().getName(),method.getName());
                } catch (IllegalAccessException | InvocationTargetException e) {
                    LOG.error("exe method error: {}",e.getMessage());
                }
            }).start();
        }
    }
}

package jelly.command.watcher.observe;

import java.io.File;
import java.util.Observable;

public class FileSubject extends Observable {

    public void makeChanged(File file){
        setChanged();
        notifyObservers(file);
    }
}

初始化

package cetc54.command.watcher.runner;

import jelly.command.watcher.bean.WatcherInfo;
import jelly.command.watcher.core.FileWatcherTask;
import jelly.command.watcher.core.FileWatcherRegister;
import jelly.command.watcher.observe.FileObserver;
import jelly.command.watcher.observe.FileSubject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *  在项目启动加载完成之后,根据已注册的路径,创建文件监听任务
 */
@Component
public class WatcherRunner implements CommandLineRunner {
    private static final Logger LOG = LoggerFactory.getLogger(WatcherRunner.class);
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    @Override
    public void run(String... args) throws Exception {
        Map<String, List<WatcherInfo>> watcherInfoMap = FileWatcherRegister.getWatcherInfoMap();
        LOG.debug("watcherInfoMap is {}",watcherInfoMap.toString());
        watcherInfoMap.forEach((path, watcherInfoList) -> {
            FileSubject fileSubject = new FileSubject();
            FileObserver fileObserver = new FileObserver(watcherInfoList);
            LOG.debug("{} add fileObserver:{}",path,watcherInfoList.toString());
            fileSubject.addObserver(fileObserver);
            FileWatcherTask fileWatcherTask = new FileWatcherTask(path, fileSubject);
            executorService.submit(fileWatcherTask);
        });
    }
}

测试类

import jelly.command.watcher.annotions.FileWatcherListener;
import org.springframework.stereotype.Component;

import java.io.File;

@Component
public class MyWatcherDemo {
   

    @FileWatcherListener({"{path}"})
    public void test01(File file) {
      
    }

    @FileWatcherListener({"D:\\temp\\"})
    public void test02(File file) {
       
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值