之前一朋友问我strut2和spring集成时使用aop后造成注入失败:关于struts2-spring整合的问题,我就分析一下struts2如果和spring集成的,并解决这个问题。
此问题已经提交到struts2的JIRA,2.3.16将修复;https://issues.apache.org/jira/browse/WW-4110。
问题:
但是当我对action类加了spring-aop之后,写@resource不加getset方法的情况下又报错了,好像spring的注解又失效了,不知道这是为什么,请问您碰到过这种情况吗。
默认情况下,失败的情况只有两种:
当如<action name="myAction" class="cn.javass.MyAction">时,即没有交给spring管理:
1、 @Autowired private Resource resource; 但无setter方法;
2、有setter方法,如setResource(),但无名字为resource的bean。
分析
首先介绍下struts2怎么去和spring集成的。
1、如果需要和spring集成,需要添加struts2-spring-plugin-***.jar,此处我们使用struts2-spring-plugin-2.3.1.1.jar;
2、接着struts2在启动时会按照如下顺序加载配置文件:
struts-default.xml 默认配置
struts-plugin.xml 插件配置(可以多个)
struts.xml 我们自己的
3、自动注册StrutsSpringObjectFactory为struts2的ObjectFactory,因此拿struts2的组件等都是问它要;
<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" />
4、 StrutsSpringObjectFactory继承了com.opensymphony.xwork2.spring.SpringObjectFactory,主要做了几件事情:
4.1、获取ApplicationContext:servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
4.2、获取自动装配的机制,可以在struts.xml配置常量:struts.objectFactory.spring.autoWire,默认是name;这个可以参考http://jinnianshilongnian.iteye.com/blog/1415461,3.3.3 自动装配;
5、接下来如果要获取如action,需要:
@Override public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception { Object o; if (appContext.containsBean(beanName)) { o = appContext.getBean(beanName); } else { Class beanClazz = getClassInstance(beanName); o = buildBean(beanClazz, extraContext); } if (injectInternal) { injectInternalBeans(o); } return o; }
5.1、首先查找spring容器有木有,如果有直接返回;
5.2、否则调用buildBean(beanClazz, extraContext);创建新的bean;即<action name="myAction" class="cn.javass.MyAction"> class配置的不是beanName时;
6、buildBean(beanClazz, extraContext);
@Override public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception { Object bean; try { // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies if (alwaysRespectAutowireStrategy) { // Leave the creation up to Spring bean = autoWiringFactory.createBean(clazz, autowireStrategy, false); injectApplicationContext(bean); return injectInternalBeans(bean); } else { bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false); bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName()); // We don't need to call the init-method since one won't be registered. bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName()); return autoWireBean(bean, autoWiringFactory); } } catch (UnsatisfiedDependencyException e) { if (LOG.isErrorEnabled()) LOG.error("Error building bean", e); // Fall back return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory); } }
6.1、 如果是alwaysRespectAutowireStrategy,默认false,这个对于大家来说直接忽略,遗留策略,现在可以直接忽略。
6.2、正常情况会走到else中:
6.2.1、首先通过bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false)创建Bean;
6.2.2、执行Bean预处理:bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName()),此时可能发生@PostConstruct的初始化及一些Aware接口的注入;
6.2.2、执行Bean的后处理:bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName()),此时可能发生创建代理对象;
6.2.3、最后调用autoWireBean(bean, autoWiringFactory)装配对象,此时会调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的populateBean完成,完成1、如果支持自动装配,会执行自动专配;2、如果有支持@Autowired注解,那么会调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues;
如上的流程可以参考如下文章,有详细讲解:
Spring开闭原则的表现-BeanPostProcessor的扩展点-1
Spring开闭原则的表现-BeanPostProcessor扩展点-2
但是getBean的流程是(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory):
1、InstantiationAwareBeanPostProcessor.applyBeanPostProcessorsBeforeInstantiation
2、InstantiationAwareBeanPostProcessor.applyBeanPostProcessorsAfterInitialization
如果此时有bean返回 直接返回;
3、doCreateBean
3.1、createBeanInstance
3.2、populateBean
3.2.1、InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
3.2.2、autowire, for example AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE
3.2.3、InstantiationAwareBeanPostProcessor.postProcessPropertyValues
3.2.4、applyPropertyValues
4、initializeBean
4.1、invokeAwareMethods
4.2、BeanPostProcessor.postProcessBeforeInitialization
4.3、invokeInitMethods
4.4、BeanPostProcessor.postProcessAfterInitialization
所以综上,SpringObjectFactory的实现是存在问题的,我给了一个正确的方式:
//1、instantiation bean = autoWiringFactory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false); //2、autowire bean = autoWireBean(bean, autoWiringFactory); //3、initialization bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
这才是正确的步骤。
如果按照struts2默认的实现,
bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());会执行创建代理,那么bean此时是代理对象;
那么接下来autoWireBean(bean, autoWiringFactory);会造成@Autowired private Resource resource; 但无setter方法;注入失败。
到此分析完毕,已经提交struts2 jira:
https://issues.apache.org/jira/browse/WW-4110
正确的SpringObjectFactory在附件中,请下载测试
相关推荐
struts2+spring+ibatis+mysql AOP日志管理,异常捕获 tomcat6.0+jdk1.6
struts2+spring2.5用AOP记录操作日志,带自定义参数aop的例子
struts2+spring4+hibernate5的所有jar包所有jar包包括spring Aop基本包、spring Ioc基本包、springweb开发包、spring事务控制、spring整合junit、spring整合struts包、hibernate包、hibernate整合spring包、struts2...
Spring+proxool+hibernate+struts2+aop整合的完整_Jar包
SSH框架集成是较复杂和难理解的,只有在不断的练习和使用中才能慢慢的理解其中的原理,仅凭看视频是远远不够的,因为这些涉及了尤其是spring底层的好多类以及控制翻转(IOC)和面向切面(AOP)编程的思想,不过在讲述...
Spring+proxool+hibernate+struts2+aop整合的完整的简单项目
本次上传的是一个SpringAOP + struts2+Hribernate的实际案例。脚本都在工程的src的目录下。
按照ssh的基本应用逐步添加的jar包,包含AOP切面编程的包。事务的提交建议用AOP去做。
struts2+spring4+mybatis3,登录jquery ajax,struts拦截器,springAOP的例子。带部分注释。
SSI(Struts2 + Spring + IBatis)框架集成。
现在的结构是,Struts负责显示层,Hibernate负责持久层,Spring负责中间的业务层,另外,由于Spring使用的依赖注射以及AOP(面向方面编程),所以它的这种内部模式非常优秀,以至于Spring自己也实现了一个使用依赖注射...
NULL 博文链接:https://ylxy3058.iteye.com/blog/2224244
Struts+Hibernate+Spring+三个框架简介,对于spring框架、IOC和AOP、IOC容器等进行了详细的介绍。
项目作品名称: 基于Struts2+Hibernate+Spring框架的超市信息管理系统 使用JQuery datatable插件浏览从数据库查询的信息记录(不少于30条记录)。 使用JQuery dropzone插件把客户端图片上传至服务器,并把该图片...
通过修改相关配置文件位置,和导入的架包,终于修复了一些错误,实现了spring和struts2的完美结合,并使用了spring的aop和日志功能,本人亲自测试过,没问题,方便下载使用
最近温习ssh2整合编程,顺便浏览下struts2有什么更新的消息,下载了新版本的struts2的2.1.8.1版,使用的是MyEclipse8.0开发,但是问题就随之而来了。MyEclipse8.0中自带的struts2版本是2.1.6,spring版本有2.0,2.5...
这个Demo主要是练习ssh中spring的AOP做的,比较简单,就是输入用户名密码后保存到数据库中然后再读出来。其中在保存数据和查询数据的方法上用spring的AOP实现了日志输出...
Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解实例详解,包含ssh最新框架的集成,IOC注入,AOP面向切面编程.
Struts2+Ibatis+Spring3.2的一个完整整合例子,完美整合springAOP、Srping Tansction,以及详解