Skip to content

Commit

Permalink
更改模块卸载逻辑
Browse files Browse the repository at this point in the history
更改模块卸载逻辑
  • Loading branch information
JoeKerouac committed Apr 16, 2018
2 parents eafbccb + 5575945 commit 29fe7de
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.alipay.jarslink.api.impl;

import com.alipay.jarslink.api.Action;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* action代理
*
* @author joe
* @version 2018.04.16 22:33
*/
class ActionProxy<R, T> implements Action<R, T> {
private Action<R, T> action;

/**
* 默认构造器
*
* @param action 要代理的Action
*/
public ActionProxy(Action<R, T> action) {
this.action = action;
}

/**
* 关闭action,便于回收
*/
public void destroy() {
action = null;
}

@Override
public T execute(R actionRequest) {
checkNotNull(action, "current module was closed");
return action.execute(actionRequest);
}

@Override
public String getActionName() {
checkNotNull(action, "current module was closed");
return action.getActionName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@
import org.springframework.beans.CachedIntrospectionResults;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import sun.misc.ClassLoaderUtil;

import java.beans.Introspector;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.net.URLClassLoader;
import java.util.*;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
Expand All @@ -51,37 +50,53 @@ public class SpringModule implements Module {

private static final Logger LOGGER = LoggerFactory.getLogger(SpringModule.class);

/** 模块的配置信息 */
/**
* 模块的配置信息
*/
ModuleConfig moduleConfig;

/** 模块的名称 */
/**
* 模块的名称
*/
private final String name;

/** 模块的版本 */
/**
* 模块的版本
*/
private final String version;

/** 模块启动的时间 */
/**
* 模块启动的时间
*/
private final Date creation;

/** 模块中的Action,Key为大写Action名称 */
/**
* 模块中的Action,Key为大写Action名称
*/
private final Map<String, Action> actions;

private final ConfigurableApplicationContext applicationContext;

public SpringModule(ModuleConfig moduleConfig, String version, String name,
ConfigurableApplicationContext applicationContext) {
public SpringModule(ModuleConfig moduleConfig, String version, String name, ConfigurableApplicationContext
applicationContext) {
this.moduleConfig = moduleConfig;
this.applicationContext = applicationContext;
this.version = version;
this.name = name;
this.creation = new Date();
this.actions = scanActions(applicationContext, Action.class,
new Function<Action, String>() {
@Override
public String apply(Action input) {
return input.getActionName();
}
});
Map<String, Action> actions = scanActions(applicationContext, Action.class, new Function<Action, String>() {
@Override
public String apply(Action input) {
return input.getActionName();
}
});
//转换为代理action
this.actions = Maps.transformValues(actions, new Function<Action, Action>() {
@Override
public Action apply(Action input) {
return new ActionProxy(input);
}
});
}

/**
Expand All @@ -93,18 +108,18 @@ public String apply(Action input) {
* @param <T>
* @return
*/
private <T> Map<String, T> scanActions(ApplicationContext applicationContext, Class<T> type,
Function<T, String> keyFunction) {
private <T> Map<String, T> scanActions(ApplicationContext applicationContext, Class<T> type, Function<T, String>
keyFunction) {
Map<String, T> actions = Maps.newHashMap();
//find Action in module
for (T action : applicationContext.getBeansOfType(type).values()) {
Collection<T> actionCollection = applicationContext.getBeansOfType(type).values();
for (T action : actionCollection) {
String actionName = keyFunction.apply(action);
if (isBlank(actionName)) {
throw new ModuleRuntimeException("JarsLink scanActions actionName is null");
}
String key = actionName.toUpperCase(Locale.CHINESE);
checkState(!actions.containsKey(key), "Duplicated action %s found by: %s",
type.getSimpleName(), key);
checkState(!actions.containsKey(key), "Duplicated action %s found by: %s", type.getSimpleName(), key);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("JarsLink Scan action: {}: bean: {}", key, action);
}
Expand Down Expand Up @@ -179,15 +194,30 @@ public void destroy() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Close application context: {}", applicationContext);
}
if (!actions.isEmpty()) {
actions.clear();
}
//destroy action
clearAction();
//close spring context
closeQuietly(applicationContext);
//clean classloader
clear(applicationContext.getClassLoader());
}

/**
* 销毁Action(如果不销毁Action对象,那么ClassLoader加载的该Action的Class就不会销毁,而Module有getAction方法有可能
* 将Action的引用泄露,所以加一层代理来达到销毁Action对象的目的进而在ClassLoader卸载后能够正确的将Class卸载)
*/
private void clearAction() {
if (actions.isEmpty()) {
return;
}
for (Action action : actions.values()) {
if (action instanceof ActionProxy) {
((ActionProxy) action).destroy();
}
}
actions.clear();
}

/**
* 清除类加载器
*
Expand All @@ -201,10 +231,14 @@ public static void clear(ClassLoader classLoader) {
//Clear the introspection cache for the given ClassLoader
CachedIntrospectionResults.clearClassLoader(classLoader);
LogFactory.release(classLoader);
if (classLoader instanceof URLClassLoader) {
ClassLoaderUtil.releaseLoader((URLClassLoader) classLoader);
}
}

/**
* 关闭Spring上下文
*
* @param applicationContext
*/
private static void closeQuietly(ConfigurableApplicationContext applicationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
*/
package com.alipay.jarslink.api.impl;

import com.alipay.jarslink.api.Action;
import com.alipay.jarslink.api.Module;
import com.alipay.jarslink.api.ModuleConfig;
import com.alipay.jarslink.api.ModuleLoader;
import com.alipay.jarslink.api.*;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
import org.junit.Test;
Expand Down Expand Up @@ -44,11 +41,20 @@ public void shouldLoadModule() {
Assert.assertNotNull(module);
Assert.assertNotNull(module.getCreation());
Assert.assertNotNull(module.getChildClassLoader());
ModuleConfig moduleConfig = module.getModuleConfig();
//卸载模块
moduleLoader.unload(module);

Assert.assertTrue(module.getActions().isEmpty());
Assert.assertNotNull(module.getChildClassLoader());
moduleConfig.addScanPackage("com.alipay.jarslink.error");
ModuleRuntimeException exception = null;
try {
moduleLoader.load(moduleConfig);
} catch (ModuleRuntimeException e) {
exception = e;
}
Assert.assertNotNull(exception);
}

@Test
Expand Down Expand Up @@ -85,6 +91,7 @@ public void shouldDoAction() {
//4.2:查找和执行Action
Action<ModuleConfig, ModuleConfig> action = module.getAction(actionName);
Assert.assertNotNull(action);
Assert.assertEquals(actionName , action.getActionName());
result = action.execute(moduleConfig);
Assert.assertNotNull(result);
Assert.assertEquals(result.getName(), moduleConfig.getName());
Expand Down
Binary file modified jarslink-api/src/test/resources/jarslink-module-demo-1.0.0.jar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.alipay.jarslink.error;

import com.alipay.jarslink.api.Action;
import org.springframework.stereotype.Component;

/**
* @author joe
* @version 2018.04.16 23:27
*/
@Component
public class NullNameAction implements Action<String, String> {
@Override
public String execute(String actionRequest) {
return null;
}

@Override
public String getActionName() {
return null;
}
}

0 comments on commit 29fe7de

Please sign in to comment.