第一个项目开发也有将近半年了,这也是自己的第一个比较成熟的系统,项目从刚开始的8个人到最后的三个人,开发团队的人也越来越少,系统也越来越成熟。中间开发也遇到了大量的问题,尤其在开发考试与试卷管理,由于界面交互太复杂一个JS可以上千行。17年11月中旬系统的第一次上线,我带着服务器到机房给客户安装部署好之后,客户的需求发生了很大的变化,直到18年元旦之后,系统才算正式开发完成,中间在客户的地方开发了将近20天,每天与客户沟通完需求之后进行开发,同时也要不停的修改别的代码,也真正理解到清晰的思路与清晰的代码的重要性,指不定哪天别人会修改你的代码,所以最好是将代码写的清除点,乱七八糟的代码容易暴露自己的实力。项目也逐渐成熟,直到项目后期才真正使用上git,刚开始人多只是不停的写代码,写完之后手动合并代码浪费了大量的时间。到客户的地方开发才使用上git,也理解了git的强大。
项目也学会了POI导出excel、iText导出pdf、jacob将word转为pdf、freemarker导出带格式的word以及利用jsoup在后台解析html标签。windows远程桌面访问、Teamview远程桌面访问。也学会了自己封装一些常用的工具类。
一、项目描述
开发环境:Eclipse+Jdk1.7+Git+Spring3.2+struts2.3+mybatis3.2+shiro1.3+mysql 5.7
前台技术:bootstrap+Easuui的分页插件+zTree树插件+jedate日期插件+kindeditor文本编辑器插件+其他
硬件:研腾身份证识别仪、南昊判卷机、指纹识别仪、人脸识别仪
项目描述:电厂安全培训管理系统是一个用于电厂进行培训的系统,主要是考试与成绩管理。考试可以分为在线考试与线下考试,在线考试就是登录系统之后在线考试,考完之后系统自动给出成绩,线下考试是将试卷导出成pdf进行线下考试,并用判卷机进行判卷之后将成绩导入系统。辅助的功能有部门管理与人员管理、违章管理。权限管理是系统的一大难点,采用了shiro框架,对不同的用户显示不同的菜单与显示界面上不同的按钮,也就是不同的用户可以看见不同的菜单,也可以看见不同的按钮。
菜单后台主界面:(主要模块)
二、前台总结:
前台技术:bootstrap+Easuui的分页插件+zTree树插件+jedate日期插件+kindeditor文本编辑器插件+其他
前台主要是bootstrap,分页统一用的是EasyUi的分页插件(使用参考:http://www.cnblogs.com/qlqwjy/p/7816707.html),树用的zTree插件(使用参考:http://www.cnblogs.com/qlqwjy/p/7351604.html),编辑器用的是kindeditor(使用参考:http://www.cnblogs.com/qlqwjy/p/7637386.html)。日期插件用的jeDate的日期插件。
前台学到最多的是jQuery的使用以及ajax的使用,项目中大量的使用异步请求数据,使用了jQuery封装的三种ajax:
前台学到的主要开发思想:打开模态框之前清除之前的废旧数据,分页点击的时候带着条件去查询(将当前页与页大小存到页面的隐藏域),有时候reset按钮不能清空隐藏域,最可靠的办法就是将需要被清空的东西加个特殊的class,点击清除的时候清空所有的带class的。
--------------------jQuery封装的三种ajax:---------------------------------
第一种: $.ajax
$.ajax({
url : contextPath + '/exam_getEmployeeIns4Exam.action',
data : $("#queryInnerForm").serialize(),
type : 'POST',
dataType : 'json',
success : showEmployeeInModal,
error : function() {
alert("查询内部员工出错!!!")
}
});
第二种:$.post
$.post(baseurl + '/distribute_getUnitInfoByBigIdAndUnitId.action', { "unitId" : unitId, "bigId" : bigId }, function(response) { var unit = response.unitInfo; $("#unitTbody").html(""); $("#unitTbody").append( '<tr><td>' + treeNode.name + '</td><td>' + unit.manager + '</td><td>' + unit.managerPhone + '</td><td>' + unit.secure + '</dh><td>' + unit.securePhone + '</td><td>' + unit.projectNames + '</td><td>' + unit.perNum + '</td><td>' + unit.unitMinisMum + '</td></tr>'); $("#unitInfoDiv").css("display", "block"); }, 'json')
第三种: $.get
$.get( "/Ajax/ajaxServlet2", //请求地址 //"name=qlq&password=qlq", //请求参数 {"name":"qlq","password":"nicai"}, //请求传递的参数,也可以是JSON function(data){ //data表示传递回来的数据,只有在成功的时候才有 alert(data); }, "json" //表示返回内容的格式,json会将传回来的自动解析成json对象 );
参考:http://www.cnblogs.com/qlqwjy/p/7357073.html
------------常用的jQuery的选择器=---------------------------------
参考:http://www.cnblogs.com/qlqwjy/p/7491721.html
三、后台总结:
后台技术:Eclipse+Jdk1.7+Git+Spring3.2+struts2.3+mybatis3.2+shiro1.3+mysql 5.7
后台用的技术主要就是SSM框架+Shiro,项目刚开始的时候是分模块开发,也没有使用上git,都是手工的合称代码。直到项目的后期才使用上git,也真正了解到git的作用(git 的使用参考:http://www.cnblogs.com/qlqwjy/p/7577381.html)
。spring主要用到了spring的IOC和AOP,IOC主要用来管理项目的对象,AOP用在事务管理。IOC主要使用基于注解方式的配置,AOP采用XML配置。(spring事务配置参考:http://www.cnblogs.com/qlqwjy/p/7296493.html)。struts采用页面跳转与json结合的方式,struts与json结合主要是struts的package继承json-default。剩下的json转换交给struts。mybatis开发采用动态代理的方式,简单的写一下接口然后在xml中实现即可。spring与struts整合主要是将struts创建对象的一个常量配置为spring:<constant name="struts.objectFactory" value="spring"></constant>,剩下的配置spring扫描struts的包就行了,struts的配置只用写类名(第一个字母小写)。后台在Action层对异常的捕捉以及对日志的记录非常重要,在部署之后可以通过日志查看错误信息
0.dp.properties
;;;;;;;;;;;;;;;;;;;;
;DataBaseConnection;
;;;;;;;;;;;;;;;;;;;;
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/exam9
jdbc.username=root
jdbc.password=123456
jdbc.initialPoolSize=10
jdbc.minPoolSize=5
jdbc.maxPoolSize=30
jdbc.maxIdleTime=200
jdbc.maxStatementsPerConnection=50
1.Spring框架的配置:
大致的配置过程:
配置数据库配置文件存放位置、将连接池放入spring容器(数据源)、配置mybatis会话工厂、配置mybatis扫描的基本包、配置事务管理器、配置事务模板对象、配置事务通知、将通知织入切面、配置扫描service与Action的包
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 0.连接池属性设置读取指定的properties文件 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 1.将连接池放入spring容器 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driver}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property> <property name="minPoolSize" value="${jdbc.minPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> <property name="maxIdleTime" value="${jdbc.maxIdleTime}"></property> <property name="maxStatementsPerConnection" value="${jdbc.maxStatementsPerConnection}"></property> </bean> <!--2. 配置 Mybatis的会话工厂 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 配置Mybatis的核心 配置文件所在位置 --> <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" /> </bean> <!-- 3.1 mapper代理配置方法一 这种方法需要大量重复的配置代理对象 MapperFactoryBean:根绝mapper接口生成代理对象 <bean id="selectUser" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="cn.qlq.core.dao.SelectUser"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> --> <!-- 3.2通过MapperScannerConfigurer扫描进行批量生成代理对象 遵循规范:mapper.java和mapper.xml名字一样且在同一个目录下 自动扫描出来的代理对象的id为mapper类类名(首字母小写) --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 指定扫描的包名,如果有多个,用半角逗号分隔 --> <property name="basePackage" value="cn.xm.exam.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!-- 4.配置事务管理器 --> <!-- 事务核心管理器,封装了事务操作,依赖于连接池 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 5.开启注解管理aop事务 --> <tx:annotation-driven /> <!-- 事务模板对象,依赖于事务核心管理器 --> <bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> <!-- ················开始使用XML管理事务························ --> <!-- 配置事务通知(无论哪种方式都要用到事务的核心管理器) --> <tx:advice transaction-manager="transactionManager" id="firstTx"> <tx:attributes> <!--以方法为单位,指定方法应用事务什么属性 isolation:隔离级别 read-only:只读属性 propagation:传播行为 --> <!-- 企业中运用通配符命名规则。两套增删改查(8种) --> <tx:method name="save*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="add*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="delete*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="remove*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="update*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="modify*" isolation="DEFAULT" read-only="false" propagation="REQUIRED" /> <tx:method name="get*" isolation="DEFAULT" read-only="true" propagation="REQUIRED" /> <tx:method name="find*" isolation="DEFAULT" read-only="true" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 配置织入 --> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* cn.xm.exam.service.impl.*.*ServiceImpl.*(..))" id="texPc" /> <!-- 配置切面:切点+通知 advice-ref:通知名称 pointcut-ref:切点名称 --> <aop:advisor advice-ref="firstTx" pointcut-ref="texPc" /> </aop:config> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* cn.xm.exam.service.impl.*.*.*ServiceImpl.*(..))" id="secondPc" /> <!-- 配置切面:切点+通知 advice-ref:通知名称 pointcut-ref:切点名称 --> <aop:advisor advice-ref="firstTx" pointcut-ref="secondPc" /> </aop:config> <!-- 5.开启组件自动扫描,也就是启用注解。前提是导入spring-context-3.2.xsd约束和引入新的命名空间 --> <context:component-scan base-package="cn.xm.exam.service"></context:component-scan> <!-- 6与struts2整合的配置 --> <!-- 扫描Action基本包 --> <context:component-scan base-package="cn.xm.exam.action"></context:component-scan> </beans>
2.mybatis配置:
SqlMapConfig.xml(主配置) 开启二级缓存与配置别名
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true" /> </settings> <!-- 只需要定义个别名,这个应该有 --> <typeAliases> <package name="cn.xm.exam.bean.common" /> <package name="cn.xm.exam.bean.employee" /> <package name="cn.xm.exam.bean.employee.in" /> <package name="cn.xm.exam.bean.employee.out" /> <package name="cn.xm.exam.bean.exam" /> <package name="cn.xm.exam.bean.grade" /> <package name="cn.xm.exam.bean.question" /> <package name="cn.xm.exam.bean.system" /> <package name="cn.xm.exam.bean.trainContent" /> </typeAliases> </configuration>
与接口对应的mapper(开启二级缓存)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="cn.xm.exam.mapper.exam.custom.ExampaperCustomMapper"> <!-- 开启二级缓存 --> <cache type="org.mybatis.caches.ehcache.EhcacheCache" /> <!-- S 分页查询试卷基本信息 --> <!-- SELECT COUNT(paperId) FROM exampaper WHERE title LIKE '%阳城%' AND LEVEL=1 --> <select id="getPaperTotalByCondition" parameterType="hashmap" resultType="int"> SELECT COUNT(paperId) FROM exampaper <where> <include refid="query_papaer_where"></include> </where> </select> <select id="findPapersByCondition" parameterType="hashmap" resultType="cn.xm.exam.bean.exam.Exampaper"> SELECT * FROM exampaper <where> <include refid="query_papaer_where"></include> </where> ORDER BY MAKETIME DESC <include refid="query_paper_limit"></include> </select> <!--S 查询条件 --> <!-- 查询试卷条件 --> <sql id="query_papaer_where"> <if test="title!=null"> and title like '%${title}%' </if> <if test="departmentId != null"> AND departmentId=#{departmentId} </if> <if test="level!=null"> and level=#{level} </if> </sql> <!--分页条件 --> <sql id="query_paper_limit"> <if test="index!=null"> LIMIT #{index},#{currentCount} </if> </sql> <!--E 查询条件 --> <!--E 分页查询试卷基本信息 --> <!-- S 查询一份试卷完整信息 --> <!-- 试卷映射map --> <resultMap type="cn.xm.exam.bean.exam.Exampaper" id="paperAllInfo"> <!-- 试卷基本信息 --> <id column="paperId" property="paperid" /> <result column="paperScore" property="paperscore" /> <result column="maketime" property="maketime" /> <result column="level" property="level" /> <result column="employeename" property="employeename" /> <result column="title" property="title" /> <result column="usetimes" property="usetimes" /> <result column="description" property="description" /> <!-- 大题 --> <collection property="bigQuestions" ofType="cn.xm.exam.bean.exam.Bigquestion"> <id column="bigquertionid" property="bigquertionid" /> <result column="paperid" property="paperid" /> <result column="bigquestionname" property="bigquestionname" /> <result column="bigquestionsequence" property="bigquestionsequence" /> <!-- 题 --> <collection property="questions" ofType="cn.xm.exam.bean.exam.Exampaperquestion"> <id column="questionid" property="questionid" /> <result column="paperid" property="paperid"></result> <result column="bigquertionid" property="bigquertionid"></result> <result column="questioncontent" property="questioncontent"></result> <result column="questionsequence" property="questionsequence"></result> <result column="type" property="type"></result> <result column="answer" property="answer"></result> <result column="analysis" property="analysis"></result> <result column="score" property="score"></result> <result column="questionsource" property="questionsource"></result> <!-- 选项 --> <collection property="options" ofType="cn.xm.exam.bean.exam.Exampaperoption"> <id column="optionid" property="optionid" /> <result column="questionid" property="questionid" /> <result column="optioncontent" property="optioncontent" /> <result column="optionsequence" property="optionsequence" /> <result column="description" property="description" /> <result column="isanswer" property="isanswer" /> </collection> </collection> </collection> </resultMap> <select id="findExamPaperAllInfoByPaperId" parameterType="string" resultMap="paperAllInfo"> SELECT paper.*,big.*,ques.*,opt.* FROM exampaper paper,bigquestion big,exampaperquestion ques,exampaperoption opt WHERE paper.paperId=#{value} AND paper.paperId = big.paperId AND ques.bigquertionid = big.bigquertionid AND opt.questionId = ques.questionId ORDER BY big.bigquestionsequence ASC,ques.questionsequence ASC,opt.optionsequence ASC </select> <!-- E 查询一份试卷完整信息 --> <!-- S 根据试卷ID增加试卷的使用次数 --> <update id="addExampaperUsetimes" parameterType="string"> UPDATE `exampaper` SET useTimes = useTimes+1 WHERE paperId=#{value} </update> <!-- E 根据试卷ID增加试卷的使用次数 --> <!-- S 根据试卷ID减少试卷的使用次数 --> <update id="minusExampaperUsetimes" parameterType="string"> UPDATE `exampaper` SET useTimes = useTimes-1 WHERE paperId=#{value} </update> <!-- E 根据试卷ID减少试卷的使用次数 --> </mapper>
mybatis开启二级缓存的配置:(与encache整合 默认就是开启)
(1) 在核心配置文件SqlMapConfig.xml中settings标签中加入下面开启总缓存
<setting name="cacheEnabled" value="true"/>
(2)在UserMapper.xml中开启二缓存 (实体需要实现序列化接口 serializable)
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
encache缓存的配置:
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore path="F:\develop\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
3.struts配置:
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"> <struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <constant name="devMode" value="true"></constant> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <!-- 配置拦截的后缀 --> <constant name="struts.action.extension" value="action,do" /> <!-- 与spring整合 --> <constant name="struts.objectFactory" value="spring"></constant> <!-- leilong --> <include file="struts/question.xml"></include> <include file="struts/grade.xml"></include> <include file="struts/onlineExam.xml"></include> <include file="struts/employeeOutPerson.xml"></include> <!-- 乔利强引入的 --> <include file="struts/ExamPaper.xml"></include> <include file="struts/Exam.xml"></include> <include file="struts/Haul.xml"></include> <include file="struts/Unit.xml"></include> <include file="struts/Distribute.xml"></include> <!-- 张文艺引入的 --> <include file="struts/innerdepartment.xml"></include> <include file="struts/innerdepartmentEmp.xml"></include> <!-- lixianyun start --> <include file="struts/traincontent.xml"></include> <include file="struts/dictionary.xml"></include> <include file="struts/newscenter.xml"></include> <include file="struts/project.xml"></include> <include file="struts/breakrules.xml"></include> <include file="struts/emplyInBreakrules.xml"></include> <!-- lixianyuan end --> <!-- mf start --> <include file="struts/struts_system.xml"></include> <!-- mf end --> <!-- jx start --> <include file="struts/newscenter.xml"></include> <!-- jx end --> </struts>
struts一个模块的配置:
Exam.xml (继承struts-default与json-default)
<?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"> <struts> <package name="exam" namespace="/" extends="json-default,struts-default"> <!-- 全局结果集,将response转换为json传到前台 --> <global-results> <result name="success" type="json"> <param name="root">response</param> </result> </global-results> <!-- 添加考试 --> <action name="exam_*" class="addExamAction" method="{1}"></action> <!-- 查询考试 --> <action name="findExam_*" class="findExamAction" method="{1}"></action> <!--删除考试 --> <action name="deleteExam" class="deleteExamAction"></action> <!--修改考试 --> <action name="UpdateExam_*" class="updateExamAction" method="{1}"> <!-- 将信息带到修改界面 (页面跳转的方式) --> <result name="findExam">/view/examParper/exam/modifyExam.jsp</result> <!-- 修改考试(ajax+json方式) --> </action> <!-- 导出参考人员信息 --> <action name="exportExamEmployees" class="extExamEmployeesAction"> <result type="stream"> <!-- 其他的参数在类中设置或者使用默认 --> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="bufferSize">8192</param> </result> </action> <!-- 导出试卷答案信息 --> <action name="extPaperAnswer" class="extPaperAnswerAction"> <result type="stream"> <!-- 其他的参数在类中设置或者使用默认 --> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="bufferSize">8192</param> </result> </action> <!-- 导出试卷信息 --> <action name="extPaper" class="extExamPaperAction"> <result type="stream"> <!-- 其他的参数在类中设置或者使用默认 --> <param name="contentType">application/octet-stream</param> <param name="inputName">inputStream</param> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="bufferSize">8192</param> </result> </action> <!-- 下载专区的文件下载 --> <action name="export_*" class="extDownloadFile"> <!-- 为setServerPath方法中的serverPath属性注入值 也就是文件存放的地址/目录(文件应该从哪里下载) --> <param name="serverPath">/files/downloadFiles</param> <!-- 文件下载的关键:视图类型一定是stream --> <result type="stream"> <!-- 往StreamResult类中的属性注入内容 --> <!-- 返回给浏览器的文件类型。返回通用的二进制 --> <param name="contentType">application/octet-stream</param> <!-- 返回给浏览器的输入流 --> <!-- 默认就是 inputStream,它将会指示 StreamResult 通过 inputName 属性值的 getter 方法, 比如这里就是 getInputStream() 来获取下载文件的内容,意味着你的 Action 要有这个方法 --> <param name="inputName">inputStream</param> <!-- 告诉浏览器的方式下载资源 ${name}:获取Action中getName()方法的数据 --> <!-- 默认为 inline(在线打开),设置为 attachment 将会告诉浏览器下载该文件,filename 指定下载文 件保存的文件名,若未指定将会是以浏览的页面名作为文件名,如以 download.action 作为文件名, 这里使用的是动态文件名,${name}, 它将通过 Action 的 getName() 获得文件名 --> <param name="contentDisposition">attachment;filename=${name}</param> <!-- 缓存大小 --> <param name="bufferSize">1024</param> </result> </action> </package> </struts>
4.shiro配置:
applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> <!-- 记住我 --> <property name="rememberMeManager" ref="rememberMeManager"/> <!-- 注入缓存管理器 --> <property name="cacheManager" ref="cacheManager"/> <!-- 注入session管理器 --> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 自定义Realm --> <bean id="myRealm" class="cn.xm.exam.utils.realm.MyRealm" /> <!-- 自定义form认证过虑器 --> <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 --> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter "> <property name="usernameParam" value="username" /> <property name="passwordParam" value="password" /> <property name="rememberMeParam" value="rememberMe"/> </bean> <!-- ehcache缓存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/> </bean> <!-- session会话管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session失效时间 单位毫秒 --> <property name="globalSessionTimeout" value="18000000"/> <!-- 删除失效的session --> <property name="deleteInvalidSessions" value="true"/> </bean> <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 --> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="rememberMeCookie" /> </bean> <!-- 会话Cookie模板 --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="sid"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="-1"/> </bean> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="2592000"/><!-- 30天 --> </bean> <!-- 自定义form认证过虑器 --> <bean id="permfilter" class="cn.xm.exam.utils.realm.ShiroPermsFilter" scope="prototype"> </bean> <!-- logout --> <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter"> <property name="redirectUrl" value="/index.jsp" /> </bean> <!-- shiro 过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 --> <property name="loginUrl" value="/index.jsp" /> <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 --> <!-- <property name="successUrl" value="/login"/> --> <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面--> <property name="unauthorizedUrl" value="unauthorized.jsp" /> <property name="filters"> <map> <entry key="logout" value-ref="logoutFilter" /> </map> </property> <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 --> <property name="filterChainDefinitions"> <value> <!-- 对静态资源设置匿名访问 --> / = anon <!-- 不拦截首页的地址 --> /index.jsp = anon /newsIP_**.action = anon /train_**.action = anon /unauthorized.jsp = anon /user_login.action = anon /view/public/** = anon /view/index/** = anon /bs/** = anon /controls/** = anon /css/** = anon /image/** = anon /js/** = anon /META-INF/** = anon <!-- 请求 logout.action地址,shiro去清除session--> /logout.action = logout <!-- /** = authc 所有url都必须认证通过才可以访问--> /** = authc </value> </property> </bean> <!-- 开启Shiro注解 --> <!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true"></property> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> --> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>
shiro缓存配置
shiro-ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!--diskStore:缓存数据持久化的目录 地址 --> <diskStore path="F:\develop\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
5.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" 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>xm</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param> <!-- shiro过滤器定义 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <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> <!-- 404页面 --> <error-page> <error-code>404</error-code> <location>/404.jsp</location> </error-page> <!-- 500页面 --> <error-page> <error-code>500</error-code> <location>/500.jsp</location> </error-page> </web-app>
四、mysql数据库总结:
项目在使用mysql的过程中也遇到了好多问题,并通过各种手段解决了这些问题。
1. 索引:
在查询的时候有时候特别慢,通过项目也了解到索引的重要性,索引是真实存在于表中的,
mysql有哪些索引类型:
-
数据结构角度上可以分:B+tree索引,hash索引,fulltext索引(innodb,myisam都支持)
-
存储角度上可以分:聚集索引,非聚集索引
-
逻辑角度上可以分:primary key,normal key,单列,复合,覆盖索引
索引也会占用内存。就对某秀特殊的列建立了索引,有时候通过代码建立索引,有时候通过sqlyog建立索引。参考:http://www.cnblogs.com/qlqwjy/p/7767279.html
2. 触发器:
项目中在扣个人积分的同时使用触发器自动修改器对应部门的积分,触发器是建立在某个表上针对表的某种操作建立的触发事件,参考:http://www.cnblogs.com/qlqwjy/p/7842647.html
下面是项目中的一个触发器:
DELIMITER $$ USE `exam9`$$ DROP TRIGGER /*!50032 IF EXISTS */ `add_unit_after_add_break`$$ CREATE /*!50017 DEFINER = 'root'@'localhost' */ TRIGGER `add_unit_after_add_break` AFTER INSERT ON `breakrules` FOR EACH ROW BEGIN DECLARE big_id VARCHAR(40); DECLARE unit_id VARCHAR(40); SET big_id=(SELECT bigId FROM haulemployeeout WHERE BigEmployeeoutId=new.BigEmployeeoutId); SET unit_id=(SELECT unitId FROM haulemployeeout WHERE BigEmployeeoutId=new.BigEmployeeoutId); UPDATE haulunit SET unitMinisMum=unitMinisMum+new.minusNum WHERE unitId=unit_id AND bigId=big_id; END; $$ DELIMITER ;
3.存储过程
相当于sql脚本,可以执行多条sql语句,项目在根据时间动态修改状态的时候使用的是存储过程,比如根据当前时间与考试的开始时间动态修改考试的状态,参考:http://www.cnblogs.com/qlqwjy/p/7920012.html
下面是项目中的一个不带输入输出参数的存储过程:
DELIMITER $$ USE `exam9`$$ DROP PROCEDURE IF EXISTS `updateStatus`$$ CREATE DEFINER=`root`@`localhost` PROCEDURE `updateStatus`() BEGIN UPDATE exam SET `status`="已结束" WHERE `status` != "已结束" AND (NOW() - endTime)>0 ; UPDATE exam SET `status`="正在答题" WHERE `status` != "已结束" AND ( NOW() - startTime)>=0 AND (NOW() - endTime)<=0; UPDATE haulinfo SET bigStatus="已结束" WHERE bigStatus != "已结束" AND (CURDATE() - bigEndDate)>0; UPDATE haulinfo SET bigStatus="进行中" WHERE (CURDATE() - bigEndDate)<=0 AND (CURDATE() - bigBeginDate)>=0; UPDATE exam SET bigStatus=(SELECT bigStatus FROM haulinfo WHERE bigId=exam.bigId); END$$ DELIMITER ;
存储过程与自定义函数的区别: 存储过程实现的过程要复杂一些,而函数的针对性较强; 存储过程可以有多个返回值,而自定义函数只有一个返回值; 存储过程一般独立的来执行,而函数往往是作为其他SQL语句的一部分来使用;
4.事件与任务调度
项目中遇到大量的根据时间修改状态,另外有一个功能就是每年的1月1日清空积分,第一个修改状态是创建一个每秒钟执行一次的事件,第二个需求是每年执行一次并且开始执行是在1月1日。任务调度需要结合存储过程来完成其功能,每秒钟或者每年执行存储过程(:参考:http://www.cnblogs.com/qlqwjy/p/7954175.html)。下面附两个项目的事件
事件使用的前提是开启任务调度:
#查看是否开启任务调度:(on代表开) show variables like '%event_scheduler%';
# 开启事件调度器 SET GLOBAL event_scheduler = ON; SET @@global.event_scheduler = ON; SET GLOBAL event_scheduler = 1; SET @@global.event_scheduler = 1; 或者是通过配置文件my.cnf event_scheduler = 1 #或者ON
第一个:每秒钟都执行修改状态的存储过程:
更新状态的存储过程:
DELIMITER $$ USE `exam9`$$ DROP PROCEDURE IF EXISTS `updateStatus`$$ CREATE DEFINER=`root`@`localhost` PROCEDURE `updateStatus`() BEGIN UPDATE exam SET `status`="已结束" WHERE `status` != "已结束" AND (NOW() - endTime)>0 ; UPDATE exam SET `status`="正在答题" WHERE `status` != "已结束" AND ( NOW() - startTime)>=0 AND (NOW() - endTime)<=0; UPDATE haulinfo SET bigStatus="已结束" WHERE bigStatus != "已结束" AND (CURDATE() - bigEndDate)>0; UPDATE haulinfo SET bigStatus="进行中" WHERE (CURDATE() - bigEndDate)<=0 AND (CURDATE() - bigBeginDate)>=0; UPDATE exam SET bigStatus=(SELECT bigStatus FROM haulinfo WHERE bigId=exam.bigId); END$$ DELIMITER ;
一个每秒钟执行一次的事件调用上面的存储过程:
DELIMITER $$ ALTER DEFINER=`root`@`localhost` EVENT `eventUpdateStatus` ON SCHEDULE EVERY 1 SECOND STARTS '2017-11-21 00:12:44' ON COMPLETION PRESERVE ENABLE DO CALL updateStatus()$$ DELIMITER ;
第二个:每年的1月1日清空积分
存储过程:
DELIMITER $$ USE `exam9`$$ DROP PROCEDURE IF EXISTS `clearEmInBreakInfo`$$ CREATE DEFINER=`root`@`localhost` PROCEDURE `clearEmInBreakInfo`() BEGIN DELETE FROM emplyin_breakrules WHERE empInMinusnum < '12'; DELETE FROM blacklist WHERE temporaryInStatus = '0' AND employeeStatus = '0'; END$$ DELIMITER ;
每年的1月1日执行的事件调用上面的存储过程:
DELIMITER $$ ALTER DEFINER=`root`@`localhost` EVENT `eventEmpInBreaksInfo` ON SCHEDULE EVERY 1 YEAR STARTS '2018-01-01 01:00:00' ON COMPLETION PRESERVE ENABLE DO CALL clearEmInBreakInfo()$$ DELIMITER ;
5.其他:
项目中也遇到了自定义变量,通过对查出的两列进行除法运算用到了自定义变量。参考:http://www.cnblogs.com/qlqwjy/p/7788324.html
列转行,对一个人分配部门与班组时需要将多行转为一列,遇到了列转行。参考:http://www.cnblogs.com/qlqwjy/p/7837569.html
中文排序:
SELECT * FROM employee_in ORDER BY CONVERT(NAME USING gbk)
还遇到一些根据年份统计或者根据不同的情况进行查询,参考case when then用法(用于分类统计):http://www.cnblogs.com/qlqwjy/p/7476533.html
SQL随机抽取几条数据:http://www.cnblogs.com/qlqwjy/p/7603170.html
计算时间差函数:http://www.cnblogs.com/qlqwjy/p/7715968.html
MySql授权和撤销权限操作http://www.cnblogs.com/qlqwjy/p/8022575.html
五、部署与优化
服务器配置:windows server 2012 64G运行内存
部署主要就是将tomcat设置为服务器启动,每次更新程序将服务停掉,更换war包。mysql设置任务调度开机开启 (参考:http://www.cnblogs.com/qlqwjy/p/7954175.html),设置mysql最长连接时间:(参考:http://www.cnblogs.com/qlqwjy/p/7798330.html)。tomcat优化设计并发优化、JVM参数优化以及JVM参数查看(参考:http://www.cnblogs.com/qlqwjy/p/8037392.html),tomcat配置虚拟路径参考:http://www.cnblogs.com/qlqwjy/p/7242255.html
1.tomcat优化
(1)并发优化
在部署的时候因为tomcat的server.xml配置中出现中文注释导致启动一直失败,因此最好在server.xml中不要用中文注释,有可能导致启动失败。
服务器server.xml配置:
<?xml version='1.0' encoding='utf-8'?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!-- Note: A "Server" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/server.html --> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <!-- Security listener. Documentation at /docs/config/listeners.html <Listener className="org.apache.catalina.security.SecurityListener" /> --> <!--APR library loader. Documentation at /docs/apr.html --> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> <Listener className="org.apache.catalina.core.JasperListener" /> <!-- Prevent memory leaks due to use of particular java/javax APIs--> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html --> <GlobalNamingResources> <!-- Editable user database that can also be used by UserDatabaseRealm to authenticate users --> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <!-- A "Service" is a collection of one or more "Connectors" that share a single "Container" Note: A "Service" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/service.html --> <Service name="Catalina"> <!--The connectors can use a shared executor, you can define one or more named thread pools--> <!-- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/> --> <!-- A "Connector" represents an endpoint by which requests are received and responses are returned. Documentation at : Java HTTP Connector: /docs/config/http.html (blocking & non-blocking) Java AJP Connector: /docs/config/ajp.html APR (HTTP/AJP) Connector: /docs/apr.html Define a non-SSL HTTP/1.1 Connector on port 8080 --> <Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443" maxThreads="500" minSpareThreads="20" acceptCount="100" disableUploadTimeout="true" enableLookups="false" URIEncoding="UTF-8" /> <!-- A "Connector" using the shared thread pool--> <!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> --> <!-- Define a SSL HTTP/1.1 Connector on port 8443 This connector uses the BIO implementation that requires the JSSE style configuration. When using the APR/native implementation, the OpenSSL style configuration is required as described in the APR/native documentation --> <!-- <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> --> <!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <!-- An Engine represents the entry point (within Catalina) that processes every request. The Engine implementation for Tomcat stand alone analyzes the HTTP headers included with the request, and passes them on to the appropriate Host (virtual host). Documentation at /docs/config/engine.html --> <!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> --> <Engine name="Catalina" defaultHost="localhost"> <!--For clustering, please take a look at documentation at: /docs/cluster-howto.html (simple how to) /docs/config/cluster.html (reference documentation) --> <!-- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> --> <!-- Use the LockOutRealm to prevent attempts to guess user passwords via a brute-force attack --> <Realm className="org.apache.catalina.realm.LockOutRealm"> <!-- This Realm uses the UserDatabase configured in the global JNDI resources under the key "UserDatabase". Any edits that are performed against this UserDatabase are immediately available for use by the Realm. --> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Context reloadable="true" path="/file" docBase="D:\images\videos" debug="0"/> <Context reloadable="true" path="/files/EmployeeIn" docBase="D:\images\employeeInPhotos" debug="0"/> <Context reloadable="true" path="/employeeOutPhotos" docBase="D:\images\employeeOutPhotos" debug="0"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server>
(2)JVM优化
主要通过以下的几个jvm参数来设置堆内存的: -Xmx512m 最大总堆内存,一般设置为物理内存的1/4 -Xms512m 初始总堆内存,一般将它设置的和最大堆内存一样大,这样就不需要根据当前堆使用情况而调整堆的大小了 -Xmn192m 年轻带堆内存,sun官方推荐为整个堆的3/8 堆内存的组成 总堆内存 = 年轻带堆内存 + 年老带堆内存 + 持久带堆内存 年轻带堆内存 对象刚创建出来时放在这里 年老带堆内存 对象在被真正会回收之前会先放在这里 持久带堆内存 class文件,元数据等放在这里 -XX:PermSize=128m 持久带堆的初始大小 -XX:MaxPermSize=128m 持久带堆的最大大小,eclipse默认为256m。如果要编译jdk这种,一定要把这个设的很大,因为它的类太多了。
在开发当中,当一个项目比较大时,依赖的jar包通常比较多,我们都知道,在应用服务器启动时,会将应用引用到的所有类通过ClassLoader 依次全部加载到内存当中。Java的逻辑内存模型大致分为堆内存、栈内存、静态内存区,也称持久区,该区的内存不会被GC回收。堆内存用于存储类的实例、 数组等引用类型数据,也就是用new生成的对象,都存放在这里,栈内存存储局部变量(如:方法参数),静态内存区存储常量、静态变量、类元数据信息(方 法、属性等)。开发当中常遇到的三类内存溢出异常:
- java.lang.OutOfMemoryError: Java heap space异常
表示堆内存空间满了,如果不是程序逻辑的bug,可能是因为项目中引用的jar比较多,导到内存溢出。JVM默认堆的最小使用内存为物理内存的1/64, 最大使用内存为物理内存的1/4,如8G的物理内存,JVM默认堆的最小和最大内存分别为128m和2048m。通过调整JVM的-Xms(初始内存)和 -Xmx(最大内存)两个参数加大内存使用限制。 - java.lang.OutOfMemoryError: PermGen space异常
表示静态内存区满了,通常是由于加载的类过多导致。jdk8以下版本通过修改JVM的-XX:PermSize和-XX:MaxPermSize两个参 数,限制静态区最小和最大内存范围。jdk8改变了内存模型,将类定义存放到了元数据(MetaspaceSize)空间,而元数据空间是与堆空间共享同 一块内存区域的,所以在JDK8以后版本不会存在PermGen space异常了,故不用设置此参数。 - java.lang.StackOverflowError异常
表示栈内存溢出。通常是由于死循环、无限递归导致。
优化方法:
修改Tomcat的内存配置,打开$TOMCAT_HOME/bin/catalina.sh文件(Windows系统是catalina.bat文件),大楖在250行左右,在JAVA_OPTS参数上添加内存参数设置即可。完整的JVM参数设置如下所示:
linux修改TOMCAT_HOME/bin/catalina.sh,在前面加入
JAVA_OPTS="$JAVA_OPTS -server -Xms2048m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=256 -Djava.awt.headless=true"
windows修改TOMCAT_HOME/bin/catalina.bat,在前面加入
set JAVA_OPTS=-server -Xms2048m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=256 -Djava.awt.headless=true
如果是Windows配置服务式的参考我的另一篇博客:
windows下注册tomcat服务以及设置jvm参数
-server参数:表示以服务模式启动,启动速度会稍微慢一点,但性能会高很多。不加这个参数,默认是以客户端模式启动。
java.awt.headless=true参数:与图形操作有关,适用于linux系统。如生成验证码,含义是当前使用的是没有安装图安装图形界面的服务器,应用中如果获取系统显示有关参数会抛异常。
(3)数据库连接池c3p0配置
dp.properties
;;;;;;;;;;;;;;;;;;;;
;DataBaseConnection;
;;;;;;;;;;;;;;;;;;;;
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/exam9
jdbc.username=root
jdbc.password=123456
jdbc.initialPoolSize=10
jdbc.minPoolSize=5
jdbc.maxPoolSize=30
jdbc.maxIdleTime=200
jdbc.maxStatementsPerConnection=50
spring配置:
<!-- 0.连接池属性设置读取指定的properties文件 --> <context:property-placeholder location="classpath:db.properties" /> <!-- 1.将连接池放入spring容器 --> <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driver}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property> <property name="minPoolSize" value="${jdbc.minPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> <property name="maxIdleTime" value="${jdbc.maxIdleTime}"></property> <property name="maxStatementsPerConnection" value="${jdbc.maxStatementsPerConnection}"></property> </bean>
参考:http://www.cnblogs.com/qlqwjy/p/8067275.html
整个开发也经历了将近半年,学到的更多的是开发思想与一些常用的开发技术,在平时开发中也都记录下来啊了。
一个牛逼的程序员不仅是每天是敲多少行代码,更重要的是每天能学到多少,要时刻保持清晰的思路,养成良好的编程思路。