前言
笔者第一次用netty开发功能,也是第一次把服务部署到weblogic上,之前没用过weblogic,算是小白一个,说得不对的地方,请各路大神不吝赐教。
项目背景
笔者使用若依框架整合netty,搭建了一个http代理服务,最后要把服务部署到weblogic上。
涉及到的技术或框架
ruoyi 3.8.1 oracle版本
springboot 2.5.8
netty 4.1.72.Final
weblogic 12c
问题整理
在本地IDEA跑服务,一点儿问题没有,已部署安装到weblogic就开始出现问题,现整理如下:
问题1
把项目打成war包,在weblogic控制台安装应用时,提示tomcat-embed-websocket-9.0.56.jar和tomcat-embed-el-9.0.56.jar两个jar包里的/META-INF/web-fragment.xml有问题。报错信息如下:
cvc-enumeration-valid string value '4.0' is not a valid enumeration value for web-app-versionType in namespace http://xmlns.jcp.org/xml/ns/javaee
解决办法是编辑这两个jar包里面/META-INF/web-fragment.xml文件,把
<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-fragment_4_0.xsd"
version="4.0"
metadata-complete="true">
改成
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0">
改完终于能把应用安装上了。
问题2
在本地开发时,我把netty服务写到了应用的main方法中,在IDEA中直接run就能把netty服务也启动起来。但是部署到weblogic上,应用启动之后发现netty没有起来,其他接口是正常的,可以正常使用若依相关服务。原main方法如下:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class RuoYiApplication {
public static void main(String[] args) throws Exception {
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(RuoYiApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ ");
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new GatewayServerInitializer());
ChannelFuture future = serverBootstrap.bind(9000).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
从日志来看,应该是没有执行main方法,所以netty服务没起来。
因此,把netty服务单独提取出来,作为一个线程类(implements Runnable),在Override的run方法中启动netty服务。
import com.ruoyi.GatewayServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
public class NettyServer implements Runnable {
@Override
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new GatewayServerInitializer());
ChannelFuture future = serverBootstrap.bind(9000).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
并增加一个容器事件监听,在SpringBoot应用刷新的时候新建线程并执行。
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 容器事件监听
*/
@Component
public class NettyServerListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
NettyServer nettyServer = new NettyServer();
new Thread(nettyServer).start();
}
}
问题3
在修复问题2之前,启动的时候,除了netty服务没起来,还在日志里面发现了一个错误提示,如下:
java.lang.ClassCastException: org.apache.tomcat.websocket.server.WsServerContainer cannot be cast to org.glassfish.tyrus.server.TyrusServerContainer
经查,大神们说springboot部署到weblogic上还有3个要注意的地方:
1、要把tomcat相关jar的使用范围改为provided,打war包时不包含进去,即在pom.xml中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
注:这个依赖的作用是将SpringBoot内部自带的tomcat排除掉,要使用外部的web容器。
2、新增/src/main/webapp/WEB-INF/weblogic.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
http://xmlns.oracle.com/weblogic/weblogic-web-app
https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j</wls:package-name>
<wls:package-name>com.fasterxml.jackson.*</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>
3、修改main方法,改成:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.WebApplicationInitializer;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class RuoYiApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RuoYiApplication.class);
}
public static void main(String[] args) throws Exception {
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(RuoYiApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ ");
}
}
网上还有人说还要再添加/src/main/webapp/WEB-INF/web.xml,这个和servlet的版本有关,若依3.8.1版本使用的springBoot是2.5.8,使用了servlet 3.0,因此不需要新增web.xml,具体是否需要新增,可以参考springBoot官网文档,例如2.5.8的文档地址如下:
https://docs.spring.io/spring-boot/docs/2.5.8/reference/htmlsingle/
问题4
修复问题2后,服务正常能用了,可把我乐坏了,以前不是没用过weblogic么?看看怎么重启服务呗,免得后面服务挂了,怎么重启都不知道。
于是在weblogic管理控制台上面停止服务,再重新起来服务,坏了!日志报了一个错误:
java.net.BindException: Address already is use
从日志来看,怀疑是没有执行到finally块的shutdownGracefully方法,于是修改netty服务和容器事件监听类,把两个事件循环线程组改成静态全局变量,在容器即将关闭时调用shutdownGracefully方法。
import com.ruoyi.GatewayServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer implements Runnable {
public static EventLoopGroup bossGroup = new NioEventLoopGroup(1);
public static EventLoopGroup workerGroup = new NioEventLoopGroup();
@Override
public void run() {
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new GatewayServerInitializer());
serverBootstrap.bind(9000).sync();
} catch (Exception e) {
e.printStackTrace();
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;
/**
* 容器事件监听
*/
@Component
public class NettyServerListener implements ApplicationListener {
private static final Logger log = LoggerFactory.getLogger(NettyServerListener.class);
/**
* springBoot容器事件
* @param event
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 在这里可以监听到Spring Boot的生命周期
if (event instanceof ApplicationEnvironmentPreparedEvent) {
log.info("SpringBoot初始化环境变量");
} else if (event instanceof ApplicationPreparedEvent) {
log.info("SpringBoot初始化完成");
} else if (event instanceof ContextRefreshedEvent) {
log.info("SpringBoot应用刷新");
NettyServer nettyServer = new NettyServer();
new Thread(nettyServer).start();
} else if (event instanceof ApplicationReadyEvent) {
log.info("SpringBoot应用已启动完成");
} else if (event instanceof ContextStartedEvent) {
log.info("SpringBoot应用启动,需要在代码动态添加监听器才可捕获 ");
} else if (event instanceof ContextStoppedEvent) {
NettyServer.bossGroup.shutdownGracefully();
NettyServer.workerGroup.shutdownGracefully();
log.info("SpringBoot应用停止");
} else if (event instanceof ContextClosedEvent) {
NettyServer.bossGroup.shutdownGracefully();
NettyServer.workerGroup.shutdownGracefully();
log.info("SpringBoot应用关闭");
} else {
}
}
}
改成这种写法之后,重新安装war包,启动服务,接着停止服务,再启动服务,又报了另一个错误:
Force-closing a channel whose registration task was not accepted by an event loop: [id:0xda3d9df0] java.util.concurrent.RejectedExecutionException: event executor terminated
到这里,我就懵逼了,感觉自己应该是对netty的shutdownGracefully方法理解错了。
上网查了半天资料,大神说shutdownGracefully方法是netty的资源优雅释放,不但断开所有连接,而且将其中的内存清理掉。除非是整个进程优雅退出,一般情况下不会调用EventLoopGroup和EventLoop的shutdownGracefully方法,更多的是链路channel的关闭和资源释放。
那么也就是说,当你在本地IDEA或者apache tomcat上面跑的时候,实际上启动了一个进程,shutdown的时候进程也就退出了,那么在finally或者容器停止的时候调用shutdownGracefully去释放资源,一点儿问题也没有。
但是在weblogic上面启动服务的时候,服务和weblogic的进程是同一个,这个时候调用shutdownGracefully把资源释放了,再启动的时候就会有问题,因此只需要关闭链路channel就好。
于是再次修改netty服务和容器事件监听类,把ChannelFuture改成静态全局变量,在容器即将关闭时调用关闭链路channel的close方法。
import com.ruoyi.GatewayServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer implements Runnable {
public static ChannelFuture future = null;
@Override
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new GatewayServerInitializer());
future = serverBootstrap.bind(9000).sync();
} catch (Exception e) {
e.printStackTrace();
}
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.stereotype.Component;
/**
* 容器事件监听
*/
@Component
public class NettyServerListener implements ApplicationListener {
private static final Logger log = LoggerFactory.getLogger(NettyServerListener.class);
/**
* springBoot容器事件
* @param event
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 在这里可以监听到Spring Boot的生命周期
if (event instanceof ApplicationEnvironmentPreparedEvent) {
log.info("SpringBoot初始化环境变量");
} else if (event instanceof ApplicationPreparedEvent) {
log.info("SpringBoot初始化完成");
} else if (event instanceof ContextRefreshedEvent) {
log.info("SpringBoot应用刷新");
NettyServer nettyServer = new NettyServer();
new Thread(nettyServer).start();
} else if (event instanceof ApplicationReadyEvent) {
log.info("SpringBoot应用已启动完成");
} else if (event instanceof ContextStartedEvent) {
log.info("SpringBoot应用启动,需要在代码动态添加监听器才可捕获 ");
} else if (event instanceof ContextStoppedEvent) {
if (NettyServer.future != null) {
NettyServer.future.channel().close();
log.info("channel已关闭");
}
log.info("SpringBoot应用停止");
} else if (event instanceof ContextClosedEvent) {
if (NettyServer.future != null) {
NettyServer.future.channel().close();
log.info("channel已关闭");
}
log.info("SpringBoot应用关闭");
} else {
}
}
}
问题5
此时应用重启不报错了,再登录一下管理后台,又报了个错误,提示线程池已停止。
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@6134f059 rejected from com.ruoyi.framework.config.ThreadPoolConfig$1@3ed5be2f[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 2]
基于问题4的思路,首先怀疑是不是连接池没有正确的shutdown,调试之后发现weblogic停止应用的时候已经自动shutdown了。
那会不会是线程池只初始化了一次,导致第二次重启还是使用了被shutdown的线程池呢?调试结果是weblogic应用重启还是有重新初始化线程池的。
最后发现是线程执行时的问题,它居然一直使用的是第一次初始化的线程池。。。
于是在使用之前加个判断:
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.ruoyi.common.utils.Threads;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
* 异步任务管理器
*
* @author ruoyi
*/
public class AsyncManager
{
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager(){
}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me() {
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task)
{
if (executor == null || executor.isShutdown() || executor.isTerminated()) {
executor = SpringUtils.getBean("scheduledExecutorService");
}
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown()
{
Threads.shutdownAndAwaitTermination(executor);
}
}
问题6
因为在调试、测试上面几个问题的时候会时不时重启应用,然后就发现了这个问题:当应用第一次成功启动之后,马上停止服务,接着再次启动服务,完了去登陆管理后台,会出现如下错误:
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class com.ruoyi.framework.manager.AsyncManager
如果第一次成功启动之后有正常使用一下系统,就不会报这个错误。这个问题研究了好久就挺无解的,先记录一下,回头有空再研究研究。
2022年5月16日继续更新一下新发现的问题
问题6弄着弄着好像自己消失了。。。。。。要不是我回来看笔记,估计都忘了这回事了,算了,随它去吧。。。
问题7:
客户希望数据库源使用weblogic的jndi,而不是写死在配置文件中,于是百度了一下,已经有大神给了修改方法,参考:
https://copyfuture.com/blogs-details/20201230225025438v
在配置好weblogic的jndi后,项目需要修改两个地方:
1、配置文件:主库从库各新增一个配置项jndiName
spring:
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: oracle.jdbc.driver.OracleDriver
druid:
# 主库数据源
master:
url:
username:
password:
jndiName: proxy_jndi
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
jndiName: proxy_jndi
2、修改com.ruoyi.framework.config.DruidConfig文件的masterDataSource和slaveDataSource方法,如下:
@Value("${spring.datasource.druid.master.jndiName}")
private String masterjndiName;
@Value("${spring.datasource.druid.slave.jndiName}")
private String slavejndiName;
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties) throws NamingException {
if (StringUtils.isBlank(masterjndiName)) {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
} else {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
return lookup.getDataSource(masterjndiName);
}
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties) {
if (StringUtils.isBlank(slavejndiName)) {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
} else {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
return lookup.getDataSource(slavejndiName);
}
}
于是我发动CV大法,再在weblogic配置个数据源proxy_jndi,发布运行war包就哦了。但是,直到我更新或者重新安装war包,噩梦就来了,每次一启动老报错:
javax.naming.NameNotFoundException: Unable to resolve 'proxy_jndi'. Resolved ''; remaining name 'proxy_jndi' Unable to resolve 'proxy_jndi'. Resolved ''; remaining name 'proxy_jndi'
每次都要把数据源删了,重新配置一遍才能跑起来,要是生产上面有其他人开发的应用也使用同一个jndi,一删人家不得疯了。。。百度了半天都是说找不到proxy_jndi,要检查是不是写错了,这不科学啊,拷贝还能有错的。。。
直到前几天客户说人家的项目也是使用的jndi,从来没有这样的报错,让我参考一下,于是我解压了人家的war包,用idea一个个打开class文件去找,看人家怎么连的数据源,一比对才发现真的不一样,重新修改com.ruoyi.framework.config.DruidConfig文件的masterDataSource和slaveDataSource方法后问题解决了,如下:
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties) throws NamingException {
if (StringUtils.isBlank(masterjndiName)) {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
} else {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName(masterjndiName);
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
try {
bean.afterPropertiesSet();
} catch (NamingException e) {
throw new RuntimeException(e);
}
return (DataSource) bean.getObject();
}
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties) {
if (StringUtils.isBlank(slavejndiName)) {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
} else {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName(slavejndiName);
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
try {
bean.afterPropertiesSet();
} catch (NamingException e) {
throw new RuntimeException(e);
}
return (DataSource) bean.getObject();
}
}
问题8:
系统经过漏洞扫描,需要升级一下jar包,于是Spring Boot升级到2.5.11,Spring升级到5.3.19,poi升级到5.2.1,升级一通之后,重新部署到weblogic,登陆时报错了:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'java.lang.String'. Check configuration for 'uuid'
查了半天资料,说是weblogic jar包冲突了,于是修改weblogic.xml,在prefer-application-packages处添加以下两行:
<wls:prefer-application-packages>
......
<wls:package-name>org.hibernate.*</wls:package-name>
<wls:package-name>javax.validation.*</wls:package-name>
</wls:prefer-application-packages>
打包重新部署,以上问题不再出现。
问题9:
完成jar包升级之后,需要测试一下所有服务是否正常,发现下载功能有问题,估计是poi升级导致的,主要是以下三个错误:
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook
……
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook
……
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.ExceptionInInitializerError
……
Caused by: java.lang.ExceptionInInitializerError: null
……
Caused by: org.apache.xmlbeans.XmlRuntimeException: java.lang.ClassCastException: org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl cannot be cast to org.apache.xmlbeans.SchemaTypeLoader
……
Caused by: java.lang.ClassCastException: org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl cannot be cast to org.apache.xmlbeans.SchemaTypeLoader
……
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.io.IOUtils.byteArray(I)[B
……
Caused by: java.lang.NoSuchMethodError: org.apache.commons.io.IOUtils.byteArray(I)[B
用必应搜半天,在stackoverflow找到了解决办法,修改weblogic.xml,在prefer-application-packages处添加以下几行,然后再加prefer-application-resources:
<wls:prefer-application-packages>
......
<wls:package-name>org.apache.commons.io.*</wls:package-name>
<wls:package-name>org.apache.commons.collections4.*</wls:package-name>
<wls:package-name>org.apache.commons.compress.*</wls:package-name>
<wls:package-name>org.apache.poi.*</wls:package-name>
<wls:package-name>org.apache.xmlbeans.*</wls:package-name>
<wls:package-name>org.openxmlformats.*</wls:package-name>
<wls:package-name>schemaorg_apache_xmlbeans.*</wls:package-name>
</wls:prefer-application-packages>
<wls:prefer-application-resources>
<wls:resource-name>schemaorg_apache_xmlbeans/system/sXMLCONFIG/TypeSystemHolder.class</wls:resource-name>
<wls:resource-name>schemaorg_apache_xmlbeans/system/sXMLLANG/TypeSystemHolder.class</wls:resource-name>
<wls:resource-name>schemaorg_apache_xmlbeans/system/sXMLSCHEMA/TypeSystemHolder.class</wls:resource-name>
<wls:resource-name>schemaorg_apache_xmlbeans/system/sXMLTOOLS/TypeSystemHolder.class</wls:resource-name>
</wls:prefer-application-resources>
后语
前前后后折腾了很久,网上相关的资料也少得可怜,或者年代有些久远,把遇到的问题整理整理,当是一个笔记吧。