欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

S2SH框架搭建—基于注解配置

程序员文章站 2022-07-14 23:50:59
...

此次搭建s2sh框架为本人练手之用,如有不足请见谅,但希望能帮助到大家!

框架采用struts2.3.34、spring4.3.14、hibernate5.1.12版本搭建,jdk版本采用1.6。

框架中一些固定配置不需要长改动,此部分直接配置到xml中,其他配置则使用注解。

1、 S2SH框架介绍

Struts:控制层,用来控制的。
Spring:对Struts和Hibernate进行管理,整合的。
Hibernate:操控数据库,持久化操作。

框架优点:
1.spring管理对象的实例化,把对象的创建和获取放到外部,更加的灵活方便。
2.Hibernate避免了JDBC连接数据库的冗余繁杂。
3.各层分工明细,实现了各层之间的解耦,代码更加灵活

2、 框架依赖包下载

1) Spring

下载地址:https://repo.spring.io/webapp/#/artifacts/browse/tree/General/libs-release-local/org/springframework/spring/4.3.14.RELEASE/spring-framework-4.3.14.RELEASE-dist.zip

S2SH框架搭建—基于注解配置

Spring版本所需JDK支持:

Spring Framework 3.x

JDK5+

Spring Framework 4.x

JDK6+

Spring Framework 5.x

JDK8+

2) struts2

下载地址:http://struts.apache.org/download.cgi

S2SH框架搭建—基于注解配置

Struts2版本所需JDK支持:

Struts2.3.x及以下

JDK5+

Struts2.5.x

JDK7+

 

3) hibernate

下载地址:http://hibernate.org/orm/releases/5.1/

S2SH框架搭建—基于注解配置

Hibernate版本所需JDK支持:

Hibernate 5.0

JDK 6+

Hibernate 5.1

JDK 6+

Hibernate 5.2

JDK 8+

Hibernate 5.3

JDK 8+

 4)其他所需jar可自行搜索下载

3、 项目总体结构

S2SH框架搭建—基于注解配置

4、 导入框架所需jar包

S2SH框架搭建—基于注解配置

5、 配置文件

1) web.xml,其中主要配置spring的listener、struts2的filter,用于启动加载spring、struts2.

<?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"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    					http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
	<display-name>ssh-xml</display-name>
  	
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	
	<!-- spring的监听器配置开始 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- 使Spring支持request与session的scope,如:<bean id="loginAction" class="com.foo.LoginAction" scope="request"/> -->
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>

	<!-- Spring 刷新Introspector防止内存泄露 -->
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	
	<!-- 设置编码,防止表单提交的数据或者上传/下载中文名称文件出现乱码 -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>*.jsp</url-pattern>
	</filter-mapping>

	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	
	<!-- 事务处理拦截器, 把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式 -->
	<!-- 它允许在事务提交之后延迟加载显示所需要的对象, 解决懒加载的问题,配置在Struts2过滤器之前 -->
	<filter>
		<filter-name>openSessionInView</filter-name>
		<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
		<init-param>
			<param-name>sessionFactoryBeanName</param-name>
			<param-value>sessionFactory</param-value>
		</init-param>
		<init-param>
			<param-name>singleSession</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>   
           <param-name>flushMode</param-name>   
           <param-value>AUTO</param-value>     
        </init-param>
	</filter>

	<filter-mapping>
		<filter-name>openSessionInView</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	
	 <!-- 配置Struts2 -->  
    <filter>  
        <filter-name>struts2</filter-name>  
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
    </filter>  
  
    <filter-mapping>  
        <filter-name>struts2</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>
	
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>

2) 属性文件jdbc.properties,配置数据源的相关信息。

### HIBERNATE CONFIG
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.format_sql=false
hibernate.show_sql=false
hibernate.fetch_size=20
hibernate.batch_size=10
hibernate.hbm2ddl=none

### C3P0 CONFIG(NOT BE USED)
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/sshdemo?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.driver=com.mysql.jdbc.Driver
checkoutTimeout=30000
idleConnectionTestPeriod=60
initialPoolSize=5
maxIdleTime=10
maxPoolSize=20
minPoolSize=2
maxStatements=0
maxStatementsPerConnection=0

