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);