在Web层集成Struts2与Spring

        最近学习传智播客的巴巴运动网教程,在集成Struts与Spring时,遇到很多麻烦,在此记录,希望对以后遇到同样问题的人有所帮助。教程中集成的是Struts1,我选择的是Struts2。两者还是有所不同的。比如,默认情况下,Struts1的action使用单例模式,容易引起线程安全问题;Struts2使用的是原型模式,不存在线程安全问题;框架使用起来很方便,前提是你得配置好。

      项目框架如下:


      附上源码:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>babasport</display-name>
  <context-param>
	  <param-name>contextConfigLocation</param-name>
	  <param-value>/WEB-INF/classes/beans.xml</param-value>
	</context-param>
	<!-- 对Spring容器进行实例化 -->
	<listener>
	  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
  <filter>
    <filter-name>struts</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  
    <context:component-scan base-package="cn.luecc"/>
    <context:property-placeholder location="classpath:jdbc.properties"/>
  
    <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${driverClassName}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="initialSize" value="${initialSize}" />
		<property name="maxActive" value="${maxActive}" />
		<property name="maxIdle" value="${maxIdle}" />
		<property name="minIdle" value="${minIdle}" />
    </bean>
    <!-- 
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="luecc"/>
    </bean>  
    -->
   
    <bean id="entityManagerFactory"  
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <property name="persistenceXmlLocation"  
            value="classpath:META-INF/persistence.xml" />  
        <property name="loadTimeWeaver">  
            <bean  
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />  
        </property>  
    </bean> 
	<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  		<property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven transaction-manager="txManager"/> 
</beans>
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <!-- 指定Web应用的默认编码集,相当于调用 HttpServletRequest的setCharacterEncoding方法 -->
    <constant name="struts.i18n.encoding" value="UTF-8" />
    <!-- 该 属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即 所有匹配*.action的请求都由Struts 2处理。如 果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开 -->
    <constant name="struts.action.extension" value="do" />
    <!-- 设 置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好 关闭 -->
    <constant name="struts.serve.static.browserCache " value="false" />
    <!-- 当 struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生 产环境下使用),开发阶段最好打开 -->
    <constant name="struts.configuration.xml.reload" value="true" />
    <!-- 开 发模式下使用,这样可以打印出更详细的错误信息 -->
    <constant name="struts.devMode" value="true" />
    <!-- 默 认的视图主题 -->
    <constant name="struts.ui.theme" value="simple" />
    <!-- 该 属性指定Struts 2中的action由Spring容器创 建 -->
    <constant name="struts.objectFactory" value="spring" />
  
	<package name="productType" extends="struts-default" namespace="/product">
		<action name="type" class="cn.luecc.web.action.product.ProductTypeAction">
			<result>/test.jsp</result>
		</action>
	</package>
</struts>
     上面的代码应该注意两个地方:

        <constant name="struts.action.extension" value="do" />

     在加了这个配置后,在访问是action的后缀名为.do,如:/xxx/yyy.do

       <constant name="struts.objectFactory" value="spring" />

      指定Struts2中的action由Spring容器创建

JDBC.properites 

driverClassName=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost:3306/babasport?useUnicode=true&characterEncoding=UTF-8
username=root
password=chen
initialSize=1
maxActive=100
maxIdle=8
minIdle=1

persistence.xml 

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
             http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">  
  
    <persistence-unit name="luecc" transaction-type="RESOURCE_LOCAL">   
        <provider>org.hibernate.ejb.HibernatePersistence</provider>  
        <properties>
            <property name="hibernate.max_fetch_depth" value="3" />   
            <property name="hibernate.hbm2ddl.auto" value="update" />   
            <property name="hibernate.show_sql" value="true" />   
            <property name="hibernate.format_sql" value="true" />   
        </properties>   
    </persistence-unit>    
</persistence> 

QueryResult.java 

package cn.luecc.bean;
import java.util.List;
public class QueryResult<T> {
	private List<T> resultList;
	private long totalRecord;
	
	public List<T> getResultList() {
		return resultList;
	}
	public void setResultList(List<T> resultList) {
		this.resultList = resultList;
	}
	public long getTotalRecord() {
		return totalRecord;
	}
	public void setTotalRecord(long totalRecord) {
		this.totalRecord = totalRecord;
	}
}

ProductType.java 

package cn.luecc.bean.product;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

@Entity
public class ProductType implements Serializable{
	private static final long serialVersionUID = 1105925869147292968L;
	/** 类别id **/
	private int typeid;
	/** 类别名称 **/
	private String name;
	/** 备注,用于google搜索页面描述 **/
	private String note;
	/** 是否可见 **/
	private Boolean visible = true;
	/** 子类别 **/
	private Set<ProductType> childTypes = new HashSet<ProductType>();
	/** 所属父类别 **/
	private ProductType parentType;
	@ManyToOne(cascade=CascadeType.REFRESH)
	@JoinColumn(name="parentid")
	public ProductType getParentType() {
		return parentType;
	}

