Java热插拔技术实现总结

简介: 在这篇文章中,我对Java如何实现热插拔技术进行了总结并编写了示例,对现有项目进行了分析,希望对您有帮助。
欢迎来到:clap: 阿提说说:clap:的博客。很高兴,您能看到我的文章。
在这篇文章中,我对Java如何实现热插拔技术进行了总结并编写了示例,对现有项目进行了分析,希望对您有帮助。

@[toc]

OSGI

OSGI介绍

OSGI全称为Open Service Gateway Initiative(开放服务网关规范),有两个层面的含义,一方面它指OSGi Alliance组织;另一方面指该组织制定的一个基于Java语言的服务(业务)规范——OSGi服务平台(Service Platform)。

一般Java开发人员所说的OSGI是指由OSGi Alliance组织制定的Java模块化规范,该规范的核心部分是一个框架,其中定义了应用程序的生命周期模式和服务注册。基于这个框架定义了大量的OSGi服务:日志、配置管理,HTTP服务(运行Servlet)、XML解析、设备访问、软件包管理、许可管理、用户管理、IO连接、Jini和UPnP等。

OSGI优缺点

使用OSGI构建Java应用优点比较明显,主要体现在以下几个方面:

1、基于OSGI的应用程序可动态更改运行状态和行为。在OSGI框架中,每一个Bundle实际上都是可热插拔的,因此,对一个特定的Bundle进行修改不会影响到容器中的所有应用,运行的大部分应用还是可以照常工作。当你将修改后的Bundle再部署上去的时候,容器从来没有重新启过。这种可动态更改状态的特性在一些及时性很强的系统中比较重要,尤其是在Java Web项目中,无需重启应用服务器就可以做到应用的更新。

2、OSGI是一个微核的系统,所谓微核是指其核心只有为数不多的几个jar包。基于OSGI框架的系统可分可合,其结构的优势性导致具体的Bundle不至于影响到全局,不会因为局部的错误导致全局系统的崩溃。例如Java EE项目中可能会因为某个Bean的定义或注入有问题,而导致整个应用跑不起来,而使用OSGI则不会有这种问题,顶多相关的几个Bundle无法启动。

3、可复用性强,OSGI框架本身可复用性极强,很容易构建真正面向接口的程序架构,每一个Bundle 都是一个独立可复用的单元。

使用OSGI的缺点如下:
1、每个Bundle都由单独的类加载器加载,与一些Java EE项目中使用比较多的框架整合比较困难,如Spring MVC、Struts2等,例如笔者尝试在OSGI应用中整合Spring MVC时,通过DispatcherServlet启动的Bean与OSGI Bundle启动的Bean无法相互依赖,需要做特殊处理,后面文章中会有介绍。

2、目前OSGI框架提供的管理端不够强大,现在的管理端中仅提供了基本的Bundle状态管理、日志查看等功能,像动态修改系统级别的配置(config.ini)、动态修改Bundle的配置(Manifest.mf)、启动级别等功能都尚未提供,而这些在实际的项目或产品中都是非常有必要的。

3、采用OSGI作为规范的模块开发、部署方式自然给现有开发人员提出了新的要求,需要学习新的基于OSGI的开发方式。

OSGI实例

在这里插入图片描述
PS: Target Platform选择 standard,标准模式,这样可运行的平台就多了
在这里插入图片描述
在这里插入图片描述
这里我用模版创建,选择Hello OSGI Bundle
在这里插入图片描述
输入s查看所有的Bundle
我这边有很多,只贴出了几个自定义的Bundle

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.17.100.v20211104-1730
                Fragments=139, 146
1    RESOLVED    ServiceOSGI_1.0.0.qualifier
2    RESOLVED    TestService_1.0.0.qualifier
3    RESOLVED    HelloOSGI_1.0.0.qualifier

输入start 3 启动HelloOSGI

osgi> start 3
Hello World!!

现在修改输出为"Hello OSGI!!!"

public class Activator implements BundleActivator {
    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Hello OSGI!!");
    }
    
    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Goodbye OSGI!!");
    }
}

再启动一下

osgi> stop 3
Goodbye OSGI!!
osgi> start 3
Hello OSGI!!

Groovy脚本

ScriptEngineManager

