热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

spring4代理设计模式源码解析

手动模拟的动态代理不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,
手动模拟的动态代理

  不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

 

  自己模拟的动态代理:

  缺点:首先要生成文件

  缺点:动态编译文件 class

  缺点:需要一个URLclassloader

  模拟过程:

  Fille --(读取file,生成类的类对象)--> class(byte[]) --(通过类的类对象)-->得到对象 (可以是 clazz.newInstance();直接new 或者通过constructor.newInstance("invoke ");构造方法new 出 )

 

1、手动创建handle接口

public interface InterfaceHandle {public Object invoke(Method method, Object[] args);
}

 

2、实现handle接口

public class User_InvoctionHandle implements InterfaceHandle {private Object target;public User_InvoctionHandle(Object target) {this.target = target;}public Object invoke(Method method, Object[] args) {System.out.println("我是自定义invoke方法");try {return method.invoke(this.target, null);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}
}

 

3、手写代理类(附带逻辑重写)

public class ProxyUtil {public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {Object proxy = null;
// Class targetInfo = target.getClass().getInterfaces()[0];String tab = "\t";String line = "\n";String implName = targetInfo.getSimpleName();//创建java内容String javaContent = "";//packageString packageContent = "package com.proxy;" + line;//importClassString impPackageContent = "import "+handle.getClass().getName()+";"+line+"import java.lang.reflect.Method;"+line+"import "+targetInfo.getName()+";"+line;//创建类体String classContent = "public class $Proxy implements " + implName + " {" + line;//创建私有对象handleString privateObject = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;//创建构造,将handle当做对象String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;constructorContent += tab + tab + "this.h = h;";constructorContent += line + tab + "}" + line;//创建方法String methedContent = "";Method[] methods = targetInfo.getDeclaredMethods();for (Method method : methods) {//获取方法的返回类型String methodTypeName = method.getReturnType().getSimpleName();//获取方法的名字String methodName = method.getName();methedContent += tab + "public " + methodTypeName + " " + methodName + " (";//创建参数Object[] args = method.getParameterTypes();String argContent = "";for (int i = 0; i }

  

4、测试

public static void main(String args[]) {//调用手动写的代理ObjectDao objectDao = (ObjectDao) ProxyUtil.getInstance(ObjectDao.class, new User_InvoctionHandle(new User_Defined_Dao()));System.out.println(objectDao.queryStr());}

  

5、测试重生成的$Proxy代理类及class

自动生成代理类的编码

package com.dao;
import com.handle.User_InvoctionHandle;
import java.lang.reflect.Method;
import com.dao.ObjectDao;
public class $Proxy implements ObjectDao {private User_InvoctionHandle h;public $Proxy (User_InvoctionHandle h ){this.h = h;}public void query (){try{Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("query");h.invoke(method,null);}catch(Exception e){e.printStackTrace();}}public String queryStr (){try{Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("queryStr");return (String)h.invoke(method,null);}catch(Exception e){e.printStackTrace();}return null;}}

  

结果

 

 手写带参数动态代理

package com.proxy;/** 对象是如何生成的?* java* class* new* **/import com.handle.User_InvoctionHandle;import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLClassLoader;public class ProxyUtil {public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {Object proxy = null;
// Class targetInfo = target.getClass().getInterfaces()[0];String tab = "\t";String line = "\n";String implName = targetInfo.getSimpleName();//创建java内容String javaContent = "";//packageString packageContent = "package com.proxy;" + line;//importClassString impPackageContent = "import "+handle.getClass().getName()+";"+line+"import java.lang.reflect.Method;"+line+"import "+targetInfo.getName()+";"+line;//创建类体String classContent = "public class $Proxy implements " + implName + " {" + line;//创建私有对象handleString privateHandle = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;//创建构造,将handle当做对象String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;constructorContent += tab + tab + "this.h = h;";constructorContent += line + tab + "}" + line;//创建方法String methedContent = "";Method[] methods = targetInfo.getDeclaredMethods();for (Method method : methods) {//获取方法的返回类型String methodTypeName = method.getReturnType().getSimpleName();//获取方法的名字String methodName = method.getName();methedContent += tab + "public " + methodTypeName + " " + methodName + " (";//创建参数Object[] args = method.getParameterTypes();String argContent = "";String argClassContent = "";String argValue = "Object [] objects = new Object[]{";for (int i = 0; i // argClassContent+=",";}else{argValue+="};"+line;}}//组装方法内容methedContent += argContent + "){"+ line + tab + tab +"try{";//如果没有参数则不创建Objectsif(argContent!=null && !"".equals(argContent)){methedContent+=line+tab+tab+tab+argValue;}methedContent+= line + tab + tab + tab + "Method method = Class.forName(\""+targetInfo.getName()+"\").getDeclaredMethod(\""+method.getName()+"\"";methedContent+=argClassContent;methedContent+=");"+line ;if(!"void".equals(methodTypeName)){methedContent+= tab+tab+tab+"return ("+methodTypeName+")";}methedContent+= "h.invoke(method";if(argContent!=null && !"".equals(argContent)){methedContent+=",objects";}methedContent+= ");" + line+ tab + tab +"}catch(Exception e){"+ line + tab + tab + tab + "e.printStackTrace();"+ line + tab + tab +"}";if(!"void".equals(methodTypeName)){methedContent+= line + tab + tab +"return null;";}methedContent+= line + tab +"}"+line+line;}javaContent = packageContent + impPackageContent + classContent + privateHandle +constructorContent +methedContent + line + "}";//1、使用IO字符流将创建好String 放到D盘中,用于查看是否存在问题。String filePath = "D:\\com\\proxy\\";String classFileName = "com.proxy.$Proxy";File fileDir = new File("D:\\com\\proxy\\");try {if (!fileDir.isDirectory()) {fileDir.mkdirs();}File file = new File("D:\\com\\proxy\\$Proxy.java");if (!file.exists()) {file.createNewFile();}FileWriter fileWriter = new FileWriter(file);fileWriter.write(javaContent);fileWriter.flush();fileWriter.close();//创建java编译器JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();//第三方管理器StandardJavaFileManager fileMgr = javaCompiler.getStandardFileManager(null, null, null);//将java文件放到管理器中Iterable units = fileMgr.getJavaFileObjects(file);//创建编译任务JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileMgr, null, null, null, units);//开始启动任务task.call();fileMgr.close();//使用反射获取编译后的$Proxy对象URL [] urls = new URL[]{new URL("file:D:\\\\")};URLClassLoader ucl = new URLClassLoader(urls);Class clazz = ucl.loadClass(classFileName);Constructor constructor = clazz.getConstructor(User_InvoctionHandle.class);proxy = constructor.newInstance(handle);System.out.println("成功!");} catch (Exception e) {System.out.println("失败!");e.printStackTrace();}return proxy;}
}  

自定义写的动态代理缺点:

  需要生成文件

  动态编译文件

  IO操作

  需要一个URLClassloader

  性能问题

 

JDK动态代理源码分析

  file(java)-class-baty[]-object-User

  使用jdk代理方式查看生成的代理类在此之前我们要先读一下源码,以下代码是生成一个代理对象的实体,也就是动态代理实现接口+聚合方式生成的代理类(如不理解可以查看上章节级上上章节的知识)

public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/**查找或生成指定的代理类 这里返回的是查看代理缓存里面是否存在同一个类加载器加载的接口 return proxyClassCache.get(loader,interfaces)

*扩展,判断两个对象是否相同,首先先要判断是否是同一个类加载器加载进来的对象。(类似两个不同的公司【代表类加载器】,就算部门【对象】一致也不能说这两个部门是同一部门)

*从这段代码中,我们可以看出jkd代理,底层是存在缓存的,也就是当jdk每次代理一个接口时,已经会在完成前先存入缓存,方便以后使用。(可想而知,高级开发[规范有必要]中一定应该想到性能的问题)

     **/
      Class cl = getProxyClass0(loader, intfs);/** 使用指定的调用处理程序调用其构造函数。*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//调用静态构造器final Constructor cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
        //判断这个方法的修饰是否为public(很关键,因为这个设计到相关包的问题,如果不是public则不是同包的情况下不能引包)if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction() {public Void run() {cons.setAccessible(true);return null;}});}
        //这里的cons就是代理器的实体对象,里面包含了目标对象,非常重要return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

  从上述方法中可以看出Constructor获取的对象cons很重要,因为从此代码往下就是直接使用并获取InvocationHandle对象了,下面深入了解一下当中的构造类的构造对象如何获取

final Constructor cons = cl.getConstructor(constructorParams);

 此方法就是从缓存中获取代理对象,如果没有缓存则使用其他方式获取代理对象,请看以下代码

如何看源码?在这里叫大家一个比较使用的方式,直接找到返回值查看这个返回值是否是想要的,如果是则查看生成返回值得地方就可以

public V get(K key, P parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();Object cacheKey &#61; CacheKey.valueOf(key, refQueue);// 懒加载cacheKey的第2级valuesMapConcurrentMap> valuesMap &#61; map.get(cacheKey);if (valuesMap &#61;&#61; null) {ConcurrentMap> oldValuesMap&#61; map.putIfAbsent(cacheKey,valuesMap &#61; new ConcurrentHashMap<>());if (oldValuesMap !&#61; null) {valuesMap &#61; oldValuesMap;}}Object subKey &#61; Objects.requireNonNull(subKeyFactory.apply(key, parameter));Supplier supplier &#61; valuesMap.get(subKey);Factory factory &#61; null;while (true) {if (supplier !&#61; null) {// supplier might be a Factory or a CacheValue instanceV value &#61; supplier.get();if (value !&#61; null) {return value;}}// else no supplier in cache// or a supplier that returned null (could be a cleared CacheValue// or a Factory that wasn&#39;t successful in installing the CacheValue)// lazily construct a Factoryif (factory &#61;&#61; null) {factory &#61; new Factory(key, parameter, subKey, valuesMap);}if (supplier &#61;&#61; null) {supplier &#61; valuesMap.putIfAbsent(subKey, factory);if (supplier &#61;&#61; null) {// successfully installed Factorysupplier &#61; factory;}// else retry with winning supplier} else {if (valuesMap.replace(subKey, supplier, factory)) {// successfully replaced// cleared CacheEntry / unsuccessful Factory// with our Factorysupplier &#61; factory;} else {// retry with current suppliersupplier &#61; valuesMap.get(subKey);}}}}

  以上代码中我们得到的就是value 而这个value就是我们想要的$Proxy对象


根据上述截图将断点设置在value&#61;supplier.get();
并F5跟进去
根据以上方法我们最终进入到Proxy类中的内部类

&#64;Overridepublic Class apply(ClassLoader loader, Class[] interfaces) {Map, Boolean> interfaceSet &#61; new IdentityHashMap<>(interfaces.length);for (Class intf : interfaces) {/**验证类加载器是否解析了这个类的名称* 查看这个接口数组中的对象是否与自身相等&#xff0c;这就是上述扩展中提到的查看两个对象是否一致&#xff0c;首先要判断是否是用一个加载器加载进来的对象*/interfaceClass&#xff01;&#61;intf&#xff1f; 这个地方就是判断&#xff0c;很奇怪的判断方式有没有Class interfaceClass &#61; null;try {interfaceClass &#61; Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass !&#61; intf) {throw new IllegalArgumentException(intf &#43; " is not visible from class loader");}/** 判断interfaceClass是否是一个接口&#xff08;这里说的就是前2章节说的&#xff0c;jdk动态代理是由接口方式实现的&#xff0c;也必须是接口否则就会抛出异常"IllegalArgumentExcaption这不是一个接口"&#xff09;* interface.*/if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() &#43; " is not an interface");}/** 验证代理对象实现的接口是否在内存中单例*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) !&#61; null) {throw new IllegalArgumentException("repeated interface: " &#43; interfaceClass.getName());}}String proxyPkg &#61; null; // package to define proxy class inint accessFlags &#61; Modifier.PUBLIC | Modifier.FINAL;/** 记录一个非公共代理接口包&#xff0c;以便代理类将在同一个包中定义。验证所有非公共代理接口都在同一个包中*/for (Class intf : interfaces) {int flags &#61; intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags &#61; Modifier.FINAL;String name &#61; intf.getName();int n &#61; name.lastIndexOf(&#39;.&#39;);String pkg &#61; ((n &#61;&#61; -1) ? "" : name.substring(0, n &#43; 1));if (proxyPkg &#61;&#61; null) {proxyPkg &#61; pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg &#61;&#61; null) {// 如果没有非公共代理接口&#xff0c;则默认使用com.cun 代理包proxyPkg &#61; ReflectUtil.PROXY_PACKAGE &#43; ".";}/** 给生成的代理类取名字*/long num &#61; nextUniqueNumber.getAndIncrement();String proxyName &#61; proxyPkg &#43; proxyClassNamePrefix &#43; num;/** 生成代理类&#xff0c;重中之重&#xff0c;生成的代理类是一个byte数组使用的是ProxyGenerator中的generateProxyClass方法&#xff0c;*可以使用此方法通过IO方式生成代理类的.class文件然后通过反编译查看jdk生成的代理类是什么样子的*/byte[] proxyClassFile &#61; ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}}

  

使用byte[] proxyClassFile &#61; ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);生成jdk代理类.class文件

//获取JDK生成的代理对象.Class文件byte bytes[] &#61;ProxyGenerator.generateProxyClass("Proxy_wjw",new Class []{(ObjectDao.class)});//为了方便直接在方法中throws ExceptionFileOutputStream fileOutputStream &#61; new FileOutputStream("D:\\$Proxy_JDK.class");fileOutputStream.write(bytes);fileOutputStream.flush();fileOutputStream.close();

  

 

 $Proxy_JDK.class文件反编译后

pa

package com.handle;

import com.dao.ObjectDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy_JDK extends Proxy implements ObjectDao {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy_JDK(InvocationHandler var1) throws {
super(var1);
}

public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final String queryStr(String var1) throws {
try {
return (String)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final void query() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

static {
try {
m1 &#61; Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 &#61; Class.forName("com.dao.ObjectDao").getMethod("queryStr", Class.forName("java.lang.String"));
m3 &#61; Class.forName("com.dao.ObjectDao").getMethod("query");
m2 &#61; Class.forName("java.lang.Object").getMethod("toString");
m0 &#61; Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

}

  

区别就是多了一个继承和几个方法&#xff08;equals、toString、hashCOde&#xff09;

defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);此方法是JDK将bety数组转换成对象的底层方法

总结&#xff1a;通过接口反射成字节码&#xff0c;然后把字节码转换成class&#xff0c;再通过native方法转成对象

遗留疑问&#xff1a;JDK代理和cglib代理的区别&#xff1f;spring底层就是用的cglib&#xff0c;预知后续请持续关注





  

  

转:https://www.cnblogs.com/gnwzj/p/11117781.html



推荐阅读
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 本文详细解析了客户端与服务器之间的交互过程,重点介绍了Socket通信机制。IP地址由32位的4个8位二进制数组成,分为网络地址和主机地址两部分。通过使用 `ipconfig /all` 命令,用户可以查看详细的IP配置信息。此外,文章还介绍了如何使用 `ping` 命令测试网络连通性,例如 `ping 127.0.0.1` 可以检测本机网络是否正常。这些技术细节对于理解网络通信的基本原理具有重要意义。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 枚举类中enum关键字的常见应用与实践
    在枚举类中,`enum`关键字具有重要的作用,本文探讨了其常见的应用场景与实践。特别指出,枚举对象必须置于枚举类的首行,否则将导致编译错误。通过具体的代码示例,详细解析了这一规则及其背后的原理,帮助开发者更好地理解和使用枚举类。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 在Java程序设计中,实现高效的分页功能是提升应用性能的关键之一。本文介绍了通过使用 `PageController` 类来处理大数据集的分页操作,该类能够从一个较大的集合中提取出指定大小的小集合。具体实现中,通过优化数据访问和减少内存消耗,确保了分页操作的高效性和稳定性。此外,文章还探讨了分页算法的优化策略,包括缓存机制和懒加载技术的应用,以进一步提高系统的响应速度和用户体验。 ... [详细]
  • 在使用 Qt 进行 YUV420 图像渲染时,由于 Qt 本身不支持直接绘制 YUV 数据,因此需要借助 QOpenGLWidget 和 OpenGL 技术来实现。通过继承 QOpenGLWidget 类并重写其绘图方法,可以利用 GPU 的高效渲染能力,实现高质量的 YUV420 图像显示。此外,这种方法还能显著提高图像处理的性能和流畅性。 ... [详细]
  • 在处理 XML 数据时,如果需要解析 `` 标签的内容,可以采用 Pull 解析方法。Pull 解析是一种高效的 XML 解析方式,适用于流式数据处理。具体实现中,可以通过 Java 的 `XmlPullParser` 或其他类似的库来逐步读取和解析 XML 文档中的 `` 元素。这样不仅能够提高解析效率,还能减少内存占用。本文将详细介绍如何使用 Pull 解析方法来提取 `` 标签的内容,并提供一个示例代码,帮助开发者快速解决问题。 ... [详细]
  • 本文探讨了如何利用Java代码获取当前本地操作系统中正在运行的进程列表及其详细信息。通过引入必要的包和类,开发者可以轻松地实现这一功能,为系统监控和管理提供有力支持。示例代码展示了具体实现方法,适用于需要了解系统进程状态的开发人员。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
author-avatar
KTone
敲一夜代码,流下两三行泪水,掏空四肢五体,六杯咖啡七桶泡面,还有八个测试九层审批,可谓十分艰难
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有