	public void setParentType(ProductType parentType) {
		this.parentType = parentType;
	}
	@OneToMany(cascade={CascadeType.REFRESH,CascadeType.REMOVE},mappedBy="parentType")
	public Set<ProductType> getChildTypes() {
		return childTypes;
	}

	public void setChildTypes(Set<ProductType> childTypes) {
		this.childTypes = childTypes;
	}

	@Column(length=36,nullable=false)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Column(length=200)
	public String getNote() {
		return note;
	}

	public void setNote(String note) {
		this.note = note;
	}
	
	@Column(nullable=false)
	public Boolean getVisible() {
		return visible;
	}

	public void setVisible(Boolean visiable) {
		this.visible = visiable;
	}

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public int getTypeid() {
		return typeid;
	}

	public void setTypeid(int typeid) {
		this.typeid = typeid;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + typeid;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ProductType other = (ProductType) obj;
		if (typeid != other.typeid)
			return false;
		return true;
	}
}

DAO.java 

package cn.luecc.service.base;
import java.util.LinkedHashMap;
import cn.luecc.bean.QueryResult;
public interface DAO {
	/**
	 * 保存实体
	 * @param entity
	 */
	public void save(Object entity);
	/**
	 * 更新实体
	 * @param entity
	 */
	public void update(Object entity);
	/**
	 * 删除实体
	 * @param entityid
	 */
	public <T> void delete(Class<T> entityClass,Object entityid);
	/**
	 * 删除实体
	 * @param entityids 实体id数组
	 */
	public <T> void delete(Class<T> entityClass,Object[] entityids);
	/**
	 * 获取实体
	 * @param <T>
	 * @param entityClass
	 * @param entityId
	 * @return
	 */
	public <T> T find(Class<T> entityClass,Object entityId);
	/**
	 * 获取分页数据
	 * @param <T>
	 * @param entityClass 实体类
	 * @param firstIndex 分页的开始点
	 * @param maxResult 设置当前分页的记录数量
	 * @return
	 */
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,int firstIndex,int maxResult,String optionalSql,Object[] params,LinkedHashMap<String, String> strOrderby);
DaoSupport.java 
package cn.luecc.service.base;

import java.util.LinkedHashMap;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cn.luecc.bean.QueryResult;

@Transactional
public abstract class DaoSupport implements DAO {
	@PersistenceContext protected EntityManager em;

	public void save(Object entity) {
		em.persist(entity);
	}

	public void update(Object entity) {
		em.merge(entity);
	}

	public <T> void delete(Class<T> entityClass,Object entityid) {
		delete(entityClass,new Object[]{entityid});
	}

	public <T> void delete(Class<T> entityClass,Object[] entityids) {
		for(Object id : entityids) {
			em.remove(em.getReference(entityClass, id));
		}
	}
	@Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
	public <T> T find(Class<T> entityClass, Object entityId) {
		return em.find(entityClass, entityId);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED)
	public <T> QueryResult<T> getScrollData(Class<T> entityClass,
			int firstIndex, int maxResult,String optionalSql,Object[] params,LinkedHashMap<String, String> strOrderby) {
		QueryResult<T> qr = new QueryResult<T>();
		String entityName = getEntityName(entityClass);
		Query query = em.createQuery("select o from "+entityName+" o "+(optionalSql==null?"":"where "+optionalSql) + bulidOrderbyString(strOrderby));
		setQueryParams(query,params);
		query.setFirstResult(firstIndex).setMaxResults(maxResult);
		qr.setResultList(query.getResultList());
		query = em.createQuery("select count(o) from "+entityName+" o "+(optionalSql==null?"":"where "+optionalSql));
		setQueryParams(query,params);
		qr.setTotalRecord((Long)query.getSingleResult());
		return qr;
	}
	/**
	 * 获取实体名称
	 * @param <T>
	 * @param entityClass 实体类
	 * @return
	 */
	protected <T> String getEntityName(Class<T> entityClass) {
		String entityName = entityClass.getSimpleName();
		Entity entity = entityClass.getAnnotation(Entity.class);
		if(entity.name()!=null &&!"".equals(entity.name())) {
			entityName = entity.name();
		}
		return entityName;
	}
	/**
	 * 取得orderby子句
	 */
	protected String bulidOrderbyString(LinkedHashMap<String, String> strOrderby) {
		StringBuffer sb = new StringBuffer();
		if(strOrderby!=null && strOrderby.size()>0) {
			sb.append(" order by ");
			for(String key : strOrderby.keySet()) {
				sb.append("o.").append(key).append(" ").append(strOrderby.get(key)).append(",");
			}
			sb.deleteCharAt(sb.length()-1);
		}
		return sb.toString();
	}
	protected void setQueryParams(Query query,Object[] params){
		if(params!=null && params.length>0) {
			for(int i=0;i<params.length;i++) {
				query.setParameter(i+1, params[i]);
			}
		}
	}
}

