由于生产环境不是很可靠,出于安全考虑,需要将一部分核心代码作加密,并在程序启动时输入口令。这就需要通过classloader来实现。但发现由不同的classloader加载的类不能进行类型转换。

问题描述

定义一个接口

1
2
3
4
5
6
7
8
package demo;

/**
* Created by <a href="mailto:manwu91@gmail.com">周晓鹏</a> at 20:07, 12/01/2017
*/
public interface HelloService {
public String hello(String name);
}

及其实现类

1
2
3
4
5
6
7
8
9
10
11
package demo;

/**
* Created by <a href="mailto:manwu91@gmail.com">周晓鹏</a> at 20:07, 12/01/2017
*/
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return "Hello " + TestFunc.reverse(name);
}
}

通过自定义的classloader加载实现类并通过反射创建一个对象impl后,使用HelloService svc = (HelloService) impl;进行类型转换会导致
java.lang.ClassCastException异常。这是因为jvm通过类的全限定名称与classloader标记类,虽然HelloServiceImpl实现了HelloService接口,但在测试时HelloService与HelloServiceImpl是由不同的类加载器加载的,所以它们之间不能类型转换。

通过动态代理解决

这种方案是在调用方法时通过反射动态执行对象的对应方法来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Copied from <a href="https://github.com/kamranzafar/JCL/blob/master/JCL2/core/src/main/java/org/xeustechnologies/jcl/proxy/JdkProxyProvider.java">JdkProxyProvider</a>
*/
class JdkProxyHandler implements InvocationHandler {
private final Object delegate;

public JdkProxyHandler(Object delegate) {
this.delegate = delegate;
}

/**
* 执行接口方法时通过反射执行delegate对象的相应方法
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method delegateMethod = delegate.getClass().getMethod( method.getName(), method.getParameterTypes() );
return delegateMethod.invoke( delegate, args );
}
}

之后通过Proxy创建动态代理

1
2
3
4
public Object createProxy(Object object, Class[] interfaces, ClassLoader cl) {
JdkProxyHandler handler = new JdkProxyHandler( object );
return Proxy.newProxyInstance( cl == null ? Object.class.getClassLoader() : cl, interfaces, handler );
}

JCL

JCL(Jar Class Loader)是一个开源便捷的classloader定制库,可以很方便地通过JarClassLoader加载jar文件,JclObjectFactory创建对象,并通过JclUtils.cast方法进行类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 调用 clzName的构造函数创建对象
*
* @param apiType
* @param className
* @param args
* @param <T>
* @return
*/
public <T> T create(Class<T> apiType, String className, Object[] args) {
Object delegate = JclObjectFactory.getInstance(false).create(jcl, className, args);
return JclUtils.cast(delegate, apiType);
}