3) spring配置文件applicationContext.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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    					http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    					http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    					http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
    default-autowire="byName" default-lazy-init="false">
    					
	<!-- 所有配置类都使用注解,自动加载构建bean -->
	<context:annotation-config />
	<context:component-scan base-package="com.folwind" />
    
    
    <!-- 加载属性文件 -->
	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:jdbc.properties</value>
			</list>
		</property>
	</bean>

    <!-- 
    C3P0数据源:
    	driverClass:数据库驱动;
    	jdbcUrl:数据库连接;
    	user:数据库用户名;
    	password:数据库用户密码;
    	(以上为必须配置的属性)
    	acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目,默认为3; 
		acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30; 
		acquireRetryDelay:两次连接中间隔时间,单位毫秒,默认为1000; 
		autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。默认为false; 
		automaticTestTable: C3P0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性preferredTestQuery将被忽略。
							你不能在这张Test表上进行任何操作,它将中为C3P0测试所用,默认为null; 
		breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。
								如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为 false; 
		checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0; 
		connectionTesterClassName: 通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需设置为全限定名。默认为 com.mchange.v2.C3P0.impl.DefaultConnectionTester; 
		idleConnectionTestPeriod:隔多少秒检查所有连接池中的空闲连接,默认为0表示不检查; 
		initialPoolSize:初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3; 
		maxIdleTime:最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0; 
		maxPoolSize:连接池中保留的最大连接数。默认为15; 
		minPoolSize:连接池中保留的最小连接数
		maxStatements:JDBC的标准参数,用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属于单个Connection而不是整个连接池。
						所以设置这个参数需要考虑到多方面的因素,如果maxStatements与 maxStatementsPerConnection均为0,则缓存被关闭。默认为0; 
		maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存Statement数。默认为0; 
		numHelperThreads:C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3; 
		preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null; 
		propertyCycle: 用户修改系统配置参数执行前最多等待的秒数。默认为300; 
		testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都 将校验其有效性。
								建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。默认为false; 
		testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。默认为false
     -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="driverClass" value="${jdbc.driver}" />
		<property name="checkoutTimeout" value="${checkoutTimeout}" />
		<property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}" />
		<property name="initialPoolSize" value="${initialPoolSize}" />
		<property name="maxIdleTime" value="${maxIdleTime}" />
		<property name="maxPoolSize" value="${maxPoolSize}" />
		<property name="minPoolSize" value="${minPoolSize}" />
		<property name="maxStatements" value="${maxStatements}" />
		<property name="maxStatementsPerConnection" value="${maxStatementsPerConnection}" />
    </bean>
    
	<!-- 使用Hiberanate 创建sessionfactory
		在hibernate5中获取sessionFactory ,
		hibernate3则class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
		hibernate4则class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
	设置属性说明:
		hibernate.dialect:设置方言
		hibernate.format_sql:输出格式化的sql
		hibernate.show_sql:控制台输出sql
		hibernate.jdbc.fetch_size:Hibernate每次从数据库中取出并放到JDBC的Statement中的记录条数。FetchSize设的越大,读数据库的次数越少,速度越快,Fetch Size越小,读数据库的次数越多,速度越慢
		hibernate.jdbc.batch_size:Hibernate批量插入,删除和更新时每次操作的记录数。BatchSize越大,批量操作的向数据库发送Sql的次数越少,速度就越快,同样耗用内存就越大
		hibernate.hbm2ddl.auto:自动创建|更新|验证数据库表结构。有以下几个参数: 
		    		create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
		    		create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
		    		update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了
		    				但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。
		    		validate :每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
		current_session_context_class:使用缓存机制,配置此处 sessionFactory.getCurrentSession()可以完成一系列的工作,当调用时,hibernate将session绑定到当前线程,
				事务结束后,hibernate将session从当前线程中释放,并且关闭session。当再次调用getCurrentSession()时,将得到一个新的session,并重新开始这一系列工作。
				(hibernate3:Thread或jta);(hibernate4:org.springframework.orm.hibernate4.SpringSessionContext);
				(hibernate5:org.springframework.orm.hibernate5.SpringSessionContext)
		hibernate.cache.use_second_level_cache:是否启用二级缓存,true或false
		hibernate.cache.provider_class:使用Ehcache缓存;hibernate3是org.hibernate.cache.HashtableCacheProvider;hibernate4是net.sf.ehcache.hibernate.EhCacheProvider
		hibernate.cache.use_query_cache:是否启用查询缓存,true或false
		hibernate.temp.use_jdbc_metadata_defaults:Hibernate默认是使用jdbc方式来取的,若使用连接池的方式,将此配置项置为false,否则启动时抛出异常Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.Invocation
	-->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
			    <prop key="hibernate.jdbc.fetch_size">${hibernate.fetch_size}</prop>
			    <prop key="hibernate.jdbc.batch_size">${hibernate.batch_size}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl}</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>  
			</props>
		</property>
		<!--自动扫描(实体类)注解包:实体类位置为:com.spring.mvc.entities.User,不能写成:com.spring.mvc.entities,而要写成:com.spring.mvc,packagesToScan要比实际上前一层-->
        <property name="packagesToScan">
            <list>
                <value>com.folwind.**.entity</value>
            </list>
		</property>
	</bean>
    
    <!--hibernate3.1以及以上版本取消了hibernateTemplate,hibernate4需要通过getCurrentSession()获取session  -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

    <!-- 加载事务管理驱动 -->
	<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<!-- 配置事务切面 -->
	<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="do*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="handle*" propagation="REQUIRED" />
			<tx:method name="excute*" propagation="REQUIRED" />
			<tx:method name="analysis*" propagation="REQUIRED" />
			<tx:method name="receive*" propagation="REQUIRED" />
			<tx:method name="search*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="load*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="*" propagation="REQUIRED" read-only="true"/>
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut expression="(execution(* com.folwind..service.*.*(..))) or (execution(* com.folwind.framework.core.BaseService.*(..)))" id="txPoint" />
		<aop:advisor advice-ref="transactionAdvice" pointcut-ref="txPoint" />
	</aop:config>
</beans>

