首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

struts2默许拦截器之autowiring

2012-06-27 
struts2默认拦截器之autowiring? ?在struts2的struts-default.xml中定义了一个name为autowiring拦截器,实

struts2默认拦截器之autowiring

? ?在struts2的struts-default.xml中定义了一个name为autowiring拦截器,实现类是com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor,它的作用是在struts2和spring整合时为action注入spring上下文ApplicationContext(Action需要实现org.springframework.context.ApplicationContextAware接口),并使用com.opensymphony.xwork2.inject.Container对象为action注入其他属性。?
????首先说明一下struts2与spring的整合。?
????要实现struts2与spring的整合,只需将struts2-spring-plugin-2.x.x.jar加入到项目中即可。在该jar包中存在文件struts-plugin.xml,在struts2启动时会被加载,文件内容如下:

?

<struts>    <bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class ="org.apache.struts2.spring.StrutsSpringObjectFactory" />    <!-- Make the Spring object factory the automatic default -->    <constant name="struts.objectFactory" value="spring" />    <constant name="struts.class.reloading.watchList" value="" />    <constant name="struts.class.reloading.acceptClasses" value="" />    <constant name="struts.class.reloading.reloadConfig" value="false" />    <package name="spring-default">        <interceptors>            <interceptor name="autowiring" class ="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>        </interceptors>    </package></struts>

?????这里它将struts2框架常量struts.objectFactory设置为"spring",这里其实是使用了缩写形式,全称是"org.apache.struts2.spring.StrutsSpringObjectFactory"。这个缩写的"spring"是和bean配置中的name属性相对应的。默认情况下struts2框架创建的对象都是由ObjectFactory实例化的,ObjectFactory提供了与其他IoC容器(如Spring等)集成的方法。覆盖这个ObjectFactory的类必须继承ObjectFactory类或者它的任何子类,并且带有一个无参构造方法或者构造方法中的参数全部使用struts2的@Inject注解。上述代码使用org.apache.struts2.spring.StrutsSpringObjectFactory代替了默认的ObjectFactory。该ObjectFactory使得struts2中的bean可以由spring来装配,默认情况下框架使用的自动装配策略是name,也就是说框架会根据spring中bean的name属性自动装配,可选的装配策略还有type、auto、constructor,我们可以根据常量struts.objectFactory.spring.autoWire来进行设置。?

????在struts2与spring进行整合的时候还需要在web.xml中添加如下信息来启动spring:

?

<listener>     <listener-class >org.springframework.web.context.ContextLoaderListener</listener-class ></listener>

?但如果没有配置这个会出现什么情况呢?如果未配置上述代码,启动web服务器会出现如下错误FATAL 2011-12-28 11:53:34:272 - ********** FATAL ERROR STARTING UP

STRUTS-SPRING INTEGRATION **********Looks like the Spring listener was not configured for your web app!Nothing will work until WebApplicationContextUtils returns a valid ApplicationContext.You might need to add the following to web.xml:     <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>ERROR 2011-12-28 11:53:34:288 - Dispatcher initialization failedjava.lang.NullPointerException        at com.opensymphony.xwork2.spring.SpringObjectFactory.getClassInstance(SpringObjectFactory.java:220)        …………

?????错误中显示是出现了NullPointerException(SpringObjectFactory.java:220), 出现该异常的代码是:

?

if(appContext.containsBean(className))

????此处的appContext是org.springframework.context.ApplicationContext,说明在SpringObjectFactory类(StrutsSpringObjectFactory的直接父类)中需要存在org.springframework.context.ApplicationContext对象的引用,此处的 ApplicationContext需要什么时候进行装配呢?在StrutsSpringObjectFactory类中只存在一个构造方法

?