按照JSR223,使用标准接口ScriptEngineManager调用。

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");// 每次生成一个engine实例
Bindings binding = engine.createBindings();
binding.put("date", new Date()); // 入参
engine.eval("def getTime(){return date.getTime();}", binding);
// 如果script文本来自文件,请首先获取文件内容
//engine.eval(new FileReader(new File("/Users/chenjujun/java-projects/Java-Test/src/test/java/script/test.groovy")));
engine.eval("def sayHello(name,age){return 'Hello,I am ' + name + ',age' + age;}");
Long time = (Long) ((Invocable) engine).invokeFunction("getTime", null);// 反射到方法
System.out.println(time);
String message = (String) ((Invocable) engine).invokeFunction("sayHello", "zhangsan", 12);
System.out.println(message);

GroovyShell

Groovy官方提供GroovyShell,执行Groovy脚本片段,GroovyShell每一次执行时代码时会动态将代码编译成Java Class,然后生成Java对象在Java虚拟机上执行,所以如果使用GroovyShell会造成Class太多,性能较差。

final String script = "Runtime.getRuntime().availableProcessors()";
Binding intBinding = new Binding();
GroovyShell shell = new GroovyShell(intBinding);
final Object eval = shell.evaluate(script);
System.out.println(eval);

GroovyClassLoader

Groovy官方提供GroovyClassLoader类,支持从文件、url或字符串中加载解析Groovy Class,实例化对象,反射调用指定方法。

GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
  String helloScript = "package com.vivo.groovy.util" +  // 可以是纯Java代码
          "class Hello {" +
            "String say(String name) {" +
              "System.out.println(\"hello, \" + name)" +
              " return name;"
            "}" +
          "}";
Class helloClass = groovyClassLoader.parseClass(helloScript);
GroovyObject object = (GroovyObject) helloClass.newInstance();
Object ret = object.invokeMethod("say", "zhangshan"); 
System.out.println(ret.toString()); 

Spring

Spring常规方法

DeployUtil 工具类

@Slf4j
public class DeployUtils {
    /**
     * 读取jar包中所有类文件
     */
    public static Set<String> readJarFile(String jarAddress) throws IOException {
        Set<String> classNameSet = new HashSet<>();
        
        try(JarFile jarFile = new JarFile(jarAddress)) {
            Enumeration<JarEntry> entries = jarFile.entries();//遍历整个jar文件
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                if (name.endsWith(".class")) {
                    String className = name.replace(".class", "").replaceAll("/", ".");
                    classNameSet.add(className);
                }
            }
        } catch (Exception e) {
            log.warn("加载jar包失败", e);
        }
        return classNameSet;
    }
    /**
     * 方法描述 判断class对象是否带有spring的注解
     */
    public static boolean isSpringBeanClass(Class<?> cls) {
        if (cls == null) {
            return false;
        }
        //是否是接口
        if (cls.isInterface()) {
            return false;
        }
        //是否是抽象类
        if (Modifier.isAbstract(cls.getModifiers())) {
            return false;
        }
        //自定义注解
        if (cls.getAnnotation(Supplier.class) != null) {
            return true;
        }
        if (cls.getAnnotation(Component.class) != null) {
            return true;
        }
        if (cls.getAnnotation(Mapper.class) != null) {
            return true;
        }
        if (cls.getAnnotation(Service.class) != null) {
            return true;
        }
        return false;
    }
    
    
    public static boolean isController(Class<?> cls) {
        if (cls.getAnnotation(Controller.class) != null) {
            return true;
        }
        if (cls.getAnnotation(RestController.class) != null) {
            return true;
        }
        return false;
    }
    
    /**
     * 类名首字母小写 作为spring容器beanMap的key
     */
    public static String transformName(String className) {
        String tmpstr = className.substring(className.lastIndexOf(".") + 1);
        return tmpstr.substring(0, 1).toLowerCase() + tmpstr.substring(1);
    }
}

注册bean

