maven 配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jfinal.plugins</groupId> <artifactId>xxljob</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.jfinal</groupId> <artifactId>jfinal</artifactId> <version>4.9.03</version> </dependency> <dependency> <groupId>com.jfinal</groupId> <artifactId>jfinal-undertow</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.3.0</version> </dependency> </dependencies> </project>
xxl-job-executor.properties
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02" xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin ### xxl-job, access token xxl.job.accessToken= ### xxl-job executor appname xxl.job.executor.appname=xxl-job-executor-sample ### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null xxl.job.executor.address= ### xxl-job executor server-info xxl.job.executor.ip=127.0.0.1 xxl.job.executor.port=18000 ### xxl-job executor log-path xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler ### xxl-job executor log-retention-days xxl.job.executor.logretentiondays=30
java代码:扫描借助波总的 PathScanner 代码。直接复制黏贴。
package com.jfinal.plugins;
import java.lang.annotation.*;
/**
* @author zhangshiqiang on 2021-05-31.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface XxlJobRegister {
}package com.jfinal.plugins;
import com.jfinal.core.PathScanner;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.IPlugin;
import com.xxl.job.core.executor.impl.XxlJobSimpleExecutor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* @author zhangshiqiang on 2021-05-30.
*/
public class XxlJobPlugin implements IPlugin {
private static final Prop config = PropKit.use("xxl-job-executor.properties");
private XxlJobSimpleExecutor xxlJobSimpleExecutor;
@Override
public boolean start() {
XxlJobSimpleExecutor xxlJobSimpleExecutor = new XxlJobSimpleExecutor();
xxlJobSimpleExecutor.setAdminAddresses(config.get("xxl.job.admin.addresses"));
xxlJobSimpleExecutor.setAppname(config.get("xxl.job.executor.appname"));
xxlJobSimpleExecutor.setIp(config.get("xxl.job.executor.ip"));
xxlJobSimpleExecutor.setPort(config.getInt("xxl.job.executor.port", 0));
xxlJobSimpleExecutor.setAccessToken(config.get("xxl.job.accessToken"));
xxlJobSimpleExecutor.setLogPath(config.get("xxl.job.executor.logpath"));
xxlJobSimpleExecutor.setLogRetentionDays(config.getInt("xxl.job.executor.logretentiondays", 30));
// 扫描注解类
try {
List<Object> xxlJobBeanList = xxlJobBeanList();
xxlJobSimpleExecutor.setXxlJobBeanList(xxlJobBeanList);
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
xxlJobSimpleExecutor.start();
this.xxlJobSimpleExecutor = xxlJobSimpleExecutor;
return true;
}
@Override
public boolean stop() {
if (xxlJobSimpleExecutor != null) {
xxlJobSimpleExecutor.destroy();
}
return true;
}
private ClassLoader getClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
return ret != null ? ret : XxlJobPlugin.class.getClassLoader();
}
private List<Object> xxlJobBeanList() throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
List<Object> list = new ArrayList<>();
Enumeration<URL> urls = getClassLoader().getResources("");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if ("jar".equals(url.getProtocol())) {
scanJar(url, list);
} else if ("file".equals(url.getProtocol())) {
File file = new File(url.getPath());
String classPath = getClassPath(file);
scanFile(file, classPath, list);
}
}
return list;
}
private String getClassPath(File file) {
String ret = file.getAbsolutePath();
// 添加后缀,以便后续的 indexOf(bp) 可以正确获得下标值,因为 bp 确定有后缀
if (!ret.endsWith(File.separator)) {
ret = ret + File.separator;
}
return ret;
}
private void scanFile(File file, String classPath, List<Object> list) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File fi : files) {
scanFile(fi, classPath, list);
}
}
} else if (file.isFile()) {
String fullName = file.getAbsolutePath();
if (fullName != null && fullName.endsWith(".class")) {
String className = fullName.substring(classPath.length(), fullName.length() - 6).replace(File.separatorChar, '.');
Class<?> aClass = getClassLoader().loadClass(className);
if (aClass.getAnnotation(XxlJobRegister.class) != null && list.stream().noneMatch(s -> s.getClass() == aClass)) {
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
list.add(declaredConstructor.newInstance());
}
}
}
}
private void scanJar(URL url, List<Object> list) throws IOException {
URLConnection urlConn = url.openConnection();
if (urlConn instanceof JarURLConnection) {
JarURLConnection jarUrlConn = (JarURLConnection) urlConn;
try (JarFile jarFile = jarUrlConn.getJarFile()) {
Enumeration<JarEntry> jarFileEntries = jarFile.entries();
while (jarFileEntries.hasMoreElements()) {
JarEntry jarEntry = jarFileEntries.nextElement();
String en = jarEntry.getName();
// 只扫描 basePackage 之下的类
if (en.endsWith(".class")) {
// JarEntry.getName() 返回值中的路径分隔符在所有操作系统下都是 '/'
en = en.substring(0, en.length() - 6).replace('/', '.');
Class<?> aClass = getClassLoader().loadClass(en);
if (aClass.getAnnotation(XxlJobRegister.class) != null && list.stream().noneMatch(s -> s.getClass() == aClass)) {
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
list.add(declaredConstructor.newInstance());
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}测试代码
package com.jfinal.schedule;
import com.jfinal.plugins.XxlJobRegister;
import com.xxl.job.core.handler.annotation.XxlJob;
/**
* @author zhangshiqiang on 2021-05-31.
*/
@XxlJobRegister
public class TestJfinal {
@XxlJob("test")
public void test() {
System.out.println("测试jfinal定时器");
}
}
JarFile jarFile = new JarFile(lib);