张柏芝47张“不挂一丝滤镜”旧照疯传,岁月痕迹下的绝代风华,引爆全网热议!

核心内容摘要

甘雨被旅行者超了:一场跨越星辰的“版本更新”的深度解读
爱情岛一号线与二号线:穿越心灵的蓝色轨迹

ysl水蜜桃86:轻奢与高奢的界定,是年龄,还是态度?

近期开发系统过程中遇到的一个需求系统给定一个接口用户可以自定义开发该接口的实现并将实现打成jar包上传到系统中。

系统完成热部署并切换该接口的实现。

定义简单的接口这里以一个简单的计算器功能为例接口定义比较简单直接上代码。

public interface Calculator { int calculate(int a, int b); int add(int a, int b); }该接口的一个简单的实现考虑到用户实现接口的两种方式使用spring上下文管理的方式或者不依赖spring管理的方式这里称它们为注解方式和反射方式。

calculate方法对应注解方式add方法对应反射方式。

计算器接口实现类的代码如下Service publicclass CalculatorImpl implements Calculator { Autowired CalculatorCore calculatorCore; /** * 注解方式 */ Override public int calculate(int a, int b) { int c calculatorCore.add(a, b); return c; } /** * 反射方式 */ Override public int add(int a, int b) { returnnew CalculatorCore().add(a, b); } }这里注入CalculatorCore的目的是为了验证在注解模式下系统可以完整的构造出bean的依赖体系并注册到当前spring容器中。

CalculatorCore的代码如下Service public class CalculatorCore { public int add(int a, int b) { return ab; } }反射方式热部署用户把jar包上传到系统的指定目录下这里定义上传jar文件路径为jarAddressjar的Url路径为jarPath。

private static String jarAddress E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar; private static String jarPath file:/ jarAddress;并且可以要求用户填写jar包中接口实现类的完整类名。

接下来系统要把上传的jar包加载到当前线程的类加载器中然后通过完整类名加载得到该实现的Class对象。

然后反射调用即可完整代码/** * 热加载Calculator接口的实现 反射方式 */ public static void hotDeployWithReflect() throws Exception { URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader()); Class clazz urlClassLoader.loadClass(com.nci.cetc

calculator.impl.CalculatorImpl); Calculator calculator (Calculator) clazz.newInstance(); int result calculator.add(1,

; System.out.println(result); }注解方式热部署如果用户上传的jar包含了spring的上下文那么就需要扫描jar包里的所有需要注入spring容器的bean注册到当前系统的spring容器中。

其实这就是一个类的热加载动态注册的过程。

直接上代码/** * 加入jar包后 动态注册bean到spring容器包括bean的依赖 */ public static void hotDeployWithSpring() throws Exception { SetString classNameSet DeployUtils.readJarFile(jarAddress); URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader()); for (String className : classNameSet) { Class clazz urlClassLoader.loadClass(className); if (DeployUtils.isSpringBeanClass(clazz)) { BeanDefinitionBuilder beanDefinitionBuilder BeanDefinitionBuilder.genericBeanDefinition(clazz); defaultListableBeanFactory.registerBeanDefinition(DeployUtils.transformName(className), beanDefinitionBuilder.getBeanDefinition()); } } }在这个过程中将jar加载到当前线程类加载器的过程和之前反射方式是一样的。

然后扫描jar包下所有的类文件获取到完整类名并使用当前线程类加载器加载出该类名对应的class对象。

判断该class对象是否带有spring的注解如果包含则将该对象注册到系统的spring容器中。

DeployUtils包含读取jar包所有类文件的方法、判断class对象是否包含sping注解的方法、获取注册对象对象名的方法。

代码如下/** * 读取jar包中所有类文件 */ public static SetString readJarFile(String jarAddress) throws IOException { SetString classNameSet new HashSet(); JarFile jarFile new JarFile(jarAddress); EnumerationJarEntry 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); } } return classNameSet; }/** * 方法描述 判断class对象是否带有spring的注解 */ public static boolean isSpringBeanClass(Class? cla) { if (cla null) { returnfalse; } //是否是接口 if (cla.isInterface()) { returnfalse; } //是否是抽象类 if (Modifier.isAbstract(cla.getModifiers())) { returnfalse; } if (cla.getAnnotation(Component.class) ! null) { returntrue; } if (cla.getAnnotation(Repository.class) ! null) { returntrue; } if (cla.getAnnotation(Service.class) ! null) { returntrue; } returnfalse; }/** * 类名首字母小写 作为spring容器beanMap的key */ public static String transformName(String className) { String tmpstr className.substring(className.lastIndexOf(.)

; return tmpstr.substring(0,

.toLowerCase() tmpstr.substring(

; }删除jar时需要同时删除spring容器中注册的bean在jar包切换或删除时需要将之前注册到spring容器的bean删除。

spring容器的bean的删除操作和注册操作是相逆的过程这里要注意使用同一个spring上下文。

代码如下/** * 删除jar包时 需要在spring容器删除注入 */ public static void delete() throws Exception { SetString classNameSet DeployUtils.readJarFile(jarAddress); URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader()); for (String className : classNameSet) { Class clazz urlClassLoader.loadClass(className); if (DeployUtils.isSpringBeanClass(clazz)) { defaultListableBeanFactory.removeBeanDefinition(DeployUtils.transformName(className)); } } }测试测试类手动模拟用户上传jar的功能。

测试函数写了个死循环一开始没有找到jar会抛出异常捕获该异常并睡眠10秒。

这时候可以把jar手动放到指定的目录下。

代码如下ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml); DefaultListableBeanFactory defaultListableBeanFactory (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); while (true) { try { hotDeployWithReflect(); // hotDeployWithSpring(); // delete(); } catch (Exception e) { e.printStackTrace(); Thread.sleep(1000 *

; } }

17.c13起草视频详情-17.c13起草视频详情应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123