4) struts2配置文件struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
	<!-- 基础配置 -->
	<!-- CONSTANT CONFIG -->
	<constant name="struts.locale" value="zh_CN"/>
	<constant name="struts.i18n.encoding" value="UTF-8"/>
	<constant name="struts.multipart.parser" value="jakarta"/>
	<constant name="struts.multipart.saveDir" value=""/>
	<constant name="struts.multipart.maxSize" value="41943040"/>
	<constant name="struts.ui.templateDir" value="template" />
	
	<!-- 不用dojo的及struts2复杂标签样式的就把主题设置为simple,这样可以不加载多余的模板 -->
	<constant name="struts.ui.theme" value="simple"/>
	
    <!-- 自动动态方法的调用,使用这个设置后可以这样调用:action!method -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
	<!-- 把它设置为开发模式,发布时要设置为false -->
	<constant name="struts.devMode" value="true" />
    <!-- 设置在class被修改时是否热加载,发布时要设置为false -->
    <constant name="struts.convention.classes.reload" value="true"/>
    
    <!-- 该属性设置是否每次HTTP请求到达时,系统都重新加载资源文件,该属性默认值是false -->
	<constant name="struts.i18n.reload" value="false" />
	<!-- 当struts 2的配置文件修改后,系统是否自动重新加载该文件,默认值为false -->
	<constant name="struts.configuration.xml.reload" value="true"></constant>

	<!-- 指定Struts2默认的ObjectFactoryBean,该属性默认值是spring -->
	<constant name="struts.objectFactory" value="spring"></constant>

	<!-- 在struts2中设置处理请求的后缀为 -->
	<constant name="struts.action.extension" value="do" />
	
	<!-- 该属性设置Struts 2是否允许在Action名中使用斜线,该属性的默认值是false -->
	<!-- <constant name="struts.enable.SlashesInActionNames" value="true"/> -->
	
    <!-- 指定jsp文件所在的目录地址,默认为/WEB-INF/content/ -->
    <constant name="struts.convention.result.path" value="" />
    
    <!-- 一个action名字的获取。比如为HelloWorldAction。按照配置,actionName为hello_world。 -->
    <constant name="struts.convention.action.suffix" value="Action"/>
    <constant name="struts.convention.action.name.lowercase" value="true"/> 
	<constant name="struts.convention.action.name.separator" value="-"/>
	
	<!-- 是否不扫描类。一定要设为false,否则convention插件不起作用,零配置也没有意义。 -->
	<constant name="struts.convention.action.disableScanning" value="false"/>
	
	<!-- 设置默认的父包,一般我们都设置一个default包继承自struts-default。大部分类再继承default。如果有特殊的类需要特殊的包,只能在action中再指定父包了。 -->
	<constant name="struts.convention.default.parent.package" value="app-default"/>
	
	<!-- 确定搜索包的路径。只要是结尾为action的包都要搜索。basePackage按照默认不用配置,如果配置,只会找以此配置开头的包。locators及locators.basePackage都是一组以逗号分割的字符串 -->
	<constant name="struts.convention.package.locators" value="action"/> 
	<constant name="struts.convention.package.locators.disable" value="false"/>
	<constant name="struts.convention.package.locators.basePackage" value="com.folwind"/>

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

	<package name="app-default" namespace="/" extends="struts-default, json-default">
		
		<!-- INTERCEPTORS CONFIG -->
		<interceptors>
			<interceptor-stack name="appDefaultStack">
				<interceptor-ref name="logger" />
				<interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                     <param name="paramNameMaxLength">1000</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="deprecation"/>
			</interceptor-stack>
		</interceptors>

		<default-interceptor-ref name="appDefaultStack" />

		<!-- GLOBAL RESULT AND GLOBAL EXCEPTION CONFIG -->
		<global-results>
		    <result name="json" type="json" >
				<!-- 这里指定将被Struts2序列化的属性,该属性在action中必须有对应的getter方法 -->
				<!-- 代表从哪里开始序列化 -->
		        <param name="root">json</param>
		        <!-- <param name="ignoreHierarchy">false</param> -->
		        <!-- 代表排除Action中的哪些属性,排除多个属性时,使用逗号进行分隔(即不需要序列化的属性) -->
				<!-- <param name="excludeProperties">age</param> -->
				<!-- 代表包含Action中的哪些属性,包含多个属性是,使用逗号进行分隔(即需要序列化的属性) -->
				<!-- <param name="includeProperties">address</param> -->
				<!-- 代表排除Action中属性值为空的字段, 使用true/false 默认为false(即包含属性值为null的字段) -->
				<param name="excludeNullProperties">true</param>
				<!-- 指定内容类型,默认为application/json,IE浏览器会提示下载 -->  
                <!-- <param name="contentType">text/html</param> -->
				<!-- 注意:如果相同属性同时定义了excludeProperties和includeProperties,
					那么excludeProperties的优先级要高,因此不对该属性进行序列化 -->
				<!-- 
					private String defaultEncoding = "ISO-8859-1";//默认的编码  
					private List<Pattern> includeProperties;//被包含的属性的正则表达式,这些属性的值将被序列化为JSON字符串,传送到客户端  
					private List<Pattern> excludeProperties;//被排除的属性的正则表达式,这些属性的值在对象序列化时将被忽略  
					private String root;//根对象,即要被序列化的对象,如不指定,将序列化action中所有可被序列化的数据  
					private boolean wrapWithComments;//是否包装成注释  
					private boolean prefix;//前缀  
					private boolean enableGZIP = false;//是否压缩  
					private boolean ignoreHierarchy = true;//是否忽略层次关系,即是否序列化对象父类中的属性  
					private boolean ignoreInterfaces = true;//是否忽略接口  
					private boolean enumAsBean = false;//是否将枚举类型作为一个bean处理  
					private boolean excludeNullProperties = false;//是否排除空的属性,即是否不序列化空值属性  
					private int statusCode;//HTTP状态码  
					private int errorCode;//HTTP错误码  
					private String contentType;//内容类型,通常为application/json,在IE浏览器中会提示下载,可以通过参数配置<param name="contentType">text/html</param>,则不提示下载  
					private String wrapPrefix;//包装前缀  
					private String wrapSuffix;//包装后缀
				 -->
		    </result>
			<result name="login">/index.jsp</result>
			<result name="exception">/framework/common/exception.jsp</result>
		</global-results>

		<global-exception-mappings>
			<exception-mapping exception="java.lang.Exception" result="exception"/>
		</global-exception-mappings>

	</package>
</struts>

5) 日志配置log4j.properties,配置日志打印的相关信息

#日志框架
#日志等级
#TRACE:详细等级,堆栈信息
#debug:类似于System.out.print
#info:类似于Hibernate的show_sql
#warn:不影响运行, 只是提示
#error:出现异常
#全局日志等级配置,输出位置

log4j.rootLogger=info,stdout,R

#stdout控制器
#org.apache.log4j.ConsoleAppender(控制台),
#org.apache.log4j.FileAppender(文件),
#org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
#org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
#org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
#org.apache.log4j.net.SMTPAppender(邮件发送日志)
#org.apache.log4j.jdbc.JDBCAppender  输出到数据库
log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target=System.out