public void register(String pluginId) {

        Method detectHandlerMethods = null;
        Method unregisterMapping = null;
        
        try {
            detectHandlerMethods = ReflectUtils.findDeclaredMethod(requestMappingHandlerMapping.getClass(),
                    "detectHandlerMethods", new Class[] { Object.class });
            unregisterMapping = ReflectUtils.findDeclaredMethod(requestMappingHandlerMapping.getClass(),
                    "unregisterMapping", new Class[] { Object.class });
            
            String jarPath = "/Users/chenjujun/greentown-projects/aio-edge/aio-edge-third-plugins/device/plugin-device-md/target/plugin-device-md-1.0.4.jar";
            Set<String> classNames = DeployUtils.readJarFile(jarPath);

            URL jarURL = new File(jarPath).toURI().toURL();
            
            URLClassLoader classLoader = new URLClassLoader(new URL[] { jarURL }, Thread.currentThread().getContextClassLoader());

            for (String className : classNames) {
                Class clazz = classLoader.loadClass(className);
                if (DeployUtils.isSpringBeanClass(clazz)) {
                    ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
                    BeanDefinitionRegistry beanDefinitonRegistry = (BeanDefinitionRegistry) configurableApplicationContext
                            .getBeanFactory();

                    BeanDefinitionBuilder usersBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
                    usersBeanDefinitionBuilder.setScope("singleton");
                    beanDefinitonRegistry.registerBeanDefinition(DeployUtils.transformName(className),
                            usersBeanDefinitionBuilder.getRawBeanDefinition());
                    
                    //注册接口
                    if (Boolean.TRUE.equals(DeployUtils.isController(clazz))) {
                        try {
                            detectHandlerMethods.setAccessible(true);
                            detectHandlerMethods.invoke(requestMappingHandlerMapping, new Object[] { DeployUtils.transformName(className) });
                        } catch (Exception e) {
                            //移除bean
                            beanDefinitonRegistry.removeBeanDefinition(DeployUtils.transformName(className));
                        }
                    }
                
                }    
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Spring Brick

介绍

"Spring-brick",是一个可以动态扩展系统的框架,最早在2019年开始开发,该框架可以在SpringBoot项目上开发插件功能,开发插件就像开发独立应用一样,根据网站的介绍,使用该框架可以实现如下需求:

  • 在插件中,您可以当成一个微型的SpringBoot项目来开发,简单易用。
  • 在插件中扩展出系统各种功能点,用于系统灵活扩展,再也不用使用分支来交付不同需求的项目了。
  • 在插件中可以集成各种框架及其各种spring-boot-xxx-starter。
  • 在插件中可以定义独立依赖包了,再也不用在主程序中定义依赖包了。
  • 可以完美解决插件包与插件包、插件包与主程序因为同一框架的不同版本冲突问题了。各个插件可以定义同一依赖的不同版本框架。
  • 无需重启主程序,可以自由实现插件包的动态安装部署,来动态扩展系统的功能。
  • 插件也可以不依赖主程序独立集成微服务模块。

项目更新蛮活跃的:

文档比较丰富:

快速入门

示例地址:
https://gitee.com/starblues/springboot-plugin-framework-example

作者提供了一个项目示例,我们来跑一下看看,跑之前看下文档的快速入门:

1、将sql/plugin-test-example.sql文件导入到mysql数据库
2、修改各个模块对数据库连接配置信息,插件目录地址
3、打包插件: mvn clean package
4、进入主程序example-main 启动 Application

运行日志:


  .   ____   _     __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::  (v2.5.6)

 [Thread-1] c.g.s.l.launcher.SpringMainBootstrap     : Starting SpringMainBootstrap using Java 1.8.0_311 on MacBookAir.local with PID 21618 (/Users/chenjujun/java-projects/springboot-plugin-framework-parent/spring-brick-loader/target/classes started by apple in /Users/chenjujun/java-projects/springboot-plugin-framework-example/example-main)
 [Thread-1] c.g.s.l.launcher.SpringMainBootstrap     : The following profiles are active: dev
 [Thread-1] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
 [Thread-1] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
 [Thread-1] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.54]
 [Thread-1] o.a.c.c.C.[Tomcat].[localhost].[/]: Initializing Spring embedded WebApplicationContext
 [Thread-1] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1571 ms
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Parsed mapper file: 'file [/Users/chenjujun/java-projects/springboot-plugin-framework-example/example-main/target/classes/mapper/MainUserMapper.xml]'
 _ _   |_  _ _|_. ___ _ |    _ 
| | |\/|_)(_| | |_\  |_)||_|_\ 
     / |  
   3.4.1 
 [Thread-1] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2ControllerWebMvc#getDocumentation(String, HttpServletRequest)]
 [Thread-1] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
 [Thread-1] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
 [Thread-1] d.s.w.p.DocumentationPluginsBootstrapper : Documentation plugins bootstrapped
 [Thread-1] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
 [Thread-1] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanByInterfaceUsingGET_1
 [Thread-1] c.g.s.l.launcher.SpringMainBootstrap     : Started SpringMainBootstrap in 4.107 seconds (JVM running for 6.445)
 [Thread-1] c.g.s.i.operator.DefaultPluginOperator   : 插件加载环境: dev
 [Thread-1] c.g.s.i.operator.DefaultPluginOperator   : 开始加载插件, 插件根路径为: 
/Users/chenjujun/java-projects/springboot-plugin-framework-example/example-plugins-basic
 [Thread-1] c.g.s.s.web.PluginStaticResourceConfig   : 插件静态资源访问前缀配置为: /static-plugin/{pluginId}
 [Thread-1] c.g.s.core.PluginLauncherManager  : 插件[example-basic-2@1.0.0-SNAPSHOT]加载成功
 [Thread-1] c.g.s.example.listener.MyPluginListener  : 插件[example-basic-2]加载成功.
 [Thread-1] c.g.s.core.PluginLauncherManager  : 插件[example-basic-1@1.0.0]加载成功
 [Thread-1] c.g.s.example.listener.MyPluginListener  : 插件[example-basic-1]加载成功.
 [Thread-1] c.g.s.e.l.MyPluginInitializerListener    : 初始化之前
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-2]注册接口: {GET [/plugins/example-basic-2/hello/config]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-2]注册接口: {GET [/plugins/example-basic-2/hello]}
 [Thread-1] c.g.s.core.PluginLauncherManager  : 插件[example-basic-2@1.0.0-SNAPSHOT]启动成功
 [Thread-1] c.g.s.example.listener.MyPluginListener  : 插件[example-basic-2]启动成功
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/caller/test-param-map]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/caller/test]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/caller/test-param-list]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/caller/test-param]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/hello/name]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/hello]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/hello/config]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/main/main-user]}
 [Thread-1] c.g.s.b.p.web.PluginControllerProcessor  : 插件[example-basic-1]注册接口: {GET [/plugins/example-basic-1/thy]}
 [Thread-1] c.g.s.s.w.PluginStaticResourceResolver   : 插件[example-basic-1@1.0.0]配置的静态资源: classpath[[static/]], file[[]]
 [Thread-1] c.g.s.core.PluginLauncherManager  : 插件[example-basic-1@1.0.0]启动成功
 [Thread-1] c.g.s.example.listener.MyPluginListener  : 插件[example-basic-1]启动成功
 [Thread-1] d.s.w.p.DocumentationPluginsBootstrapper : Documentation plugins bootstrapped
 [Thread-1] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
 [Thread-1] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getExtractImplUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getExtractByInterClassUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getExtractByInterClassOfMainUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: helloUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: helloUsingGET_2
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getConfigUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: helloUsingGET_3
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: parseUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: backupPluginUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: installUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: startUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: stopUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: uninstallUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: uploadUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: verifyUsingPOST_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getPluginInfoUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanByInterfaceUsingGET_2
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getObjectWithAnnotationUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanNameUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanUsingGET_2
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanUsingGET_3
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanByInterfaceErrorUsingGET_1
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getBeanByInterfaceUsingGET_3
 [Thread-1] .d.s.w.r.o.CachingOperationNameGenerator : Generating unique operation named: getObjectWithAnnotationByPluginIdUsingGET_1
 [Thread-1] c.g.s.e.l.MyPluginInitializerListener    : 初始化完成
 [Thread-1] c.g.s.i.operator.DefaultPluginOperator   : 插件初始化完成

