`
cywhoyi
  • 浏览: 421025 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

依托于Spring重加载

 
阅读更多

这几天发现单位同事都在使用JRebel作为热部署工具,它集合多个容器,能够不在重启的情况下进行部署。我们在开发过程中,经常碰到改动xml等配置文件的时候,需要进行重新启动容器,重而带来开发进度缓慢。

 

我是一个传统的人,感觉WTP部署已经够强悍,但是看着90后小朋友玩这么酷炫的万一,让我由衷的觉得更加无聊的事情,不就是能够热部署,大不了写一个定时任务时不时的去加载xml文件,一直检测文件的最后修改,然后重新加载吗?Tomcat的reload是启动后台线程加载这,这种做法挺好的。

 

但是以下这种作法依托于spring,感觉比较容易配置,值得去倡导。

public class XMLMapperLoader implements DisposableBean, InitializingBean, ApplicationContextAware {

	private static final Log logger = LogFactory.getLog(XMLMapperLoader.class);

	private ConfigurableApplicationContext context = null;

	private transient String basePackage = null;

	private final HashMap<String, String> fileMapping = new HashMap<String, String>();

	private Scanner scanner = null;

	private ScheduledExecutorService service = null;


	@Override
	public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
		context = (ConfigurableApplicationContext) applicationContext;

	}


	public void setBasePackage( String basePackage ) {
		this.basePackage = basePackage;
	}


	@Override
	public void afterPropertiesSet() throws Exception {
		try {
			Environment environment = EnvironmentDetect.detectEnvironment();
			if ( environment.isProduct() || environment.isTest() ) {
				return;
			}
			// Assert.notNull(sqlSessionFactory, "sqlSessionFactory不能为null");
			service = Executors.newScheduledThreadPool(1);
			// 获取xml所在包
			if ( basePackage == null ) {
				MapperScannerConfigurer config = context.getBean(MapperScannerConfigurer.class);
				Field field = config.getClass().getDeclaredField("basePackage");
				field.setAccessible(true);
				basePackage = (String) field.get(config);
			}
			// 触发文件监听事件XMLMapperEntityResolver
			scanner = new Scanner();
			scanner.scan();
			service.scheduleAtFixedRate(new Task(), 5, 5, TimeUnit.SECONDS);

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

	}

	class Task implements Runnable {

		@Override
		public void run() {
			try {
				if ( scanner.isChanged() ) {
					logger.debug("*Mapper.xml文件改变,重新加载.");
					scanner.reloadXML();

					logger.debug("加载完毕.");
				}
			} catch ( Exception e ) {
				logger.error(e.getMessage(), e);
			}
		}

	}

	@SuppressWarnings({ "rawtypes" })
	class Scanner {

		private final String[] basePackages;

		private static final String XML_RESOURCE_PATTERN = "**/*.xml";

		private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();


		public Scanner() {
			basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		}


		public Resource[] getResource( String basePackage, String pattern ) throws IOException {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
					+ ClassUtils.convertClassNameToResourcePath(context.getEnvironment().resolveRequiredPlaceholders(
						basePackage)) + "/" + pattern;
			Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
			return resources;
		}


		public void reloadXML() throws Exception {
			SqlSessionFactory factory = context.getBean(SqlSessionFactory.class);
			Configuration configuration = factory.getConfiguration();
			// 移除加载项
			removeConfig(configuration);
			// 重新扫描加载
			for ( String basePackage : basePackages ) {
				Resource[] resources = getResource(basePackage, XML_RESOURCE_PATTERN);
				if ( resources != null ) {
					for ( Resource resource : resources ) {
						if ( resource == null ) {
							continue;
						}
						try {
							XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
									configuration, resource.toString(), configuration.getSqlFragments());
							xmlMapperBuilder.parse();
						} catch ( Exception e ) {
							throw new NestedIOException("Failed to parse mapping resource: '" + resource + "'", e);
						} finally {
							ErrorContext.instance().reset();
						}
					}
				}
			}

		}


		private void removeConfig( Configuration configuration ) throws Exception {
			Class<?> classConfig = configuration.getClass();
			clearMap(classConfig, configuration, "mappedStatements");
			clearMap(classConfig, configuration, "caches");
			clearMap(classConfig, configuration, "resultMaps");
			clearMap(classConfig, configuration, "parameterMaps");
			clearMap(classConfig, configuration, "keyGenerators");
			clearMap(classConfig, configuration, "sqlFragments");

			clearSet(classConfig, configuration, "loadedResources");

		}


		private void clearMap( Class<?> classConfig, Configuration configuration, String fieldName ) throws Exception {
			Field field = classConfig.getDeclaredField(fieldName);
			field.setAccessible(true);
			Map mapConfig = (Map) field.get(configuration);
			mapConfig.clear();
		}


		private void clearSet( Class<?> classConfig, Configuration configuration, String fieldName ) throws Exception {
			Field field = classConfig.getDeclaredField(fieldName);
			field.setAccessible(true);
			Set setConfig = (Set) field.get(configuration);
			setConfig.clear();
		}


		public void scan() throws IOException {
			if ( !fileMapping.isEmpty() ) {
				return;
			}
			for ( String basePackage : basePackages ) {
				Resource[] resources = getResource(basePackage, XML_RESOURCE_PATTERN);
				if ( resources != null ) {
					for ( Resource resource : resources ) {
						String multi_key = getValue(resource);
						fileMapping.put(resource.getFilename(), multi_key);
					}
				}
			}
		}


		private String getValue( Resource resource ) throws IOException {
			String contentLength = String.valueOf(resource.contentLength());
			String lastModified = String.valueOf(resource.lastModified());
			return new StringBuilder(contentLength).append(lastModified).toString();
		}


		public boolean isChanged() throws IOException {
			boolean isChanged = false;
			for ( String basePackage : basePackages ) {
				Resource[] resources = getResource(basePackage, XML_RESOURCE_PATTERN);
				if ( resources != null ) {
					for ( Resource resource : resources ) {
						String name = resource.getFilename();
						String value = fileMapping.get(name);
						String multi_key = getValue(resource);
						if ( !multi_key.equals(value) ) {
							isChanged = true;
							fileMapping.remove(name);
							fileMapping.put(name, multi_key);
						}
					}
				}
			}
			return isChanged;
		}
	}


	@Override
	public void destroy() throws Exception {
		if ( service != null ) {
			service.shutdownNow();
		}
	}

}

 

Spring的配置:

<bean id="*********XmlMapper"/>

 

 

个人结论:

类似的作法其实都差不多,都是检测文件,但是一直想做一个类似于mysql的驱动插件,能够把mysql的文件变动的内容,直接push过来,而不是pull来。

 

 

 

分享到:
评论

相关推荐

    基于Spring Boot 和 WxJava 实现的微信小程序Java后端Demo.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    一款面试刷题的 Spring Cloud 系统.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    环兴商城基于 Spring Cloud 微服务商城系统.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    微服务商城系统 springcloud微服务商城 小程序商城.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    基于spring boot的点餐小程序后台开发.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    基于Spring Boot的卡包管理小程序。.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    基于Spring Boot的《倾心短视频》小程序的后端API服务.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    Spring Boot + mybatis + MySQL社区团购小程序后台.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    一个使用SpringCloud Alibaba开发的电商项目.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    足球报名小程序JAVA后端(spring boot +mybatis).zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    基于Spring Boot的微服务框架-整合微信小程序登录.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    基于spring boot + vue + 微信小程序实现外卖系统.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    微信小程序移动OA的后台服务端项目,框架Spring+springmvc+jdbc.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    基于Spring Cloud的空调维修平台(前端小程序,毕设用,非实际项目).zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    商城项目 Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    前端微信小程序原生开发,ui框架使用weui和iview。后端SpringMVC+Spring+hibernate.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    这是一个社区电商平台,基于spring boot的后端,vue前端管理,微信小程序构建.zip

    作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、...

    springboot微信小程序登录获取用户信息的demo案例.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

    springboot实现调用openAI接口.zip

    速度快:加载速度相较于传统的HTML5网页更快,依托于微信强大的基础设施,能够实现近乎原生应用的流畅体验。 跨平台兼容:开发者一次开发,即可在多种终端设备上运行,免除了复杂的适配工作,大大提高了开发效率。...

Global site tag (gtag.js) - Google Analytics