#org.apache.log4j.HTMLLayout(以HTML表格形式布局),
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#输出格式
# %c  列出logger名字空间的全称,如果加上{<层数>}表示列出从最内层算起的指定层数的名字空间
#                   假设当前logger名字空间是"a.b.c"
#                   %c           a.b.c
#                   %c{2} 	     b.c
#                   %20c 	      (若名字空间长度小于20,则左边用空格填充)
#                   %-20c 	      (若名字空间长度小于20,则右边用空格填充)
#                   %.30c 	      (若名字空间长度超过30,截去多余字符)
#                   %20.30c 	(若名字空间长度小于20,则左边用空格填充;若名字空间长度超过30,截去多余字符)
#                   %-20.30c 	(若名字空间长度小于20,则右边用空格填充;若名字空间长度超过30,截去多余字符)
# %C  列出调用logger的类的全名(包含包路径)
#                   假设当前类是"org.apache.xyz.SomeClass"
#                   %C           org.apache.xyz.SomeClass
#                   %C{1} 	     SomeClass
# %d  显示日志记录时间,{<日期格式>}使用ISO8601定义的日期格式 	
#                   %d{yyyy/MM/dd HH:mm:ss,SSS} 	   2005/10/12 22:23:30,117
#                   %d{ABSOLUTE} 	                   22:23:30,117
#                   %d{DATE} 	                       12 Oct 2005 22:23:30,117
#                   %d{ISO8601} 	                   2005-10-12 22:23:30,117
# %F  显示调用logger的源文件名 	
#                   %F 	         MyClass.java
# %l  输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数
#                   %l           MyClass.main(MyClass.java:129)
# %L  显示调用logger的代码行 
#                   %L           129
# %m  显示输出消息
#                   %m           This is a message for debug.
# %M  显示调用logger的方法名 
#                   %M           main
# %n  当前平台下的换行符 
#                   %n           Windows平台下表示rn,UNIX平台下表示n
# %p  显示该条日志的优先级
#                   %p           INFO
# %r  显示从程序启动时到记录该条日志时已经经过的毫秒数
#                   %r           1215
# %t  输出产生该日志事件的线程名
#                   %t           MyClass
# %x  按NDC(Nested Diagnostic Context,线程堆栈)顺序输出日志
#                   假设某程序调用顺序是MyApp调用com.foo.Bar
#                   %c %x - %m%n               MyApp - Call com.foo.Bar. 
#                                              com.foo.Bar - Log in Bar
#                                              MyApp - Return to MyApp.
# %X  按MDC(Mapped Diagnostic Context,线程映射表)输出日志。通常用于多个客户端连接同一台服务器,方便服务器区分是那个客户端访问留下来的日志。
#                   %X{5} 	(记录代号为5的客户端的日志)
# %%  显示一个百分号
#                   %%           %
log4j.appender.stdout.layout.ConversionPattern= [%p] %d{yyyy-MM-dd HH:mm:ss,SSS} [%c.%M(%F:%L)] --> %m%n
#log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

#文件路径输出
log4j.appender.R=org.apache.log4j.RollingFileAppender
#默认输出到tomcat的根路径或者运行测试工程的根路径
log4j.appender.R.File=D\:/work/workspaces/training-workspaces/apache-tomcat-6.0.37/logs/ssh-scan.log
log4j.appender.D.Append = true
#log4j.appender.D.Threshold = error
log4j.appender.R.MaxFileSize=1024KB
# Keep three backup files.
log4j.appender.R.MaxBackupIndex=20
# Pattern to output: date priority [category] - message
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern= [%p] %d{yyyy-MM-dd HH:mm:ss,SSS} [%c.%M(%F:%L)] --> %m%n

6、 Base组件定义,定义分页组件PageModel、BaseDAO持久化操作、BaseService基本业务逻辑操作、BaseActionSupport控制层

1) 分页组件PageModel

