正在 OSGi 容器中加载 spring dm

Loading spring dm in OSGi container

我正在尝试在我的 OSGi 包中加载 spring dm。我关注了一个tutorial。我的目标是,对于特定的 URL,URL 应该由 spring 而不是默认的 sling servlet 处理。我部分成功地实现了同样的目标。

有时,它不起作用。我的 globaldispatcher servlet 没有被初始化。 问题是间歇性的。有时我的 spring servlet 初始化得很好。但有时我会收到此错误:

[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet FrameworkServlet 'globaldispatcher': initialization started
[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet Context initialization failed
java.lang.IllegalArgumentException: bundle context should be set before refreshing the application context
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.normalRefresh(AbstractDelegatedExecutionApplicationContext.java:179)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$NoDependenciesWaitRefreshExecutor.refresh(AbstractDelegatedExecutionApplicationContext.java:89)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:175)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:467)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:483)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:358)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:325)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.createServlet(SlingServletResolver.java:988)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.bindServlet(SlingServletResolver.java:936)
at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)

Java 版本:1.7.0_45

我正在使用 tutorial 中提到的以下依赖项:(所有都是 OSGi 包)

Spring AOP v3.0.6.RELEASE 
Spring ASM v3.0.6.RELEASE 
Spring Aspects v3.0.6.RELEASE 
Spring Beans v3.0.6.RELEASE 
Spring Context v3.0.6.RELEASE 
Spring Context Support v3.0.6.RELEASE 
Spring Core v3.0.6.RELEASE 
Spring Expression v3.0.6.RELEASE
Spring Web v3.0.6.RELEASE 
Spring Web Servlet v3.0.6.RELEASE 
Spring OSGi IO v1.2.1 
Spring OSGi Core v1.2.1 
Spring OSGi Extender v1.2.1 
Spring OSGi Annotation v1.2.1 
Spring OSGi Web v1.2.1 
AOP Alliance v1.0.0
CGLib 2.2.0
Commons Lang v2.6 
Commons Codec v1.5 
Commons Logging v1.1.1 
ASM v3.2.0 
JSR 330 (javax.inject) v1.0.0 
JSR 250 (javax.annotation) v1.0.0

以下是我的 spring-osgi.xml 中的代码,它位于 META-INF/spring/ 文件夹

 <osgi:service ref="globalSlingDispatcherServlet">
<osgi:interfaces>
  <value>javax.servlet.Servlet</value>
</osgi:interfaces>

<osgi:service-properties>
  <entry key="sling.servlet.resourceTypes" value="examples/sling/spring/mvc/dispatcher/global" />
  <entry key="sling.servlet.extensions">
    <array>
      <value>json</value>
      <value>html</value>
    </array>
  </entry>
  <entry key="sling.servlet.methods">
    <array>
      <value>GET</value>
      <value>POST</value>
    </array>
  </entry>
  <entry key="contextClass" value="org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"/>
  <entry key="sling.core.servletName" value="globaldispatcher" />
</osgi:service-properties>

下面是来自 META-INF\spring\spring.xml

的代码
<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc.sling" />
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />

以下是来自WEB-INF/globaldispatcher

的代码
<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc">
    <context:exclude-filter type="regex"
        expression="net\.jasonday\.examples\.sling\.spring\.mvc\.sling\..*" />
</context:component-scan>
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />

SlingConfiguration.java :

@Configuration
public class SlingConfiguration {
@Bean
@DependsOn("slingContextLoader")
public SlingDispatcherServlet globalSlingDispatcherServlet() {
    return new SlingDispatcherServlet();
    }
}

SlingContextLoader.java :

@Component
public class SlingContextLoader extends ContextLoader {

private ServletContext servletContext;

private ApplicationContext applicationContext;

public ApplicationContext getApplicationContext() {
    return applicationContext;
}

@Inject
public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

public ServletContext getServletContext() {
    return servletContext;
}
@ServiceReference
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}
@Override
protected Class<?> determineContextClass(
        ServletContext currentServletContext) {
    return OsgiBundleXmlWebApplicationContext.class;
}
@Override
protected ApplicationContext loadParentContext(
        ServletContext currentServletContext) {
    return applicationContext;
}

@PostConstruct
public void init() {
    initWebApplicationContext(servletContext);
}
@PreDestroy
public void destroy() {
    closeWebApplicationContext(servletContext);
}
}

我的SlingDispatcherServlet.java

public class SlingDispatcherServlet extends DispatcherServlet implements     Servlet {
  @PreDestroy
  @Override
  public void destroy() {
super.destroy();
String attrName = getServletContextAttributeName();
getServletContext().removeAttribute(attrName);
  }
}

造成此问题的原因是,由于 OSGi class 加载问题,@PostConstruct 等 Spring 注释有时无法按预期工作。

因此,解决方法是不依赖注释。下面的代码为我解决了这个问题。

我在 SlingContextLoader 的 setServletContext() 中手动调用 init() 方法

@ServiceReference
public void setServletContext(ServletContext servletContext) {
    log.info("in setServletContext");
    this.servletContext = servletContext;
    init();
}

在 init() 方法中我添加了以下代码:

//@PostConstruct
public void init() {
    log.info("in init");
    //Remove existing application context if any
    if (servletContext
            .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        log.info("removing existing application context to create new one");
        closeWebApplicationContext(servletContext);
    }
    initWebApplicationContext(servletContext);
}