Spring component-scan broken in osgi container

*爱你&永不变心* 提交于 2019-12-04 08:37:15

I hate the idea of answering my own question but it seemed the best way to report back what I did. I've upvoted the answers so far because they were helpful for me to make a decision.

Through reading the answers I've learned a bit about why classpath scanning is bad. Nonetheless it's a pretty common part of the spring framework these days. My solution was to go "old skool" and manually define my beans.

Can't do this:

<context:component-scan base-package="com.company.application.services" />

Do this instead:

<bean id="service1" class="com.company.application.services.impl.Service1" />
<bean id="service2" class="com.company.application.services.impl.Service2" />

You can at least still do this:

<context:annotation-config />

Annotation config means spring will still wire your beans together, it's just the process of discovering the beans that is being done manually. That's the compromise.

Ideally I would have preferred a means to deploy the project with component scanning but from what I can gather this requires changing the way the project is built specifically for an osgi container. My answer means it'll work both in an osgi and a normal container so it's less specialized.

If someone finds a way to deploy a war file with spring component scanning then I'll gladly re-consider the accepted answer.

Thanks

For your web application to run with spring you need spring dm, and therefore you need to have something like the following:

    <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
</context-param>

Or for a complete sample take a look at the spring-osgi sample at pax-web: Sample web.xml

I tried commenting your initial post, however I can't do that due to my ranking in stackoverflow unfortunately. Don't get me wrong, I am not saying I have a solution here, but I would like share my workaround in newer version of Spring framework here.

Firstly, I have a big configuration class, which servers as the entry in this AnnotationConfigApplicationContext(PseudoSpringBootApplication.class) statement.

/**
 * This class serves as the entry point from OSGI framework to Spring framework
 *
 */
@Configuration
//@ComponentScan
//@SpringBootApplication
public class PseudoSpringBootApplication {

  @Autowired
  ApplicationContext context;

  @Bean
  DummySpringBean1 dummySpringBean1() {
    return new DummySpringBean("This is a dummy message");
  }

  @Bean
  DummySpringBean2 dummySpringBean2() {
    return new DummySpringBean("This is a dummy message");
  }

}

Basically, as @ComponentScan doesn't work with OSGI plugin, (as mentioned in you answer post, this is mainly due to the fact that Spring framework try to scan classes assuming the artifacts are in file system, however, in this case, OSGI (e.g., Apache Felix) has transformed the FILE:// prefixed url to its internal bundle:// prefixed. java.io.FileNotFoundException: URL [bundle://21.0:1/com/***] cannot be resolved to absolute file path because it does not reside in the file system: bundle://21.0:1/com/*** Nevertheless, @Autowired is still working. I've shown the example code below, where DummySpringBean2 has a dependency of DummySpringBean1 and is @Autowired annotated.

//@Component
class DummySpringBean1 {

  private final String dummyMessage;

  DummySpringBean1(String dummyMessage) {
    this.dummyMessage = dummyMessage;
  }
}

For DummySpringBean2, it has dependency on DummySpringBean1.

//@Component
class DummySpringBean2 {

  private final String dummyMessage;

  @Autowired
  private DummySpringBean1 bean1;

  DummySpringBean(String dummyMessage) {
    this.dummyMessage = dummyMessage;
  }
}

So I moved all bean declaration to the central configuration class above.

Classpath scanning is a terrible idea and it will break in many runtimes, not just in OSGi. The OSGi service registry is a much more effective approach to the decoupling problem.

You can use the OSGi Service Registry outside of OSGi itself, take a look at PojoSR.

As for how to register and consume OSGi services... since you are using Spring it would be best to use Blueprint, which is an evolution of an older project called Spring Dynamic Modules.

I did this little script for linux so I unpack TrackServer.jar and execute the main class without loosing simplicity from Spring (newb to spring here so not so pleased to maneuver and add extra code there)

# Remove any old file before new version
rm -rf tracker;
# Recreate executable folder
mkdir tracker;
# copy main jar to executable folder
cp TrackServer.jar tracker;
# move to executable folder
cd tracker;
# Extract jar on executable folder
jar xf TrackServer.jar;
# Execute main class (at the end of the line) 
# with all the jar dependencies that came 
# in original TrackerServer.jar
java -cp '.;'`ls -dm *.jar | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' | sed -e 's/,[ \n\r\t]/:/g'` incodemode.mainApp.Main;
# sed bits are for getting .:dependency1.jar:dependency2.jar etc...
# incodemode.mainApp.Main is the full class path of the Main class
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!