package com.folwind.framework.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PageModel {
	// 总记录数
	private int totalRecords = 0;
	// 结果集
	private List<?> data = Collections.emptyList();
	// 当前页
	private int pageNo = 1;
	// 每页显示多少条
	private int pageSize = 10;
	
	private List<String> sortField = null;
	
	private List<String> directions = null;
	
	private boolean success = true;
	
	private String msg = "";
	
	private final String SPACE = " ";
	
	public int getTotalRecords() {
		return totalRecords;
	}

	public void setTotalRecords(int totalRecords) {
		this.totalRecords = totalRecords;
	}

	public List<?> getData() {
		return data;
	}

	public void setData(List<?> data) {
		this.data = data;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getPageNo() {
		return pageNo;
	}

	public void setPageNo(int pageNo) {
		this.pageNo = pageNo;
	}

	public boolean isNull() {
		if (data == null || data.size() <= 0) {
			return true;
		} else {
			return false;
		}
	}
	
	public void addSort(String fieldName, String direction) {
		if(this.sortField == null) {
			this.sortField = new ArrayList<String>(1);
			this.directions = new ArrayList<String>(1);
		}
		this.sortField.add(fieldName);
		this.directions.add(direction);
	}
	
	public String generateSortStr() {
		StringBuilder builder = new StringBuilder();
		if(this.sortField != null) {
			int length = this.sortField.size();
			for (int i = 0; i < length; i++) {
				if( i > 0) {
					builder.append(", ");
				}
				builder.append(this.sortField.get(i));
				builder.append(SPACE);
				builder.append(this.directions.get(i));
			}
		}
		return builder.toString();
	}
	
	public boolean isSuccess() {
		return success;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
}

2) BaseDAO

package com.folwind.framework.core;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.folwind.framework.util.HqlConvertUtil;
import com.folwind.framework.util.PageUtil;

@SuppressWarnings("unchecked")
@Repository
public class BaseDAO<T> extends HibernateDaoSupport {
	
	public T load(Class<T> clazz, Serializable id) {
		return this.getHibernateTemplate().load(clazz, id);
	}
	
	public void save(T obj) throws Exception {
		getHibernateTemplate().save(obj);
		getHibernateTemplate().flush();
	}
	
	public void update(T obj) throws Exception {
		getHibernateTemplate().update(obj);
		getHibernateTemplate().flush();
	}
	
	public void saveOrUpdate(T obj) throws Exception {
		getHibernateTemplate().saveOrUpdate(obj);
		getHibernateTemplate().flush();
	}
	
	public void merge(T obj) throws Exception {
		getHibernateTemplate().merge(obj);
		getHibernateTemplate().flush();
	}
	
	public void delete(T obj) throws Exception {
		if(obj != null) {
			getHibernateTemplate().delete(obj);
			getHibernateTemplate().flush();
		}
	}
	
	public void delete(Class<T> clazz, Serializable id) throws Exception {
		T obj = load(clazz, id);
		if (obj != null)
			delete(obj);
	}
	
	public void delete(String hql) throws Exception {
		this.getHibernateTemplate().deleteAll(find(hql));
	}
	
	public void delete(String hql, String paramName, Object value) throws Exception {
		this.getHibernateTemplate().deleteAll(find(hql, paramName, value));
	}
	
	public void delete(String hql, List<String> paramNames, List<Object> values) throws Exception {
		this.getHibernateTemplate().deleteAll(find(hql, paramNames, values));
	}
	
	public List<T> findAll(Class<T> clazz) throws Exception {
		return this.getHibernateTemplate().loadAll(clazz);
	}
	
	public List<T> find(String hql) throws Exception {
		return (List<T>) getHibernateTemplate().find(HqlConvertUtil.convert(hql));
	}
	
	public List<T> find(String hql, String paramName, Object value) throws Exception {
		return (List<T>) getHibernateTemplate().findByNamedParam(HqlConvertUtil.convert(hql), paramName, value);
	}
	
	public List<T> find(String hql, List<String> paramNames, List<Object> values) throws Exception {
		return (List<T>) getHibernateTemplate().findByNamedParam(HqlConvertUtil.convert(hql), paramNames.toArray(new String[paramNames.size()]), values.toArray());
	}
	
	/**
	 * 根据属性名和属性值查询对象 ,返回唯一对象
	 */
	public T findBy(Class<T> clazz, String paramName, Object value) throws Exception {
		Criteria criteria = currentSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(paramName, value));
		return (T) criteria.uniqueResult();
	}
	
	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的对象列表
	 */
	public List<T> findAllBy(Class<T> clazz, String paramName, Object value) throws Exception {
		Criteria criteria = currentSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(paramName, value));
		return criteria.list();
	}
	
	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的排序对象列表
	 */
	public List<T> findAllBy(Class<T> clazz, String paramName, Object value, String orderby) throws Exception {
		Criteria criteria = currentSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(paramName, value));
		criteria.addOrder(Order.asc(orderby));
		return criteria.list();
	}
	
	public List<T> findAllBy(Class<T> clazz, String paramName, Object value, String orderby, boolean asc) throws Exception {
		Criteria criteria = currentSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(paramName, value));
		if (asc)
			criteria.addOrder(Order.asc(orderby));
		else
			criteria.addOrder(Order.desc(orderby));
		return criteria.list();
	}
	
	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的对象列表
	 */
	public List<T> findAllByLike(Class<T> clazz, String name, Object value) throws Exception {
		Criteria criteria = currentSession().createCriteria(clazz);
		criteria.add(Restrictions.like(name, value));
		return criteria.list();
	}
	
	/**
	 * -----------------------------------------------------------------------
	 * 搜索统一实现
	 * -----------------------------------------------------------------------
	 */
	public PageModel findPage(String hql, PageModel pm) throws Exception {
		return findPage(hql, null, null, pm);
	}
	
	/**
	 * 根据HQL语句进行分页查询
	 * 
	 * @param hql HQL语句
	 * @param params HQL语句带的多个参数值
	 * @param offset 从第几条记录开始查询
	 * @param pageSize 每页显示多少行
	 * @return 分页模型
	 */
	public PageModel findPage(final String hql, final List<String> paramNames, final List<Object> values, PageModel pm) throws Exception {
		// 获取总记录数total
		if(paramNames != null && values != null && paramNames.size() != values.size()) {
			throw new Exception("参数列表不一致!");
		}
		final String convertedHql = HqlConvertUtil.convert(hql);
		final String countHql = getCountHql(convertedHql);
		
	
		int total = (Integer)getHibernateTemplate().execute(new HibernateCallback<Object>() {
			public Object doInHibernate(Session session) throws HibernateException {
				
				Query query = session.createQuery(countHql);
				setParameters(query, paramNames, values);
				return ((Long) query.uniqueResult()).intValue();
			}
	    });
		
		final int pageNo = pm.getPageNo();
		final int pageSize = pm.getPageSize();
		List<?> datas = (List<?>)getHibernateTemplate().execute(new HibernateCallback<Object>() {
			public Object doInHibernate(Session session) throws HibernateException {
				
				Query query = session.createQuery(convertedHql);
				setParameters(query, paramNames, values);
				return query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();
			}
	    });

	    int pageCurrent = PageUtil.getCurrentPage(total, pm.getPageSize(), pm.getPageNo());
	    
	    pm.setPageNo(pageCurrent);
		// 返回数据
		pm.setTotalRecords(total);
		pm.setData(datas);
		return pm;
	}
	
	public int executeSql(String sql) throws Exception {
		Query query = currentSession().createSQLQuery(sql);
		return query.executeUpdate();
	}
	
	public List<Map<String, Object>> findBySql(String sql) throws Exception {
		Query query = currentSession().createSQLQuery(sql);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}

	public List<Map<String, Object>> findBySql(String sql, String paramName, Object value) throws Exception {
		Query query = currentSession().createSQLQuery(sql);
		query.setParameter(paramName, value);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}
	
	public List<Map<String, Object>> findBySql(String sql, List<String> paramNames, List<Object> values) throws Exception {
		Query query = currentSession().createSQLQuery(sql);
		setParameters(query, paramNames, values);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}
	
	public PageModel findPageBySql(String sql, String countSql, PageModel pm) throws Exception {
		Query countquery = currentSession().createSQLQuery(countSql);
		List<?> countList = countquery.list();
		int rowCount = 0;
		if ((countList != null) && (!countList.isEmpty())) {
			rowCount = new Integer(countList.get(0).toString()).intValue();
		}
	    int pageCurrent = PageUtil.getCurrentPage(rowCount, pm.getPageSize(), pm.getPageNo());
	    
	    pm.setTotalRecords(rowCount);
	    pm.setPageNo(pageCurrent);
		
		Query query = currentSession().createSQLQuery(sql);
		query.setFirstResult((pageCurrent - 1) * pm.getPageSize());
	    query.setMaxResults(pm.getPageSize());
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		
		pm.setData(query.list());
		
		return pm;
	}
	
	public PageModel findPageBySql(String sql, String countSql, String paramName, Object value, PageModel pm) throws Exception {
		Query countquery = currentSession().createSQLQuery(countSql);
		countquery.setParameter(paramName, value);
		List<?> countList = countquery.list();
		int rowCount = 0;
		if ((countList != null) && (!countList.isEmpty())) {
			rowCount = new Integer(countList.get(0).toString()).intValue();
		}
	    int pageCurrent = PageUtil.getCurrentPage(rowCount, pm.getPageSize(), pm.getPageNo());
	    
	    pm.setTotalRecords(rowCount);
	    pm.setPageNo(pageCurrent);
		
		Query query = currentSession().createSQLQuery(sql);
		query.setParameter(paramName, value);
		query.setFirstResult((pageCurrent - 1) * pm.getPageSize());
	    query.setMaxResults(pm.getPageSize());
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		
		pm.setData(query.list());
		
		return pm;
	}
	
	public PageModel findPageBySql(String sql, String countSql, List<String> paramNames, List<Object> values, PageModel pm) throws Exception {
		Query countquery = currentSession().createSQLQuery(countSql);
		setParameters(countquery, paramNames, values);
		List<?> countList = countquery.list();
		int rowCount = 0;
		if ((countList != null) && (!countList.isEmpty())) {
			rowCount = new Integer(countList.get(0).toString()).intValue();
		}
	    int pageCurrent = PageUtil.getCurrentPage(rowCount, pm.getPageSize(), pm.getPageNo());
	    
	    pm.setTotalRecords(rowCount);
	    pm.setPageNo(pageCurrent);
		
		Query query = currentSession().createSQLQuery(sql);
		setParameters(query, paramNames, values);
		query.setFirstResult((pageCurrent - 1) * pm.getPageSize());
	    query.setMaxResults(pm.getPageSize());
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		
		pm.setData(query.list());
		
		return pm;
	}
	
	/**
	 * 根据HQL语句,获得查找总记录数的HQL语句 如: select ... from Orgnization o where o.parent is
	 * null 经过转换,可以得到: select count(*) from Orgnization o where o.parent is null
	 * @param hql
	 * @return 
	 */
	private String getCountHql(String hql) throws Exception {
		int index = hql.indexOf("from");
		if (index != -1) {
			return "select count(*) " + hql.substring(index);
		}
		return null;
	}
	
	private void setParameters(Query query, List<String> paramNames, List<Object> values) throws HibernateException {
		if (paramNames != null && paramNames.size() > 0) {
			int length = paramNames.size();
			Object obj = null;
			for (int i = 0; i < length; i++) {
				obj = values.get(i);
				if(obj == null) {
					continue;
				}
				if(Collection.class.isAssignableFrom(obj.getClass())) {
					query.setParameterList(paramNames.get(i), (Collection<?>) obj);
				}
				else if(obj.getClass().isArray()) {
					query.setParameterList(paramNames.get(i), (Object[])obj);
				}
				else {
					query.setParameter(paramNames.get(i), values.get(i));
				}
			}
		}
	}
}

