Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

更改模块卸载逻辑 #66

Merged
merged 6 commits into from
Apr 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}
}