这是已经加载完成了。

试试怎么动态部署。
动态部署需要使用prod模式启动,毕竟是要模拟线上环境。
插件目录没有插件的时候是这样的:

[Thread-1] c.g.s.i.operator.DefaultPluginOperator   : 插件加载环境: prod
[Thread-1] c.g.s.i.operator.DefaultPluginOperator   : 开始加载插件, 插件根路径为: 
../plugins
[Thread-1] c.g.s.s.web.PluginStaticResourceConfig   : 插件静态资源访问前缀配置为: /static-plugin/{pluginId}
[Thread-1] c.g.s.core.PluginLauncherManager         : 以下路径未发现插件: 
../plugins
请检查路径是否合适.
请检查配置[plugin.runMode]是否合适.
请检查插件是否合法.

调用下插件上传安装接口:

返回成功了:

看下日志,已经安装成功了:

后面就可以跟调用插件的HTTP接口了。

调用流程

为了能够更好的理解,我整理了一个调用流程图

扩展

xxl-job动态定时任务

我们主要看Java相关的部分

public class ExecutorBizImpl implements ExecutorBiz {
    //省略....

    @Override
    public ReturnT<String> run(TriggerParam triggerParam) {
        //省略....

        //使用Groovy加载的Java代码
        } else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) {

            // valid old jobThread
            if (jobThread != null &&
                    !(jobThread.getHandler() instanceof GlueJobHandler
                        && ((GlueJobHandler) jobThread.getHandler()).getGlueUpdatetime()==triggerParam.getGlueUpdatetime() )) {
                // change handler or gluesource updated, need kill old thread
                removeOldReason = "change job source or glue type, and terminate the old job thread.";

                jobThread = null;
                jobHandler = null;
            }

            // valid handler
            if (jobHandler == null) {
                try {
                    //载入源码
                    IJobHandler originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getGlueSource());
                    jobHandler = new GlueJobHandler(originJobHandler, triggerParam.getGlueUpdatetime());
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    return new ReturnT<String>(ReturnT.FAIL_CODE, e.getMessage());
                }
            }
        } 
        //省略....

        // replace thread (new or exists invalid)
        if (jobThread == null) {
            jobThread = XxlJobExecutor.registJobThread(triggerParam.getJobId(), jobHandler, removeOldReason);
        }

        // push data to queue
        ReturnT<String> pushResult = jobThread.pushTriggerQueue(triggerParam);
        return pushResult;
    }
    //省略....

}