3) BaseService

package com.folwind.framework.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class BaseService<T> {
	@Autowired
	protected BaseDAO<T> dao;

	public abstract Class<T> getEntityClass();
	public abstract String getEntityClassName();

	/**
	 * 生成hql的条件语句,开头为and条件连接符,实体别名为t
	 * @param queryEntity 存储查询条件的实体对象
	 * @param paramNames 条件语句中的插值符
	 * @param values  条件语句中插值符的值
	 * @return
	 */
	protected abstract String generateWheres(T queryEntity, List<String> paramNames, List<Object> values) throws Exception;

	public T loadById(Serializable id) {
		return dao.load(getEntityClass(), id);
	}

	public void save(T obj) throws Exception {
		dao.save(obj);
	}

	public void update(T obj) throws Exception {
		dao.update(obj);
	}

	public void saveOrUpdate(T obj) throws Exception {
		dao.saveOrUpdate(obj);
	}

	public void merge(T obj) throws Exception {
		dao.merge(obj);
	}

	public void delete(T obj) throws Exception {
		dao.delete(obj);
	}

	public void deleteById(Serializable id) throws Exception {
		dao.delete(getEntityClass(), id);
	}

	public void deleteByIds(Serializable[] ids) throws Exception {
		for (int i = 0; i < ids.length; i++) {
			dao.delete(getEntityClass(), ids[i]);
		}
	}

	public List<T> findAll() throws Exception {
		return dao.findAll(getEntityClass());
	}

	/**
	 * 根据属性名和属性值查询对象 ,返回唯一对象
	 */
	public T findBy(String name, Object value) throws Exception {
		return dao.findBy(getEntityClass(), name, value);
	}

	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的对象列表
	 */
	public List<T> findAllBy(String name, Object value) throws Exception {
		return dao.findAllBy(getEntityClass(), name, value);
	}

	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的排序对象列表
	 */
	public List<T> findAllBy(String name, Object value, String orderby) throws Exception {
		return dao.findAllBy(getEntityClass(), name, value, orderby);
	}

	public List<T> findAllBy(String name, Object value, String orderby, boolean asc) throws Exception {
		return dao.findAllBy(getEntityClass(), name, value, orderby, asc);
	}

	/**
	 * 根据属性名和属性值查询对象 ,返回符合条件的对象列表
	 */
	public List<T> findAllByLike(String name, Object value) throws Exception{
		return dao.findAllByLike(getEntityClass(), name, value);
	}

	/**
	 * -----------------------------------------------------------------------
	 * 搜索统一实现
	 * -----------------------------------------------------------------------
	 */
	protected PageModel findPage(String hql, PageModel pm) throws Exception{
		return dao.findPage(hql, pm);
	}

	/**
	 * 根据HQL语句进行分页查询
	 * 
	 * @param hql HQL语句
	 * @param params HQL语句带的多个参数值
	 * @param offset 从第几条记录开始查询
	 * @param pageSize 每页显示多少行
	 * @return 分页模型
	 */
	protected PageModel findPage(String hql, List<String> paramNames, List<Object> values, PageModel pm) throws Exception {
		return dao.findPage(hql, paramNames, values, pm);
	}

	public PageModel findPage(T queryEntity, PageModel pm) throws Exception {
		List<String> paramNames = new ArrayList<String>();
		List<Object> values = new ArrayList<Object>();
		StringBuilder builder = generateHql(generateWheres(queryEntity, paramNames, values), pm.generateSortStr());

		return dao.findPage(builder.toString(), paramNames, values, pm);
	}

	public BaseDAO<T> getDao() {
		return dao;
	}

	public void setDao(BaseDAO<T> dao) {
		this.dao = dao;
	}

	/**
	 * 生成hql语句,实体别名为t
	 * @param wheres 条件语句,开头为and条件连接符
	 * @param orderBy 排序语句,可多个字段排序,例:field1 asc,field2 desc
	 * @return
	 */
	protected StringBuilder generateHql(String wheres, String orderBy) throws Exception {
		StringBuilder builder = new StringBuilder();
		builder.append("from ");
		builder.append(getEntityClassName());
		builder.append(" t ");
		builder.append(" where 1=1 ");
		if(StringUtils.isNotBlank(wheres)) {
			builder.append(wheres);
		}
		if(StringUtils.isNotBlank(orderBy)) {
			builder.append(" order by ");
			builder.append(orderBy);
		}

		return builder;
	}
}

