spring 扫描不到jar中class文件的原因和解决方法

背景

公司一web项目使用的是spring mvc开发的,老员工们写了一个缓存service,即EhcacheService , 该缓存service在web中使用了spring 的@Scheduled 启动加载缓存,代码如下:

applicationContext.xml

<context:component-scan base-package="cn.com.service" />

EhcacheService .java

// 启动加载缓存, 以上一次执行完为准
@Scheduled(fixedDelay = 365 * 24 * 60 * 60 * 1000)   
public void initEhcache() {
    logger.debug("++++++++++++++++++++缓存加载开始++++++++++++++++++++");
    long start = System.currentTimeMillis();
    try {
        this.ehcacheService.loadCache();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    long end = System.currentTimeMillis();
    logger.debug("++++++++++++++++++++缓存加载结束,耗时:" + (end - start) + "++++++++++++++++++++");
}

然而最近同步数据,需要用到EhcacheService , 本人也懒得重写里面的方法,便想着使用ClassPathXmlApplicationContext 或者FileSystemXmlApplicationContext 或者GenericXmlApplicationContext来加载spring配置,然后打包成jar包,丢到linux上,使用java -jar my.jar。代码如下:
test.java

// 程序入口
public static void main(String[] args) throws Exception {
    // 加载spring配置
    GenericXmlApplicationContext context = new GenericXmlApplicationContext();
    context.setValidating(false);
    context.load("classpath*:spring-*.xml");
    context.refresh();

    // ApplicationContext ctx = new FileSystemXmlApplicationContext("spring-*.xml");
    // ApplicationContext context = new ClassPathXmlApplicationContext("spring-*.xml");

    System.out.println("bean的数据量" + context.getBeanDefinitionNames().length);
    EhcacheService ehcacheService = (EhcacheService) context.getBean("ehcacheService");
}

我们先看一下在Eclipse中运行情况
这里写图片描述

我们再看看打包成Runable jar File后,使用 java -jar my.jar的运行情况
这里写图片描述
根据图片中的错误,我们可以看到,spring-*.xml是成功被加载了,然而找不到bean, 很明显,它存在一种可能,那就是bean的class文件没有被spring扫描到。

那么为什么会出现这种情况呢?经过我多方面的查证,spring 扫描bean文件是通过Thread.currentThread().getContextClassLoader().getResource(packageName)加载的。那么我们分析一下ContextClassLoader资源加载机制。

举例说明:我们有这样的一个: cn.com.Test, 类加载器首先会把这个包名转化成文件夹的形式 cn/com, 然后到这个文件夹里去加载Test.class。

然后,当你打包成Runable jar File时,jar的包和文件系统中的包便不是一个概念了,它不能将cn.com转换成cn/com文件夹方式去解读, 类加载转换成cn/com去加载类的时候,便会报出classNotFoundException异常

下面我们使用如下代码验证一下这个过程:

// 项目中jar包所在物理路径  
String jarName = "C:\\Users\\Administrator\\Desktop\\my.jar";  
// 项目中war包所在物理路径
//String jarName = "C:\\Users\\Administrator\\Desktop\\my.war";  
 JarFile jarFile = new JarFile(jarName);  
 Enumeration<JarEntry> entrys = jarFile.entries();  
 while (entrys.hasMoreElements()) {  
     JarEntry jarEntry = entrys.nextElement();  
     System.out.println(jarEntry.getName());  
 }     

打印结果如下:

META-INF/MANIFEST.MF
cn/com/server/action/JobAction.class
cn/com/server/annotation/DataDigestAnnotation.class
cn/com/server/dao/EhcacheDao.class

然后我们打包成war包再看看他的war包物理路径,我们可以看到打印结果如下:

META-INF/MANIFEST.MF
META-INF/
WEB-INF/classes/
WEB-INF/classes/cn/
WEB-INF/classes/cn/com/
WEB-INF/classes/cn/com/
WEB-INF/classes/cn/com/server/
WEB-INF/classes/cn/com/server/action/
WEB-INF/classes/cn/com/server/action/JobAction.class
WEB-INF/classes/cn/com/server/addrsrv/
WEB-INF/classes/cn/com/server/addrsrv/GeoAddrSrv.class
…..

我们可以看到war类的文件目录和jar的文件目录明显不同,这样就能解释上面我所描述的问题。

Q: 那么我们怎么解决spring 扫描不到jar中class这个问题呢?

A: 有一种做法,就是打jar包的时候,打成JAR file, 然后选择 add directory entries, 如图:

这里写图片描述

然后这种打包方式,虽然能解决spring 扫描不到jar中class文件问题,但是打并不是我们想要的,我们想要的是一个可以执行jar,也就是Runable JAR FILE。

Q: 那么我们怎么打包成Runable JAR FILE,并且解决spring 扫描不到jar中class的问题?

A:

1、首先使用Eclipse打包,打包成JAR file。
2、上传到Linux, 解压my.jar

unzip my.jar -d myapp

3、进入 myapp文件夹, 使用以下命令:

java -Djava.ext.dirs=WebContent/WEB-INF/lib cn.com.test

大功告成

其他技巧:除了上诉使用代码方式查看jar包物理路径,我们还可以是 jar tr my.jar来查看。如图:
这里写图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring,使用@Autowired注解来自动装配Bean,可以将一个Bean注入到另一个Bean。但是,如果想要注入的Bean在一个jar包,而不是在当前项目,就需要注意一些问题。以下是几种可能的解决方案: 1. 确认jar包已被正确引用 首先需要确认你所使用的jar包已经被正确引用。可以在你的项目查看是否已经将该jar包添加到依赖。如果没有添加成功,可以尝试手动将该jar包添加到项目。 2. 确认的包路径是否正确 如果jar包已被正确引用,但是加载不到某个,那么可能是因为的包路径不正确。可以通过反编译jar包class文件或者查看jar包的文档来确定的完整包路径。 3. 使用@ComponentScan注解Spring Boot,可以使用@ComponentScan注解扫描指定的包,以便自动装配Bean。可以在主上添加@ComponentScan注解,并指定要扫描的包路径,例如: ``` @SpringBootApplication @ComponentScan(basePackages = {"com.example.package1", "com.example.package2"}) public class MyApplication { ... } ``` 这样就可以扫描指定的包,自动装配Bean。 4. 使用@Import注解 另一种解决方案是使用@Import注解,将jar包的配置导入到当前项目。可以在主上添加@Import注解,并指定要导入的jar包的配置,例如: ``` @SpringBootApplication @Import(com.example.mypackage.MyConfig.class) public class MyApplication { ... } ``` 这样就可以将jar包的配置导入到当前项目,自动装配Bean。 总之,如果无法加载jar包,请先确认jar包已被正确引用,并检查的完整包路径是否正确。如果仍然无法加载,可以尝试使用@ComponentScan或@Import注解解决问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值