Glue工厂GlueFactory

    public IJobHandler loadNewInstance(String codeSource) throws Exception{
        if (codeSource!=null && codeSource.trim().length()>0) {
            Class<?> clazz = getCodeSourceClass(codeSource);
            if (clazz != null) {
                //实例化对象
                Object instance = clazz.newInstance();
                if (instance!=null) {
                    if (instance instanceof IJobHandler) {
                        //注入bean
                        this.injectService(instance);
                        return (IJobHandler) instance;
                    } else {
                        throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, "
                                + "cannot convert from instance["+ instance.getClass() +"] to IJobHandler");
                    }
                }
            }
        }
        throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, instance is null");
    }

SpringGlueFactory处理工厂

public void injectService(Object instance){
        if (instance==null) {
            return;
        }

        if (XxlJobSpringExecutor.getApplicationContext() == null) {
            return;
        }

        Field[] fields = instance.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }

            Object fieldBean = null;
            // with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired

            if (AnnotationUtils.getAnnotation(field, Resource.class) != null) {
                try {
                //根据指定bean名称获取
                    Resource resource = AnnotationUtils.getAnnotation(field, Resource.class);
                    if (resource.name()!=null && resource.name().length()>0){
                        fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(resource.name());
                    } else {
                    //根据字段名获取,默认bean名称
                        fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getName());
                    }
                } catch (Exception e) {
                }
                if (fieldBean==null ) {
                    //根据字段类型获取
                    fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType());
                }
            } else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) {
                Qualifier qualifier = AnnotationUtils.getAnnotation(field, Qualifier.class);
                if (qualifier!=null && qualifier.value()!=null && qualifier.value().length()>0) {
                    fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(qualifier.value());
                } else {
                    fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType());
                }
            }

            if (fieldBean!=null) {
                field.setAccessible(true);
                try {
                //注入spring bean
                    field.set(instance, fieldBean);
                } catch (IllegalArgumentException e) {
                    logger.error(e.getMessage(), e);
                } catch (IllegalAccessException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
    }
相关文章
|
1月前
|
安全 Java Apache
Java中的数据安全与隐私保护技术
Java中的数据安全与隐私保护技术
|
1天前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
7 1
|
5天前
|
Java
Java BasePooledObjectFactory 对象池化技术
Java BasePooledObjectFactory 对象池化技术
9 1
|
15天前
|
安全 Java
Java RMI技术详解与案例分析
在实际的银行系统中,当然还需要考虑安全性、事务性、持久性以及错误处理等多方面的因素,RMI的网络通信也需要在安全的网络环境下进行,以防止数据泄露或被篡改。你在应用中是怎么使用 RMI 的,欢迎关注威哥爱编程,一起交流一下哈。
133 4
|
5天前
|
存储 设计模式 安全
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
5 0
|
27天前
|
Java 运维
开发与运维技术问题之ava对象头压缩技术支持所有的Java垃圾回收器如何解决
开发与运维技术问题之ava对象头压缩技术支持所有的Java垃圾回收器如何解决
21 1
|
1月前
|
存储 安全 算法
Java中的数据脱敏与隐私保护技术
Java中的数据脱敏与隐私保护技术
|
1月前
|
存储 缓存 NoSQL
Java中的内存数据库与缓存技术
Java中的内存数据库与缓存技术
|
1月前
|
存储 算法 安全
实现Java应用的数据加密与解密技术
实现Java应用的数据加密与解密技术
|
1月前
|
存储 安全 算法
Java中的数据加密与数字签名技术
Java中的数据加密与数字签名技术