4) BaseActionSupport

package com.folwind.framework.core;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONArray;

import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.springframework.beans.factory.annotation.Autowired;

import com.folwind.sys.menu.service.SysMenuService;
import com.folwind.sys.user.service.SysUserService;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;

//继承ActionSupport,让本类具备强大的功能(例如:校验、国际化等)
public abstract class BaseActionSupport extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware, Preparable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	protected final String JSON = "json";

	protected HttpServletResponse response;
	protected HttpServletRequest request;
	protected Map<String, Object> session;

	private Object json;
	private Map<String, Object> jsonMap;

	//=================Service注入========================
	@Autowired
	protected SysUserService sysUserService;
	@Autowired
	protected SysMenuService sysMenuService;

	@Override
	public void prepare() throws Exception {
		this.jsonMap = new HashMap<String, Object>(5);
		setSuccessJson();
	}

	@Override
	public void setServletResponse(HttpServletResponse response) {
		this.response = response;
	}

	@Override
	public void setServletRequest(HttpServletRequest request) {
		this.request = request;
	}

	@Override
	public void setSession(Map<String, Object> session) {
		this.session = session;
	}

	public void setJson(Object json) {
		this.json = json;
	}

	public Object getJson() {
		return this.json;
	}

	protected void setSuccessJson() {
		jsonMap.put("success", Boolean.TRUE);
		jsonMap.put("msg", "操作成功");
		setJson(jsonMap);
	}

	protected void setSuccessJson(String msg) {
		jsonMap.put("success", Boolean.TRUE);
		jsonMap.put("msg", msg);
		setJson(jsonMap);
	}

	protected void addJson(String key, Object value) {
		jsonMap.put(key, value);
		setJson(jsonMap);
	}

	protected void setFailJson(String errorMsg) {
		jsonMap.put("success", Boolean.FALSE);
		jsonMap.put("msg", errorMsg);
		setJson(jsonMap);
	}

	/**
	 * 将list转换成json数据
	 * @param list
	 * @return
	 */
	public String convertListToJson(List<?> list) {
		//将list转为json
		JSONArray jsonArray = new JSONArray();
		jsonArray = JSONArray.fromObject(list);
		return  jsonArray.toString();
	}

	/**
	 * 输出字符串 Json数据
	 * @param json
	 */
	public void outPutJson(String json) {
		try {
			// 设置文本流头信息
			response.setContentType("text/html;charset=UTF-8");
			// 获取流
			PrintWriter out = response.getWriter();
			// 将流打到客户端
			out.print(json);
			// 清空缓存
			out.flush();
			// 关闭流
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

7、 Entity,数据库表ORM映射信息

package com.folwind.sys.user.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

/**
 * SysUser entity. @author MyEclipse Persistence Tools
 */
@Entity//声明当前类为hibernate映射到数据库中的实体类
@Table(name = "T_SYS_USER")
public class SysUser implements java.io.Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// Fields

	@Id//声明此列为主键,作为映射对象的标识符
	/**
	 *  @GeneratedValue注解来定义生成策略
	 *  GenerationType.TABLES 当前主键的值单独保存到一个数据库的表中
	 *  GenerationType.SEQUENCE  利用底层数据库提供的序列生成标识符
	 *  GenerationType.IDENTITY 采取数据库的自增策略
	 *  GenerationType.AUTO 根据不同数据库自动选择合适的id生成方案,这里使用mysql,为递增型
	 */
	//@GeneratedValue(strategy = GenerationType.AUTO)
	@GenericGenerator(name = "assigned", strategy = "assigned")
	@GeneratedValue(generator="assigned")
	@Column(name = "ID", unique=true, nullable = false, length=50)
	private String id;

	@Column(name = "PASSWORD", length = 32)
	private String password;

	@Column(name = "REAL_NAME", length = 255)
	private String realName;

	@Column(name = "EMAIL", length = 255)
	private String email;

	@Column(name = "MOBILE", length = 16)
	private String mobile;

	@Column(name = "ORG_ID", length = 50)
	private String orgId;

	// Constructors

	/** default constructor */
	public SysUser() {
	}

	/** minimal constructor */
	public SysUser(String id) {
		this.id = id;
	}

	/** full constructor */
	public SysUser(String id, String password, String realName, String email,
			String mobile, String orgId) {
		this.id = id;
		this.password = password;
		this.realName = realName;
		this.email = email;
		this.mobile = mobile;
		this.orgId = orgId;
	}

	// Property accessors

	public String getId() {
		return this.id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRealName() {
		return this.realName;
	}

	public void setRealName(String realName) {
		this.realName = realName;
	}

	public String getEmail() {
		return this.email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getMobile() {
		return this.mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public String getOrgId() {
		return this.orgId;
	}

	public void setOrgId(String orgId) {
		this.orgId = orgId;
	}

}

8、 Service,service层业务逻辑操作,继承BaseService

package com.folwind.sys.user.service;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import com.folwind.framework.core.BaseService;
import com.folwind.sys.user.entity.SysUser;

@Service
public class SysUserService extends BaseService<SysUser> {

	@Override
	public Class<SysUser> getEntityClass() {
		return SysUser.class;
	}

	@Override
	public String getEntityClassName() {
		return "SysUser";
	}

	@Override
	protected String generateWheres(SysUser queryEntity,
			List<String> paramNames, List<Object> values) throws Exception {
		if(queryEntity == null) {
			return "";
		}
		StringBuilder sb = new StringBuilder();

		if(StringUtils.isNotBlank(queryEntity.getRealName())) {
			sb.append(" and t.realName like :realName");
			paramNames.add("realName");
			values.add("%" + queryEntity.getRealName() + "%");
		}
		if(StringUtils.isNotBlank(queryEntity.getMobile())) {
			sb.append(" and t.mobile like :mobile");
			paramNames.add("mobile");
			values.add("%" + queryEntity.getMobile() + "%");
		}
		return sb.toString();
	}
}

9、 Action,action层控制逻辑操作,继承BaseActionSupport

package com.folwind.sys.user.action;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

import com.folwind.framework.core.BaseActionSupport;
import com.folwind.framework.core.PageModel;
import com.folwind.framework.util.BeanUtil;
import com.folwind.framework.util.PageUtil;
import com.folwind.sys.user.entity.SysUser;

@Results({
	@Result(name="list",location="/sys/user/jsp/user.jsp")
})
public class SysUserAction extends BaseActionSupport {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final Log LOG = LogFactory.getLog(SysUserAction.class);

	private SysUser entity;
	private String id;

	public String list() {
		return "list";
	}

	public String search() {
		PageModel pm = null;
		try {
			pm = PageUtil.createPageModel(request);

			pm = sysUserService.findPage(getEntity(), pm);

			pm.setSuccess(Boolean.TRUE);
			pm.setMsg("查询数据成功!");
		} catch (Exception e) {
			if(pm == null) {
				pm = new PageModel();
			}

			pm.setSuccess(Boolean.FALSE);
			pm.setMsg("查询数据失败!");
			LOG.error("查询数据失败!" + e.getMessage(), e);
		}
		setJson(pm);

		return JSON;
	}

	public String searchById() {
		try {
			entity = sysUserService.loadById(id);

			addJson("entity", entity);
			setSuccessJson("查询数据成功!");
		} catch (Exception e) {
			setFailJson("查询数据失败!");
			LOG.error("查询数据失败!" + e.getMessage(), e);
		}

		return JSON;
	}

	public String doCreate() {
		try {
			sysUserService.save(getEntity());

			setSuccessJson("创建数据成功!");
		} catch (Exception e) {
			setFailJson("创建数据失败!");
			LOG.error("创建数据失败!" + e.getMessage(), e);
		}

		return JSON;
	}

	public String doModify() {
		try {
			SysUser entity = getEntity();
			SysUser oldEntity = sysUserService.loadById(entity.getId());

			BeanUtil.copyPropertiesExcludeNullValue(oldEntity, entity);

			sysUserService.update(oldEntity);

			setSuccessJson("修改数据成功!");
		} catch (Exception e) {
			setFailJson("修改数据失败!");
			LOG.error("修改数据失败!" + e.getMessage(), e);
		}

		return JSON;
	}

	public String delete() {
		try {
			if(StringUtils.isNotBlank(id)) {
				sysUserService.deleteByIds(StringUtils.split(id.toString(), ","));
			}

			setSuccessJson("删除数据成功!");
		} catch (Exception e) {
			setFailJson("删除数据失败!");
			LOG.error("删除数据失败!" + e.getMessage(), e);
		}

		return JSON;
	}

	public SysUser getEntity() {
		return this.entity;
	}

	public void setEntity(SysUser entity) {
		this.entity = entity;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

}

10、 前台调用

前台调用Action方法的方式:sys-user!list.do

S2SH框架搭建—基于注解配置