【发布时间】:2020-07-12 10:03:25
【问题描述】:
我尝试使用 RESTEasy 和 Jetty 开发多个 Web 服务。我计划让每个 web 服务都有自己的一组 JAR 文件,这些文件将从某个目录加载。
我所做的是像这样创建自定义 ClassLoader
public class AppClassLoader extends ClassLoader{
static Logger log = Logger.getLogger(AppClassLoader.class.getName());
String libPath = "";
public AppClassLoader(String libPath) {
this.libPath = libPath;
}
@Override
public Class loadClass(String name) throws ClassNotFoundException {
Class clazz = findLoadedClass(name);
if(clazz == null) {
try {
clazz = ClassLoader.getSystemClassLoader().loadClass(name);
if(clazz == null) {
clazz = getClass(name);
if(clazz == null) {
throw new ClassNotFoundException();
}
}
return clazz;
}catch (ClassNotFoundException e) {
// TODO: handle exception
throw new ClassNotFoundException();
}
}else {
return getSystemClassLoader().loadClass(name);
}
}
private Class<?> getClass(String name) throws ClassNotFoundException {
try {
File dir = new File(this.libPath);
if(dir.isDirectory()) {
for(File jar : dir.listFiles()) {
JarFile jarFile = new JarFile(jar.getPath());
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + jar.getPath()+"!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if(je.isDirectory() || !je.getName().endsWith(".class")){
continue;
}
String className = je.getName().substring(0,je.getName().length()-6);
className = className.replace('/', '.');
if(className.equals(name)) {
return cl.loadClass(className);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return null;
}
然后我在初始化 Jetty 和 RestEasy 以启动服务器时,将这个自定义类加载器分配给 ServletContextHandler,如下所示
final int port = 8080;
final Server server = new Server(port);
// Setup the basic Application "context" at "/".
// This is also known as the handler tree (in Jetty speak).
final ServletContextHandler context = new ServletContextHandler(server, CONTEXT_ROOT);
AppClassLoader classLoader = new AppClassLoader("../apps/dummy/lib");
context.setClassLoader(classLoader);
// Setup RESTEasy's HttpServletDispatcher at "/api/*".
final ServletHolder restEasyServlet = new ServletHolder(new HttpServletDispatcher());
restEasyServlet.setInitParameter("resteasy.servlet.mapping.prefix",APPLICATION_PATH);
restEasyServlet.setInitParameter("javax.ws.rs.Application",App.class.getCanonicalName());
final ServletHolder servlet = new ServletHolder(new HttpServletDispatcher());
context.addServlet(restEasyServlet, APPLICATION_PATH + "/*");
server.start();
server.join();
然后在 jax-rs 端点我有这段代码
@Path("/")
public class Dummy {
Logger log = Logger.getLogger(Dummy.class.getName());
@GET
@Path("dummy")
@Produces(MediaType.TEXT_PLAIN)
public String test() {
HikariConfig src = new HikariConfig();
JwtMap jw = new JwtMap();
return "This is DUMMY service : "+src.getClass().getName().toString()+" ### "+jw.getClass().getName();
}}
我成功地启动了服务器,但是当我尝试调用 web 服务时,它返回了
java.lang.NoClassDefFoundError: com/zaxxer/hikari/HikariConfig
然后我看到线程中使用的classLoader不是我的自定义类加载器而是java标准类加载器。
我在这里做错了什么?我对这个类加载东西很陌生,我不确定我真的理解如何使用它。
问候
【问题讨论】:
标签: java jax-rs jetty classloader resteasy