public class StrutsSpringObjectFactory extends SpringObjectFactory {    private static final Logger LOG = LoggerFactory.getLogger(StrutsSpringObjectFactory.class);    //@Inject    //public StrutsSpringObjectFactory(    //        @Inject(value=StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE,required=false) String autoWire,    //        @Inject(value=StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE,required=false) String useClassCacheStr,    //        @Inject ServletContext servletContext) {    //    this(autoWire, "false", useClassCacheStr, servletContext);    //}    /**     * Constructs the spring object factory     * @param autoWire The type of autowiring to use     * @param alwaysAutoWire Whether to always respect the autowiring or not     * @param useClassCacheStr Whether to use the class cache or not     * @param servletContext The servlet context     * @since 2.1.3     */    @Inject    public StrutsSpringObjectFactory(            @Inject(value=StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE,required=false) String autoWire,            @Inject(value=StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE_ALWAYS_RESPECT,required=false) String alwaysAutoWire,            @Inject(value=StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE,required=false) String useClassCacheStr,            @Inject ServletContext servletContext,            @Inject(StrutsConstants.STRUTS_DEVMODE) String devMode,            @Inject Container container) {                  super();        boolean useClassCache = "true".equals(useClassCacheStr);        LOG.info("Initializing Struts-Spring integration...");        Object rootWebApplicationContext =  servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);        if(rootWebApplicationContext instanceof RuntimeException){            RuntimeException runtimeException = (RuntimeException)rootWebApplicationContext;            LOG.fatal(runtimeException.getMessage());            return;        }        ApplicationContext appContext = (ApplicationContext) rootWebApplicationContext;        if (appContext == null) {            // uh oh! looks like the lifecycle listener wasn't installed. Let's inform the user            String message = "********** FATAL ERROR STARTING UP STRUTS-SPRING INTEGRATION **********\n" +                    "Looks like the Spring listener was not configured for your web app! \n" +                    "Nothing will work until WebApplicationContextUtils returns a valid ApplicationContext.\n" +                    "You might need to add the following to web.xml: \n" +                    "    <listener>\n" +                    "        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>\n" +                    "    </listener>";            LOG.fatal(message);            return;        }                String watchList = container.getInstance(String.class, "struts.class.reloading.watchList");        String acceptClasses = container.getInstance(String.class, "struts.class.reloading.acceptClasses");        String reloadConfig = container.getInstance(String.class, "struts.class.reloading.reloadConfig");        if ("true".equals(devMode)                && StringUtils.isNotBlank(watchList)                && appContext instanceof ClassReloadingXMLWebApplicationContext) {            //prevent class caching            useClassCache = false;            ClassReloadingXMLWebApplicationContext reloadingContext = (ClassReloadingXMLWebApplicationContext) appContext;            reloadingContext.setupReloading(watchList.split(","), acceptClasses, servletContext, "true".equals(reloadConfig));            LOG.info("Class reloading is enabled. Make sure this is not used on a production environment!", watchList);            setClassLoader(reloadingContext.getReloadingClassLoader());            //we need to reload the context, so our isntance of the factory is picked up            reloadingContext.refresh();        }        this.setApplicationContext(appContext);        int type = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;   // default        if ("name".equals(autoWire)) {            type = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;        } else if ("type".equals(autoWire)) {            type = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;        } else if ("auto".equals(autoWire)) {            type = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;        } else if ("constructor".equals(autoWire)) {            type = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;        } else if ("no".equals(autoWire)) {            type = AutowireCapableBeanFactory.AUTOWIRE_NO;        }        this.setAutowireStrategy(type);        this.setUseClassCache(useClassCache);        this.setAlwaysRespectAutowireStrategy("true".equalsIgnoreCase(alwaysAutoWire));        LOG.info("... initialized Struts-Spring integration successfully");    }}
?

?在上述代码中从servletContext中取到一个名称为"org.springframework.web.context.WebApplicationContext.ROOT"的属性值,将其强制转换为ApplicationContext类型,之后调用SpringObjectFactory类setApplicationContext方法将spring上下文传递给了SpringObjectFactory。spring上下文是什么时候放置到servletContext中的呢?我们接下来看一下org.springframework.web.context.ContextLoaderListener类,这个监听器到底做了什么? 我们知道ContextLoaderListener是在服务器初始化的时候执行contextInitialized这个方法,该方法的代码如下:

?

public void contextInitialized(ServletContextEvent event) {    this.contextLoader = createContextLoader();    this.contextLoader.initWebApplicationContext(event.getServletContext());}

?可以看出主要起作用的应该是initWebApplicationContext方法,初始化WebApplicationContext,我们进去看看:

?

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)throws IllegalStateException, BeansException {if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +"check whether you have multiple ContextLoader* definitions in your web.xml!");}servletContext.log("Initializing Spring root WebApplicationContext");if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try {// Determine parent for root web application context, if any.ApplicationContext parent = loadParentContext(servletContext);// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.this.context = createWebApplicationContext(servletContext, parent);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);if (logger.isDebugEnabled()) {logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");}if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");}return this.context;}catch (RuntimeException ex) {logger.error("Context initialization failed", ex);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}catch (Error err) {logger.error("Context initialization failed", err);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);throw err;}}

?????上面代码,将spring上下文放置到了servletContext中,因此只要通过org.springframework.web.context.ContextLoaderListener类启动了spring,那么在struts2中通过servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)或者ActionContext.getContext().getApplication().get(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)就能取到spring上下文对象

????spring把ApplicationContext放在application里面,然后struts2在application中把这个对象取出来,放在SpringObjectFactory里面,然后struts2与spring就实现了集成,当我们没有在web.xml里面配置监听器,也就是没有把ApplicationContext的对象放在application里面,所以struts2去application里面去取的时候会是null,这就是为什么没有配置监听器那里会抛NullPointerException异常。
????当struts2和spring集成之后,struts2将允许Spring创建Action、Interceptror和Result,并且由Struts创建的对象能够被Spring装配。这样就可以在配置action时不使用类名,而使用spring中bean的name值了。如果在spring中找不到与Action的class值相匹配的bean,struts2框架会根据class值初始化一个action对象,并对action对象进行装配。
????其实SpringObjectFactory已经对Action对象注入了ApplicationContext和其他struts2对象,那么autowiring拦截器岂不是没用了?有一种情况,当项目中同时使用了Struts2和spring,但未将struts.objectFactory的值设置为"spring",即Struts2的objectFactory还是默认的ObjectFactory类,这样action中就不会被注入spring的上下文对象,就需要使用autowiring拦截器了。autowiring拦截器会从application中获取到spring上下文对象,初始化一个SpringObjectFactory对象,使用该对象对Action对象进行装配。

?

?

版权所有,转载请标明出处:http://blogwarning.iteye.com/blog/1332214

热点排行