完成了Spring和Hibernate的整合,最后一步我们将对Struts2和Spring进行整合。编写一个struts.xml的文件,该文件存放在src目录下。
配置由Spring实例化Struts2,需要Spring框架的支持
<constant name="struts.objectFactory" value="spring"></constant>
配置国际化编码
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
配置convention-plugin注解插件,要让Struts2支持注解,这一步是必须的!
<constant name="struts.convention.default.parent.package" value="default" />
完整配置如下
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="false" /> <constant name="struts.objectFactory" value="spring"></constant> <!--charset--> <constant name="struts.i18n.encoding" value="UTF-8"></constant> <!-- convention-plugin --> <constant name="struts.convention.default.parent.package" value="default" /> <package name="default" extends="struts-default,json-default"> <interceptors> <interceptor-stack name="securedStack"> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <default-interceptor-ref name="securedStack"></default-interceptor-ref> <global-results> <result name="error">/error.jsp</result> <result name="invalid.token">/error.jsp</result> <result name="login" type="redirect">/login</result> </global-results> </package> </struts>
在web.xml中增加Struts2的过滤器,将Struts2集成到WEB工程中。
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter>
前面我们已经完了SSH架构的整合,为了让程序更容易调试和跟踪问题,我们把log4j加上,以便排查项目开发中出现的各种异常。本配置使用了apache的slf4j框架,slf4j框架对log4j进行了集成,使log4j的使用更简单,slf4j是开源社区中非常火的框架。
log4j.debug=false log4j.rootLogger=debug,Stdout # please keep this setting FATAL to avoid questions from users # why there are stacktraces in the test output. You can turn it # down if you need to when testing, but don't check it in. (eelco) # changing back to ERROR. Looks like in some cases the log4j.properties # in wicket gets picked which results in not printing the exceptions # and that can be a bit dangerous (matej) log4j.logger.com.ssh=info log4j.logger.DBstep=error log4j.logger.org.hibernate=info log4j.logger.org.apache.struts2=debug log4j.logger.org.springframework=debug log4j.logger.com.opensymphony=info log4j.appender.Stdout=org.apache.log4j.ConsoleAppender log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n
Spring的注解有很多,我们只介绍在架构中常用到的注解,分别为@Resource、@Autowired、@Service、@Repository、@Component、@Transactional六个注解。
DI注解:
@Resource其实不是Spring中的注解,而是JAVA中的标准注解,是JSR-250中的一个规范,Spring对其进行了实现。使用@Resource注解可以注入JavaBean,默认按照名字注入JavaBean。
@Resource(name="personDao") IPersonDao personDao;
@Autowired是Spring的一个注解,作用和@Resource是一样的,@Autowired默认按照类型注入。
IOC注解:
@Service用来标注在Service类上,表示让Spring来实例化Service类,也就是我们所说的IOC。
<span style="font-size:14px;">@Service //标注service层 public class PersonService implements IPersonService </span>
@Repository用来标注在DAO类或是数据访层的类中,作用和@Service一样。
@Repository //标注dao层 public class PersonDao extends GenericReposistoryHibernate implements IPersonDao
@Component用来标注普通的JavaBean,例如POJO和MODEL类等。
@Component("personPojo" )// 把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/> public class PersonPojo extends GenericObject
事务注解:
@Transactional是事务注解,使用该注解可以让Spring代替我们接管事务,不用每次都由程序员来提交和回滚事务。
之前讲到过Hibernate中使用注解用到的是JPA。JPA其实也是JAVA中的规范之一,是与EJB3一起诞生的产物,全称为Java Persistence API(JAVA持久化API)。使用JPA可以实现Java Bean的ORM映射,包括对象和表、属性和字段、对象依赖和表间关系的映射(例如一对一、一对多、多对多的映射)。下面的示例是JPA的基本使用方法:
<span style="font-size:14px;">@Entity //标注该类为实体类。 @Table(name = "person") //标注对应的数据库表明。 @Component("personPojo" )// 把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/> public class PersonPojo extends GenericObject { /** * */ private static final long serialVersionUID = 1L; private String name; @Column(name = "name", unique = true) public String getName() { return name; } public void setName(String name) { this.name = name; } } </span>
GenericObject类,该类提供一个父类的ID,所有继承该类的子类都具有一个ID
<span style="font-size:14px;">@MappedSuperclass public abstract class GenericObject{ private static final long serialVersionUID = 1L; private String id; public GenericObject() { super(); } @Id @Column(name = "id", length = 36, nullable = true) @GeneratedValue(generator = "hibernate-uuid.hex") @GenericGenerator(name = "hibernate-uuid.hex", strategy = "uuid.hex") public String getId() { return id; } public void setId(String id) { this.id = id; } } </span>
使用<prop key="hibernate.hbm2ddl.auto">update</prop>属性可以使Hibernate自动创建和修改表,而不用再手工修改。
关于如何使用JPA,请参考我的另一篇博文《JPA在Hibernate中的使用》。这篇文章详细介绍了JPA的语法和使用方法。
Struts2框架的注解是使用convention-plugin插件实现的,关于Struts2插件的使用请参考我的另一篇博文《在Struts2框架中使用注解》。
在使用JDBC或者是Hibernate对数据库的进行增删改查操作时,通常都需要使用事务。事务是保证数据一致性和完整性的重要技术,如果使用不当,可能会产生不完整的交易数据,所以在软件开发中对事务的控制一定要认真对待。Spring为我们提供了AOP切面事务,使程序员不用自行控制事务。使用Spring事务我们只需要标注@Transactional注解即可,Spring会自动接管方法中的事务。我们可以定一个数据库访问层的公共类,所有的DAO类都继承至该类,而不用在每个DAO都使用@Transactional注解。
/** * 持久化基类 * * @author 山人 * */ @Transactional @Component("repositoryImp") public class GenericReposistoryHibernate { @Resource protected SessionFactory sessionFactory; private Session session = null; @Transactional public <T, PK extends Serializable> PK create(T newInstance) { PK save = (PK) getSession().save(newInstance); return save; } @Transactional public <T> void delete(T transientObject) { getSession().delete(transientObject); } @Transactional public <T> void update(T transientObject) { getSession().update(transientObject); } @Transactional(readOnly=true) public <T> List<T> findAll(Class<T> type) { return getSession().createCriteria(type).list(); } public <T> List<T> findHQLByParam(String hql, int firstResult, int maxResult, Object... params) { Query query = getSession().createQuery(hql); for (int i = 0; i < params.length; i++) { query.setParameter(i + 1, params[i]); } query.setMaxResults(maxResult); query.setFirstResult(firstResult); return query.list(); } @Transactional(readOnly=true) public <T> List<T> findAllValid(Class<T> clas) { return getSession().createCriteria(clas).list(); } @Transactional(readOnly=true) public <T, PK extends Serializable> T get(Class<T> type, PK id, boolean block) { if (block) return (T) getSession().get(type, id, LockMode.UPGRADE); else return (T) getSession().get(type, id); } public Session getSession() { if (sessionFactory.isClosed()) { sessionFactory.openSession(); } else { session = sessionFactory.getCurrentSession(); } return session; } }