完整MyEclipse搭建SSH框架,教程+源码
完整工程下载地址:SSH
准备工作,jar包
- Struts2下载 :官网点这里;
最新版本为2.5,这里用2.3
-
Spring下载 :官网点这里;
根据自己版本选择下载:
具体教程可见另一篇:https://blog.csdn.net/weixin_42176639/article/details/94360648
-
Hibernate :hibernate官网;
4. 搭建SSH所需的jar包 ,图中文件包括数据库驱动包,druid数据库连接池,ajax包:
5.工程目录结构:
6.配置文件说明
- 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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<!-- 类似于财务部门一样,类就是钱,所有需要类的实例都由srping去管理 -->
<!-- 配置注解自动扫描的包 -->
<!-- <context:component-scan base-package="com.ssh.*"></context:component-scan> -->
<!-- 配置数据源 -->
<context:property-placeholder location="classpath:druid_db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- c3p0 -->
<!-- <property name="driverClass" value="${jdbc.driverClass}"/> -->
<!-- <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/> -->
<!-- <property name="user" value="${jdbc.user}"/> -->
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.jdbcUrl}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 ,log4j-->
<property name="filters" value="stat" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="2" />
<property name="minIdle" value="1" />
<property name="maxActive" value="20" />
<!-- 配置获取连接等待超时的时间 ms 60s-->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- oracle必须这样配置检查,不了会报检查错误 -->
<!-- <property name="validationQuery" value="SELECT 1 FROM DUAL" /> -->
<property name="validationQuery" value="select 1" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开 removeAbandoned 功能 -->
<property name = "removeAbandoned" value = "true" />
<!-- 1800 秒,也就是 30 分钟 -->
<property name = "removeAbandonedTimeout" value ="1800" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<!-- 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。分库分表较多的数据库,建议配置为false。 -->
<property name="poolPreparedStatements" value="false" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<property name="keepAlive" value="true" />
</bean>
<!-- 生成SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean" scope="prototype">
<!-- 使用某个数据源生成SessionFactory -->
<property name="dataSource" ref="dataSource"></property>
<!-- hibernate 配置文件的路径 -->
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<!-- <property name="hibernateProperties">
<props>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop>
</props>
</property>
-->
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" ></property>
</bean>
<!-- 配置dao -->
<!-- 配置service -->
<!-- 配置action -->
<!-- 登录 -->
<bean id="log_d" class="com.demo.daoImpl.LogDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="log_s" class="com.demo.serviceImpl.LogServiceImpl">
<property name="dao" ref="log_d"></property>
</bean>
<bean id="log_a" class="com.demo.action.LogAction" scope="prototype">
<property name="service" ref="log_s"></property>
</bean>
<!-- 用户 tb_user -->
<bean id="user_d" class="com.demo.daoImpl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="user_s" class="com.demo.serviceImpl.UserServiceImpl">
<property name="dao" ref="user_d"></property>
</bean>
<bean id="user_a" class="com.demo.action.UserAction" scope="prototype">
<property name="service" ref="user_s"></property>
</bean>
<!-- 基于注解的事务,当注解中发现@Transactional时,使用id为“transactionManager”的事务管理器 -->
<!-- 如果没有设置transaction-manager的值,则spring以缺省默认的事务管理器来处理事务,默认事务管理器为第一个加载的事务管理器 -->
<!-- <tx:annotation-driven transaction-manager="transactionManager"/> -->
<tx:annotation-driven />
<!-- 声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="test*" propagation="REQUIRED" />
<tx:method name="*Transaction" propagation="REQUIRED" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 只对业务逻辑层实施事务 -->
<aop:config expose-proxy="true">
<aop:pointcut expression="execution(* com.demo.serviceImpl.*.*(..))" id="txPointcut"/>
<!-- Advisor定义,切入点和通知分别为txPointcut、txAdvice -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
</beans>
- hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库连接配 置 默认的-->
<!-- 在建立sessionfactory时会引入hibernate.cfg.xml,需要用到数据库配置信息 -->
<property name="connection.driver_class">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="connection.url">
jdbc:sqlserver://127.0.0.1:1433;DatabaseName=Test_SSH
</property>
<property name="connection.username">sa</property>
<property name="connection.password">654321</property>
<property name="connection.characterEncoding">UTF-8</property>
<!-- 在hibernate里配置druid的连接信息 -->
<!-- <property name="hibernate.connection.provider_class">com.alibaba.druid.support.hibernate.DruidConnectionProvider</property>
<property name="driverClassName">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="url">jdbc:sqlserver://127.0.0.1:1433;DatabaseName=Test_SSH</property>
<property name="username">sa</property>
<property name="password">654321</property>
-->
<property name="hibernate.current_session_context_class">
org.springframework.orm.hibernate5.SpringSessionContext
</property>
<!-- 每个数据库都有1个,针对特定的关系型数据库生成优化的SQL -->
<!-- 会报错找不到:org.hibernate.dialect.SQLServer2008Dialect -->
<property name="dialect">
org.hibernate.dialect.SQLServerDialect
</property>
<!-- 显示SQL -->
<property name="show_sql">false</property>
<!-- 格式化SQL -->
<property name="format_sql">true</property>
<!-- 根据schema更新数据表的工具 -->
<property name="hbm2ddl.auto">update</property>
<!-- #hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider -->
<!-- <property name="hibernate.connection.provider_class">
org.hibernate.connection.C3P0ConnectionProvider
</property>
-->
<!-- 数据表映射配置文件 -->
<mapping resource="com/demo/entity/TbUsers.hbm.xml" />
</session-factory>
</hibernate-configuration>
- 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.3.dtd">
<!-- 上面的头,注意版本,从样例里复制过来 showcase.war\WEB-INF\src\java\struts.xml -->
<struts>
<!-- 告知Struts2运行时使用Spring来创建对象 -->
<constant value="utf-8" name="struts.i18n.encoding" />
<constant value="Massage" name="struts.custom.i18n.resources" />
<constant value="/chatext,/pages/mes/TakePhoto.html" name="struts.action.excludePattern" />
<constant value="gif,jpeg,jpg,png,bmp" name="com.chinacnd.allowed.images" />
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<constant name="struts.devMode" value="false" /><!-- true开发模式 -->
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.multipart.saveDir" value="E://mesPhotos"></constant><!-- 临时文件夹 -->
<constant name="struts.multipart.maxSize" value="10701096" /><!-- 最大上传10M -->
<!-- 第1步:先定义一个包 -->
<package name="demo" extends="struts-default" namespace="/">
<!-- 第2步:定义一个action,配置跳转信息 name 类似于Servlet @WebServlet("/IndexServlet")
http://xxxx/xxx/Index.action http://xxxx/xxx/Index class
对应于自己写的Action类 当不写method属性时,默认调用的是execute
class="ssh.action.IndexAction" ** new ssh.action.IndexAction()
设计思想:关心了具体的实现类必须改为不要关注那个实现类
加入spring后,struts的action节点的class属性意义发生变化,直接引用spring帮忙创建的实例 -->
<action name="UserExist" class="user_a" method="IfExist">
<result name="success">index.jsp</result>
<result name="error">indexs.jsp</result>
</action>
</package>
<!-- extends="json-default" 阿贾克斯-->
<package name="ajax" namespace="/ajax" extends="json-default">
<action name="LogIn" class="log_a" method="LogIn">
<result type="json"></result>
</action>
</package>
</struts>
- druid.properties
jdbc.driverClass=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.jdbcUrl=jdbc:sqlserver://127.0.0.1:1433;DatabaseName=Test_SSH
jdbc.user=sa
jdbc.password=******
- logback-test.xml --日志记录配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration debug="false" scan="true" scanPeriod="60 seconds">
<!-- Stop output INFO at start -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<property name="LogBack_WARN" value="C:/Log/My_SSH/logback_warn"></property> //这里为日志的存储地址
<property name="LogBack_ERROR" value="C:/Log/My_SSH/logback_error"></property> //这里为日志的存储地址
<property name="LogBack_INFO" value="C:/Log/My_SSH/logback_info"></property> //这里为日志的存储地址
<property name="LogBack_DEBUG" value="C:/Log/My_SSH/logback_debug"></property> //这里为日志的存储地址
<!-- 日志最大的历史 30天 -->
<property name="maxHistory_WARN" value="60"/>
<property name="maxHistory_ERROR" value="60"/>
<property name="maxHistory_INFO" value="30"/>
<property name="maxHistory_DEBUG" value="60"/>
<!-- 负责写日志,控制台日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 一是把日志信息转换成字节数组,二是把字节数组写入到输出流 -->
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%5level] [%thread] %logger{0} %msg%n</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- WARN级别日志 appender -->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${LogBack_WARN}/%d{yyyy-MM-dd}/warn-log.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory_WARN}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- ERROR级别日志 -->
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志输出位置 可相对、和绝对路径 -->
<fileNamePattern>${LogBack_ERROR}/%d{yyyy-MM-dd}/error-log.log</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6,
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
<maxHistory>${maxHistory_ERROR}</maxHistory>
</rollingPolicy>
<!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy> -->
<!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy> -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<!-- 按日期和大小区分的滚动日志 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LogBack_INFO}/%d{yyyy-MM-dd}/info-log.log</fileNamePattern>
<maxHistory>${maxHistory_INFO}</maxHistory>
</rollingPolicy>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<encoder>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{85} - %msg%n</Pattern>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="DEBUG" class="ch.qos.logback.core.FileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- LevelFilter: 级别过滤器,根据日志级别进行过滤 -->
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${LogBack_DEBUG}/%d{yyyy-MM-dd}/debug-log.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory_DEBUG}</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%5level] [%thread] %logger{0} %msg%n</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- TRACE级别日志 appender -->
<!-- <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
过滤器,只记录ERROR级别的日志
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
按天回滚 daily
<fileNamePattern>${LogBack_ERROR}/%d{yyyy-MM-dd}/trace-log.log
</fileNamePattern>
日志最大的历史 60天
<maxHistory>${maxHistory_ERROR}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender> -->
<!--<!– 异常日志输出 –>-->
<!--<appender name="EXCEPTION" class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!--<file>exception.log</file>-->
<!--<!– 求值过滤器,评估、鉴别日志是否符合指定条件. 需要额外的两个JAR包,commons-compiler.jar和janino.jar –>-->
<!--<filter class="ch.qos.logback.core.filter.EvaluatorFilter">-->
<!--<!– 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator –>-->
<!--<evaluator>-->
<!--<!– 过滤掉所有日志消息中不包含"Exception"字符串的日志 –>-->
<!--<expression>return message.contains("Exception");</expression>-->
<!--</evaluator>-->
<!--<OnMatch>ACCEPT</OnMatch>-->
<!--<OnMismatch>DENY</OnMismatch>-->
<!--</filter>-->
<!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
<!--<!– 触发节点,按固定文件大小生成,超过5M,生成新的日志文件 –>-->
<!--<maxFileSize>5MB</maxFileSize>-->
<!--</triggeringPolicy>-->
<!--</appender>-->
<!-- 异步输出 -->
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold >0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref ="ERROR"/>
</appender>
<!--
- 1.name:包名或类名,用来指定受此logger约束的某一个包或者具体的某一个类
- 2.未设置打印级别,所以继承他的上级<root>的日志级别“DEBUG”
- 3.未设置additivity,默认为true,将此logger的打印信息向上级传递;
- 4.未设置appender,此logger本身不打印任何信息,级别为“DEBUG”及大于“DEBUG”的日志信息传递给root,
- root接到下级传递的信息,交给已经配置好的名为“STDOUT”的appender处理,“STDOUT”appender将信息打印到控制台;
-->
<logger name="ch.qos.logback" />
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.type" level="TRACE" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
<!--
- 1.将级别为“INFO”及大于“INFO”的日志信息交给此logger指定的名为“STDOUT”的appender处理,在控制台中打出日志,
- 不再向次logger的上级 <logger name="logback"/> 传递打印信息
- 2.level:设置打印级别(TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF),还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。
- 如果未设置此属性,那么当前logger将会继承上级的级别。
- 3.additivity:为false,表示此logger的打印信息不再向上级传递,如果设置为true,会打印两次
- 4.appender-ref:指定了名字为"STDOUT"的appender。
-->
<logger name="com.demo.z_Test.LogBack_Test" level="WARN" additivity="false">
<!-- <appender-ref ref="STDOUT"/> -->
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
<!-- <appender-ref ref="INFO"/> -->
<!--<appender-ref ref="DEBUG"/>-->
<!--<appender-ref ref="EXCEPTION"/>-->
<!-- <appender-ref ref="ASYNC"/> -->
</logger>
<!--
- 根logger
- level:设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。
- 默认是DEBUG。
-appender-ref:可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger
-->
<root level="WARN">
<!-- <appender-ref ref="STDOUT"/> -->
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
<!-- <appender-ref ref="INFO"/> -->
<!--<appender-ref ref="DEBUG"/>-->
<!--<appender-ref ref="EXCEPTION"/>-->
<!-- <appender-ref ref="ASYNC"/> -->
</root>
</configuration>
- web.xml:此处配置了自定义的拦截器和session监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>MySSH</display-name>
<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>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>WEB.ROOT</param-value>
</context-param>
<!-- spring的监听器配置开始 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>com.demo.dbutil.SessionListener</listener-class>
</listener>
<!-- <servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
-->
<!-- 用户登录验证过滤器 -->
<filter>
<filter-name>UserLogFilter</filter-name>
<filter-class>com.demo.Filter.UserLogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UserLogFilter</filter-name>
<url-pattern>/pages/*</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>
<!-- hibernate session过滤器 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
工程文件
HibernateUtil.java:sessionFactory的实现
package com.demo.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
//为保证线程安全,将Seeeion放到ThreadLocal中管理。这样就避免了Session的多线程共享数据的问题
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private static SessionFactory sessionFactory = null;//SessionFactory对象
//静态块(在类被加载时执行,且生命周期内只执行一次)
static {
try {
// 加载Hibernate配置文件,默认为hibernate.cfg.xml
Configuration cfg = new Configuration().configure();
// 创建会话工厂
// hibernate4.0版本前这样获取sessionFactory = configuration.buildSessionFactory();
// hibernate5以后规定,所有的配置或服务,要生效,必须配置或服务注册到一个服务注册类(服务构建器-->服务注册器)
ServiceRegistry serviceRegistry = cfg.getStandardServiceRegistryBuilder().build();
// 根据服务注册类创建一个元数据资源集,同时构建元数据并生成应用一般唯一的的session工厂
sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
} catch (Exception e) {
System.err.println("1:创建会话工厂失败");
e.printStackTrace();
}
}
/**
* 获取Session
* @return Session
* @throws HibernateException
*/
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get(); //获取ThreadLocal中当前线程共享变量的值。
if (session == null || !session.isOpen()) {
if (sessionFactory == null) { //如果会话工厂创建失败为空就在重新创建一次
rebuildSessionFactory();
}
//创建Sqlsession数据库会话
session = (sessionFactory != null) ? sessionFactory.openSession(): null;
//设置ThreadLocal中当前线程共享变量的值。
threadLocal.set(session);
}
return session;
}
/**
* 重建会话工厂
*/
public static void rebuildSessionFactory() {
try {
// 加载Hibernate配置文件
Configuration cfg = new Configuration().configure();
// 创建会话工厂
// hibernate4.0版本前这样获取sessionFactory = configuration.buildSessionFactory();
// hibernate5以后规定,所有的配置或服务,要生效,必须配置或服务注册到一个服务注册类(服务构建器-->服务注册器)
ServiceRegistry serviceRegistry = cfg.getStandardServiceRegistryBuilder().build();
// 根据服务注册类创建一个元数据资源集,同时构建元数据并生成应用一般唯一的的session工厂
sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory();
} catch (Exception e) {
System.err.println("2:创建会话工厂失败");
e.printStackTrace();
}
}
/**
* 获取SessionFactory对象
* @return SessionFactory对象
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 关闭Session
* @throws HibernateException
*/
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
//使用set(null)来回收ThreadLocal设置的值.
threadLocal.set(null);
if (session != null) {
session.close();//关闭Session
}
}
}
SessionListener监听器
package com.demo.dbutil;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class SessionListener implements HttpSessionListener{
private MySessionContext myc = MySessionContext.getInstance();
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
myc.addSession(session);
//System.out.println("获取session:"+session.getId());
}
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
myc.delSession(session);
//System.out.println("丢失session:"+session.getId());
//session过期时要恢复用户登录状态
}
}
MySessionContext
package com.demo.dbutil;
import java.util.HashMap;
import javax.servlet.http.HttpSession;
public class MySessionContext {
private static MySessionContext instance;
private HashMap<String,HttpSession> sessionMap;
private MySessionContext() {
sessionMap = new HashMap<String,HttpSession>();
}
public static MySessionContext getInstance() {
if (instance == null) {
instance = new MySessionContext();
}
return instance;
}
public synchronized void addSession(HttpSession session) {
if (session != null) {
sessionMap.put(session.getId(), session);
}
}
public synchronized void delSession(HttpSession session) {
if (session != null) {
sessionMap.remove(session.getId());
}
}
public synchronized HttpSession getSession(String sessionID) {
if (sessionID == null) {
return null;
}
return sessionMap.get(sessionID);
}
}
DAO层的实现
daoImpl
package com.demo.daoImpl;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import com.demo.dao.LogDao;
import com.demo.entity.TbUsers;
import com.demo.hibernate.HibernateUtil;
public class LogDaoImpl extends HibernateDaoSupport implements LogDao{
/*
* 登录
*/
@SuppressWarnings("unchecked")
public List<TbUsers> Login(String account,String password){
Session session = null;
List<TbUsers> list = null;
session = HibernateUtil.getSession();
// HQL查询的是实体类的名称,不是数据表的名称
String hql = "from TbUsers u where u.userAccount=?0 and u.userPassword=?1";
if(session!=null){
try{
Query<TbUsers> query = session.createQuery(hql);
query.setParameter(0, account);
query.setParameter(1, password);
list = query.list();
}catch(HibernateException e){
e.printStackTrace();
}
}
HibernateUtil.closeSession();
return list;
}
/*
* 改变登录状态
*/
@SuppressWarnings("rawtypes")
public boolean UpdateUserState(String sessionId,String state,String account,String password){
boolean ret = true;
Session session = null;
Transaction tx = null;
session = HibernateUtil.getSession();
String hql = "update TbUsers u set u.userC = ?0, u.userD = ?1 "
+"where u.userAccount = ?2 and u.userPassword = ?3"; //第二种删除方式
if(session!=null){
try{
tx = session.beginTransaction();
Query query = session.createQuery(hql);
query.setParameter(0,sessionId);
query.setParameter(1,state);
query.setParameter(2,account);
query.setParameter(3,password);
query.executeUpdate();
tx.commit();
}catch(HibernateException e){
ret = false;
e.printStackTrace();
tx.rollback();
}
}
HibernateUtil.closeSession();
return ret;
}
/*
* session销毁时清空状态信息
*/
@SuppressWarnings("rawtypes")
public boolean UpdateUserStateBySession(String sessionId){
//System.out.println("更新session:"+sessionId);
boolean ret = true;
Session session = null;
Transaction tx = null;
session = HibernateUtil.getSession();
String hql = "update TbUsers u set u.userC=?0 u.userD = ?1"
+"where u.userC = ?2"; //第二种删除方式
if(session!=null){
try{
tx = session.beginTransaction();
Query query = session.createQuery(hql);
query.setParameter(0,"");
query.setParameter(1,"0");
query.setParameter(2,sessionId);
query.executeUpdate();
tx.commit();
}catch(HibernateException e){
ret = false;
e.printStackTrace();
tx.rollback();
}
}
HibernateUtil.closeSession();
return ret;
}
}
dao
package com.demo.dao;
import java.util.List;
import com.demo.entity.TbUsers;
public interface LogDao {
public List<TbUsers> Login(String account,String password);
public boolean UpdateUserState(String sessionId,String state,String account,String password);
public boolean UpdateUserStateBySession(String sessionId);
}
SERVICE层
serviceImpl
package com.demo.serviceImpl;
import java.util.List;
import com.demo.dao.LogDao;
import com.demo.entity.TbUsers;
import com.demo.service.LogService;
public class LogServiceImpl implements LogService{
private LogDao dao;
public void setDao(LogDao dao) {
this.dao = dao;
}
@Override
public List<TbUsers> Login(String account, String password) {
// TODO Auto-generated method stub
return dao.Login(account, password);
}
public boolean UpdateUserState(String sessionId,String state,String account,String password){
return dao.UpdateUserState(sessionId,state, account, password);
}
public boolean UpdateUserStateBySession(String sessionId){
return dao.UpdateUserStateBySession(sessionId);
}
}
service
package com.demo.service;
import java.util.List;
import com.demo.entity.TbUsers;
public interface LogService {
public List<TbUsers> Login(String account,String password);
public boolean UpdateUserState(String sessionId,String state,String account,String password);
public boolean UpdateUserStateBySession(String sessionId);
}
ACTION层
package com.demo.action;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.demo.dbutil.MySessionContext;
import com.demo.entity.TbUsers;
import com.demo.service.LogService;
public class LogAction {
private MySessionContext myc = MySessionContext.getInstance();
private LogService service;
public void setService(LogService service) {
this.service = service;
}
public String LogIn() throws Exception{
HttpServletResponse response = ServletActionContext.getResponse();
response.setCharacterEncoding("UTF-8");
HttpServletRequest request=ServletActionContext.getRequest();
HttpSession session = request.getSession(true);
session.setMaxInactiveInterval(60*30);//60*30秒失效
String account = null;
String password = null;
List<TbUsers> list = null;
String re = null;
if(request.getParameter("account")!=null){
account = request.getParameter("account").toString();
password = request.getParameter("password").toString();
}
try{
list = service.Login(account, password);
}catch(Exception e){
e.printStackTrace();
re = "unusual";
}
if(list!=null){
if(list.get(0).getUserD().equals("0")){
if(service.UpdateUserState(session.getId(),"1", account, password)){
myc.addSession(session);
re = "success";
}else{
re = "error";
}
}else{
if(service.UpdateUserState("","0", account, password)){
re = "exist";
}else{
re = "error";
}
//存在则断开session
HttpSession sess = myc.getSession(list.get(0).getUserC());
if(sess!=null){
myc.delSession(sess);
sess.invalidate();
}
}
}else{
re = "error";
}
try {
response.getWriter().write(re);
} catch (IOException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
list = null;
return null;
}
}
日志测试类
package com.demo.z_Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBack_Test {
private static final Logger logger = LoggerFactory.getLogger(LogBack_Test.class);
public static void main(String[] args) {
//LogBack打印日志测试
for(int i=0;i<10;i++){
logger.trace("打印第:" + i + " 个日志,trace");
logger.debug("打印第:" + i + " 个日志,debug");
logger.info("打印第:" + i + " 个日志,info");
logger.warn("打印第:" + i + " 个日志,warn");
logger.error("打印第:" + i + " 个日志,error");
}
}
}