ProductTypeService.java 

package cn.luecc.service.product;

import cn.luecc.service.base.DAO;

public interface ProductTypeService extends DAO{

}

ProductTypeServiceBean.java

package cn.luecc.service.product.impl;

import javax.persistence.Query;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.luecc.service.base.DaoSupport;
import cn.luecc.service.product.ProductTypeService;
/**
@Service相当于配置一个bean
@Transactional采用spring默认事物管理机制
 **/
@Service
@Transactional
public class ProductTypeServiceBean extends DaoSupport implements ProductTypeService {
	@Override
	public <T> void delete(Class<T> entityClass, Object[] entityids) {
		StringBuffer jpql = new StringBuffer();
		if(entityids!=null && entityids.length>0) {
			for(int i=0; i<entityids.length; i++) {
				jpql.append("?").append(i+2).append(",");
			}
			jpql.deleteCharAt(jpql.length()-1);
			Query query = em.createQuery("update ProductType pt set pt.visible = ?1 where pt.typeid in("+ jpql.toString() +")").setParameter(1, false);
			for(int i=0; i<entityids.length; i++) {
				query.setParameter(i+2, entityids[i]);
			}
			query.executeUpdate();
		}		
	}
}

ProductTypeAction.java 

package cn.luecc.web.action.product;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Controller;

import cn.luecc.bean.product.ProductType;
import cn.luecc.service.product.ProductTypeService;
import com.opensymphony.xwork2.ActionSupport;

@Controller
public class ProductTypeAction extends ActionSupport {
	private static final long serialVersionUID = -1290944953355785357L;
	/*
	private static ApplicationContext cxt;
	private static ProductTypeService productTypeService;	
	@Override
	public String execute() throws Exception {
		cxt = new ClassPathXmlApplicationContext("beans.xml");
		productTypeService = (ProductTypeService)cxt.getBean("productTypeServiceBean");
		ProductType productType = productTypeService.find(ProductType.class, 3);
		HttpServletRequest request = ServletActionContext.getRequest();
		request.setAttribute("producttype", productType);
		return SUCCESS;
	}
	*/
	@Resource(name="productTypeServiceBean")
	private ProductTypeService productTypeService;
	private ProductType productType;
	@Override
	public String execute() throws Exception {
		productType = productTypeService.find(ProductType.class, 3);
		HttpServletRequest request = ServletActionContext.getRequest();
		request.setAttribute("producttype", productType);
		return SUCCESS;
	}
}
         这里应该注意两点,如果不采用@Resource方式注入bean的话,请参考注释中的代码,重写构造一个bean。使用@Controller注解,Spring将扫描指定的路径获得需要的资源,这里注意如果@Controller使用默认配置,那么struts.xml文件中action的class属性应该与这里的ProductTypeAction.java名字一致。如果这里不使用注解的方式,可以参考注释中的代码, 相应的在配置文件struts.xml 中也不需要那么复杂,具体请自己思考减少配置代码。

test.jsp(这里有好多中方法,可以使用el表达式测试,下面使用基本的jsp代码)

<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>
<%@ page import="cn.luecc.bean.product.*" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
ProductType type = (ProductType)request.getAttribute("producttype");
%>
<html>
  <head>
    <base href="<%=basePath%>">    
    <title>产品列表</title>
  </head> 
  <body>
   	<%=type.getName()%>
  </body>
</html>
在实际测试中遇到很多问题,最主要的就是下面两个:

出现异常:

Cannot locate the chosen ObjectFactory implementation: spring

看到网上说原因是  
没有添加struts2-spring-plugin-XXXX.jar  
但是很多人早就已经添加了struts2-spring-plugin-2.0.11.1.jar包,还是会出现上面的异常,在这里请把这个包复制到
/WebRoot/WEB-INT/lib目录下即可解决。

还有一个最常见的异常就是:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productTypeServiceBean': Injection of persistence fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [beans.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Cannot parse persistence unit from class path resource.......

    出现这种错误应该是没有指定Struts2中的factory由Spring容器创建,而你在程序中使用了各种Spring的注解,所以应该仔细检查程序中的配置文件,具体可以参考上面的struts.xml文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值