栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java代码动态编译,动态注入功能的实际应用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java代码动态编译,动态注入功能的实际应用

为了针对日益严峻的跨系统传输异常,决定开发po+mq合体的中间件。在po功能的扩展上,针对请求参数、返回参数进行转换扩展。传统项目针对新加入的接口进行抽象处理,再进行转换,在服务发布的时间差内,会造成请求失败,数据丢失。考虑到中间件的稳定性,为了解决这个问题,我决定加入动态编译,动态注入,卸载的功能。每个接口都可以配置一个实现的子类,来满足异构数据的处理工作。如果有业务场景要求不能重启服务,大家可以参考一下我的做法!!! 下面聊一下主要实现逻辑:

一、首先新建抽象工厂类HttpFactoryService,定义两个抽象方法,也加入了xml转对象,对象转xml的方法,还有http请求方法:

package com.asd.po.rest;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.asd.po.rest.bean.HttpRequestBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;


public abstract class HttpFactoryService {

    public abstract HttpRequestBean req(HttpRequestBean httpRequestBean);

    public abstract ResponseEntity resp(ResponseEntity  obj);



    
    protected  T  convertToObj(String xml,Class clazz) throws JAXBException {
        StringReader reader = new StringReader(xml);
        JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        T obj = (T)jaxbUnmarshaller.unmarshal(reader);
        return obj;
    }

    
    protected String convertToXML(Object obj) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());
        StringWriter writer = new StringWriter();
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.marshal(obj, writer);
        String xmlStr = writer.toString();
        xmlStr = StringUtils.replace(xmlStr, """, "'");
        return xmlStr;
    }

    protected  T request(String url, Map headers , Map formMap, String bodyJson, Method method, Class clazz) {
        String result = HttpRequest.post(url)
                .headerMap(headers, true)
                .form(formMap)
                .body(bodyJson)
                .timeout(600000)//超时,毫秒
                .method(method)
                .execute().body();
        return JSON.parseObject(result, clazz);
    }

    protected  T request(String url,String bodyJson, Method method, Class clazz) {
        String result = HttpRequest.post(url)
                .body(bodyJson)
                .timeout(600000)//超时,毫秒
                .method(method)
                .execute().body();
        return JSON.parseObject(result, clazz);
    }

    protected JSonObject request(String url, String bodyJson, Method method) {
        String result = HttpRequest.post(url)
                .body(bodyJson)
                .timeout(600000)//超时,毫秒
                .method(method)
                .execute().body();
        return JSON.parseObject(result);
    }


}

二、请求抽象方法的参数bean HttpRequestBean:

package com.asd.po.rest.bean;

import lombok.Builder;
import lombok.Data;
import java.util.Map;


@Data
@Builder
public class HttpRequestBean {
    private String requestBody ;
    private Map headersMap;
    private Map params;
    //参考号
    private String refNo;
    //请求方法
    private String method;
    private String url;
}

构建调度服务DistanceSpringContext:

package com.asd.po.rest;

import com.asd.po.entity.InterfaceConfig;
import com.asd.po.rest.bean.HttpRequestBean;
import com.asd.po.util.ClassUtils;
import com.asd.po.util.CustomJavaCompiler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;



@Slf4j
@Service
public class DistanceSpringContext {


    private final Map map = new ConcurrentHashMap<>();

    public void DistanceSpringContext(List list) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        for (Class c : ClassUtils.getAllAssignedClass(HttpFactoryService.class)) {
            map.put(ClassUtils.getBeanId(c), (HttpFactoryService) c.newInstance());
        }
        for (InterfaceConfig e : list) {
            CustomJavaCompiler compiler = new CustomJavaCompiler(e.getJavaClassCode());
            boolean res = compiler.compiler();
            if (!res) {
                continue;
            }
            log.info("compilerSuccess:{},compilerTime:{}", compiler.getCompilerMessage(), compiler.getCompilerTime());
            Class clz = compiler.getCompilerClass();
            map.put(ClassUtils.getBeanId(clz), (HttpFactoryService) clz.newInstance());
        }
    }

    public HttpRequestBean setReq(String serviceName, HttpRequestBean httpRequestBean) {
        HttpFactoryService obj = map.get(serviceName);
        return obj.req(httpRequestBean);
    }

    public ResponseEntity setResp(String serviceName, ResponseEntity respObj) {
        HttpFactoryService obj = map.get(serviceName);
        return obj.resp(respObj);
    }

    
    public void register(String javaClassCode) {
        CustomJavaCompiler compiler = new CustomJavaCompiler(javaClassCode);
        boolean res = compiler.compiler();
        if (!res) {
            return;
        }
        log.info("compilerSuccess:{},compilerTime:{}", compiler.getCompilerMessage(), compiler.getCompilerTime());
        Class clz = compiler.getCompilerClass();
        try {
            map.put(ClassUtils.getBeanId(clz), (HttpFactoryService) clz.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    
    public void cancel(String beanId) {
        map.remove(beanId);
    }

}

三、下面是java代码动态编译器CustomJavaCompiler

package com.asd.po.util;

import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class CustomJavaCompiler {
    //源码
    private String sourceCode;
    //类全名
    private String fullClassName;
    //获取java的编译器
    private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    //存放编译之后的字节码(key:类全名,value:编译之后输出的字节码)
    private Map javaFileObjectMap = new ConcurrentHashMap<>();
    //存放编译过程中输出的信息
    private DiagnosticCollector diagnosticsCollector = new DiagnosticCollector<>();
    //编译耗时(单位ms)
    private long compilerTime;

    public CustomJavaCompiler(String sourceCode) {
        this.sourceCode = sourceCode;
        this.fullClassName = getFullClassName(sourceCode);
    }

    
    public boolean compiler() {
        if(compiler == null)
            return false;

        long startTime = System.currentTimeMillis();
        //标准的内容管理器,更换成自己的实现,覆盖部分方法
        StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
        JavaFileManager javaFileManager = new StringJavaFileManage(standardFileManager);
        //构造源代码对象
        JavaFileObject javaFileObject = new StringJavaFileObject(fullClassName, sourceCode);
        //获取一个编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, javaFileManager, diagnosticsCollector, null, null, Arrays.asList(javaFileObject));
        //设置编译耗时
        compilerTime = System.currentTimeMillis() - startTime;
        return task.call();
    }

    
    public Class getCompilerClass() {
        StringClassLoader scl = new StringClassLoader();
        Class clz = null;
        try {
            clz = scl.findClass(fullClassName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return clz;
    }

    
    public String getCompilerMessage() {
        if(compiler == null)
            return "JRE环境未配置(请复制JDK路径下lib目录内的tools.jar到JRE路径下lib目录里)";

        StringBuilder sb = new StringBuilder();
        List> diagnostics = diagnosticsCollector.getDiagnostics();
        for (Diagnostic diagnostic : diagnostics) {
            sb.append(diagnostic.toString()).append("rn");
        }
        return sb.toString();
    }

    public long getCompilerTime() {
        return compilerTime;
    }

    
    public static String getFullClassName(String sourceCode) {
        String className = "";
        Pattern pattern = Pattern.compile("package\s+\S+\s*;");
        Matcher matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";
        }

        pattern = Pattern.compile("class(\s.*?\s)[extends|\{]");
        matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            className += matcher.group(1).replace("extends","").replace("HttpFactoryService","").trim();
        }
        return className;
    }

    
    private class StringJavaFileObject extends SimpleJavaFileObject {
        //等待编译的源码字段
        private String contents;

        //java源代码 => StringJavaFileObject对象 的时候使用
        public StringJavaFileObject(String className, String contents) {
            super(URI.create("string:///" + className.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
            this.contents = contents;
        }

        //字符串源码会调用该方法
        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            return contents;
        }

    }

    
    private class ByteJavaFileObject extends SimpleJavaFileObject {
        //存放编译后的字节码
        private ByteArrayOutputStream outPutStream;

        public ByteJavaFileObject(String className, Kind kind) {
            super(URI.create("string:///" + className.replaceAll("\.", "/") + Kind.SOURCE.extension), kind);
        }
        @Override
        public OutputStream openOutputStream() {
            outPutStream = new ByteArrayOutputStream();
            return outPutStream;
        }

        //在类加载器加载的时候需要用到
        public byte[] getCompiledBytes() {
            return outPutStream.toByteArray();
        }
    }

    
    private class StringJavaFileManage extends ForwardingJavaFileManager {
        StringJavaFileManage(JavaFileManager fileManager) {
            super(fileManager);
        }

        //获取输出的文件对象,它表示给定位置处指定类型的指定类
        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
            ByteJavaFileObject javaFileObject = new ByteJavaFileObject(className, kind);
            javaFileObjectMap.put(className, javaFileObject);
            return javaFileObject;
        }
    }

    
    private class StringClassLoader extends ClassLoader {
        @Override
        protected Class findClass(String name) throws ClassNotFoundException {
            ByteJavaFileObject fileObject = javaFileObjectMap.get(name);
            if (fileObject != null) {
                byte[] bytes = fileObject.getCompiledBytes();
                return defineClass(name, bytes, 0, bytes.length);
            }
            try {
                return ClassLoader.getSystemClassLoader().loadClass(name);
            } catch (Exception e) {
                return super.findClass(name);
            }
        }
    }

    public static  T invokeMethod(Object object, String methodName, Class[] classes, Object... args)
            throws Exception {
        Method method = object.getClass().getMethod(methodName, classes);
        return (T) method.invoke(object, args);
    }
}

四、抽象方法的子类TestHttp,这个可以存在数据库里面,通过订阅发布,动态新增变更各个节点的服务,动态编译、注入、卸载。

package com.asd.po.rest.extend;

import com.asd.po.rest.HttpFactoryService;
import com.asd.po.rest.bean.HttpRequestBean;
import org.springframework.http.ResponseEntity;


public class TestHttp extends HttpFactoryService {
    @Override
    public HttpRequestBean req(HttpRequestBean httpRequestBean) {
        httpRequestBean.setUrl("http://127.0.0.1:8080/publish/custom");
        httpRequestBean.setMethod("POST");
        return httpRequestBean;
    }

    @Override
    public ResponseEntity resp(ResponseEntity obj) {
        return obj;
    }
}

五、下面看一下数据里面的配置数据,TestHttp继承的子类逻辑可以动态修改新增,不需要重启服务,服务重启后会自动读取配置数据,自动编译注入实现类

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/605500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号