??????? JSPXCMS開發(fā)架構(gòu)介紹
V1 – 架構(gòu)概述
基本概述
配置文件目錄
/src/main/resources/conf/plugin/plug
有以下文件:
backend.xml:后臺Controller配置文件
conf.properties:前臺模板Freemarker標(biāo)簽沦寂、國際化文件目錄等配置文件
context.xml:Spring配置文件
menu.yml:后臺菜單及權(quán)限配置文件
源代碼目錄
/src/main/java/com/jspxcms/plug
有以下包:
domain:實體類Entity
repository:數(shù)據(jù)庫操作類Dao
service:服務(wù)類Service
web.back:后臺Controller
web.fore:前臺Controller
web.directive:FreeMarker標(biāo)簽類
后臺JSP文件
/src/main/webapp/WEB-INF/views/plug
前臺模板文件
/src/main/webapp/template/1/default/
國際化文件
/src/main/resources/messages/plugin/plug?后臺國際化文件
/src/main/resources/messages/plugin/plugfore?前臺國際化文件
編輯和查看國際化文件学密,請安裝Eclipse的Properties Editor插件。否則不能看到中文传藏,只能看到\u5217\u8868之類的代碼腻暮;并且在編輯時直接輸入中文,頁面會顯示為亂碼毯侦。
源代碼目錄結(jié)構(gòu)詳解
webapp目錄
jsp?jsp文件哭靖。如果需要可以直接訪問的jsp頁面,可以放在這個文件夾下侈离,放到其他文件夾下的jsp是無法直接訪問的试幽。訪問路徑不需要加上jsp路徑,例如/jsp/abc.jsp文件的訪問路徑為abc.jsp卦碾。
static?靜態(tài)資源文件铺坞。
css
img
js
vendor?第三方組件庫。如jquery洲胖、bootstrop济榨、ueditor、ztree宾濒、My97DatePicker等腿短。
template?前臺FreeMarker模板。
uploads?文件上傳目錄绘梦。
WEB-INF
fulltext?Lucene全文檢索文件目錄。
tags?后臺jsp標(biāo)簽懒闷。
tlds?JSTL functions飞崖。
views?后臺jsp頁面堤器。
commons?部分公用jsp頁面。
core?核心模塊jsp頁面榄棵。
error?發(fā)生異常時顯示的jsp頁面。
ext?擴(kuò)展模塊jsp頁面潘拱。
plug?插件模塊jsp頁面疹鳄。
index.jsp?后臺首頁框架頁。
login.jsp?后臺登錄頁面芦岂。
weblogic.xml?用于部署在weblogic瘪弓。
crossdomain.xml?跨域策略文件。主要針對flash禽最。
favicon.ico?瀏覽器頭部圖標(biāo)腺怯。
Java源碼
com.jspxcms.common?公用組件袱饭。
captcha?驗證碼。
file?文件相關(guān)呛占。
freemarker
fulltext?全文索引虑乖。
image?圖片相關(guān)。
ip?通過IP地址查詢實際地址晾虑。
office?word轉(zhuǎn)html疹味。
orm?對象關(guān)系映射。JPA及SpringDataJPA相關(guān)輔助類帜篇。
security?安全相關(guān)佛猛。如密碼加密等。
upload?上傳相關(guān)坠狡。
util?工具類继找。
web?SpringMVC等web相關(guān)類。
com.jspxcms.core?核心模塊逃沿。
constant?靜態(tài)變量婴渡。
domain?實體類。
fulltext?全文索引凯亮。
holder
html?生成靜態(tài)頁边臼。
listener?監(jiān)聽器。
quartz?定時器假消。
repository?數(shù)據(jù)庫持久化層柠并。
security?安全相關(guān)。
service?服務(wù)層富拗。
support?支持類臼予。
web?Controller層。
back?后臺Controller啃沪。
directive?FreeMarker標(biāo)簽粘拾。
fore?前臺Controller。
method?FreeMarker方法创千。
com.jspxcms.ext?擴(kuò)展模塊缰雇。
com.jspxcms.com?插件模塊。
Resources目錄
源碼包的resources目錄在/src/main/resources追驴,安裝包里的resources目錄在/WEB-INF/classes械哟。
conf?配置文件目錄。
core?核心模塊配置文件殿雪。
plugin?插件模塊配置文件暇咆。
conf.properties?系統(tǒng)properties配置文件。
context.xml?spring context配置文件冠摄。
context-quartz.xml?定時任務(wù)配置文件糯崎。
menu.yml?后臺菜單配置文件几缭。
spring.jpa.propertis?spring jpa配置文件。
ehcache?ehcache緩存配置文件沃呢。
messages?國際化文件
application.properties?spring-boot配置文件年栓。
config.properties?微博第三方登錄配置。
custom.xml?驗證碼薄霜、全文索引配置文件某抓。
IKAnalyzer.cfg.xml?IK Analyzer配置文件。
qqconnectconfig.properties?QQ第三方登錄配置文件惰瓜。
qqwry.dat?IP地址數(shù)據(jù)庫否副。
quartz.properties?定時任務(wù)配置文件。
stopword.dic?IK Analyzer停止詞文件崎坊。
stopword_ext.dic?IK Analyzer停止詞擴(kuò)展文件备禀。
weixin.properties?微信配置文件。
V2 – 配置文件介紹
二次開發(fā)的核心就是配置文件奈揍,通過配置文件將不同模塊和插件整合到一起曲尸。可以在這個目錄下新建自己的文件夾男翰,如:abc另患、novel等,本例為plug蛾绎,即/src/main/resources/conf/plugin/plug
Spring配置自動加載
配置文件目錄及其子目錄下文件名為context*.xml的配置文件會自動加載為Spring的WebApplicationContext昆箕。
觸發(fā)自動加載的類是com.jspxcms.core.Application,相關(guān)代碼為:@ImportResource({"classpath:conf/**/context*.xml",
"classpath:custom.xml" })租冠。
本例的配置文件/src/main/resources/conf/plugin/plug/context.xml鹏倘,符合自動加載規(guī)則,文件中
class="com.jspxcms.plug.ContextConfig" />會加載該類中的配置肺稀。在這個類里分別加載了:
Entity:@EntityScan({
? ? "com.jspxcms.plug.domain" })
Dao:@EnableJpaRepositories(basePackages
? ? = { "com.jspxcms.plug.repository" }, repositoryFactoryBeanClass
? ? = MyJpaRepositoryFactoryBean.class)
Service和前臺Controller:@ComponentScan({
? ? "com.jspxcms.plug.service.impl",
? ? "com.jspxcms.plug.web.fore" })
后臺Controller配置自動加載
配置文件目錄及其子目錄下文件名為backend*.xml的配置文件會自動加載為后臺Controller
配置文件目錄及其子目錄下文件名為backend*.xml的配置文件會自動加載為后臺Controller第股。
觸發(fā)自動加載的類是com.jspxcms.core.BackendWebConfig,相關(guān)代碼為:@ImportResource({ "classpath:conf/**/backend*.xml"
})话原。
本例的配置文件/src/main/resources/conf/plugin/plug/backend.xml符合自動加載規(guī)則。
<context:component-scanbase-package="com.jspxcms.plug.web.back"use-default-filters="false">
? <context:include-filtertype="annotation"expression="org.springframework.stereotype.Controller"/>
? <context:include-filtertype="annotation"expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
context:component-scan>
這個配置會自動加載com.jspxcms.plug.web.back包下所有帶有Controller或ControllerAdvice注解的類诲锹。
conf.properties自動加載
前臺模板Freemarker標(biāo)簽繁仁、國際化文件目錄等配置在conf.properties中配置。
配置文件目錄及其子目錄下文件名為conf*.properties的文件會自動加載归园。
觸發(fā)自動加載的類是com.jspxcms.core.ShiroConfig黄虱,相關(guān)代碼為:
loader.setValue("classpath:conf/plugin/**/conf*.properties","classpath:conf/conf.properties");。
本例的配置文件/src/main/resources/conf/plugin/plug/conf.properties庸诱,符合自動加載規(guī)則捻浦。
menu.yml自動加載
后臺功能菜單及權(quán)限由該文件配置晤揣。
配置文件目錄及其子目錄下文件名為menu*.yml的文件會自動加載。
觸發(fā)自動加載的類是com.jspxcms.core.MenuConfig朱灿,相關(guān)代碼為:appContext.getResources("classpath:conf/**/menu*.yml");
本例的配置文件/src/main/resources/conf/plugin/plug/menu.properties昧识,符合自動加載規(guī)則。
V3 - 菜單與權(quán)限
Jspxcms的菜單和權(quán)限信息存放在配置中盗扒,方便管理跪楞、維護(hù)和升級;只需要在一個配置文件中設(shè)置好侣灶,即可以無縫整合系統(tǒng)的菜單甸祭、權(quán)限、賦權(quán)等問題褥影,無需另外修改代碼和頁面池户。
開發(fā)時可以根據(jù)需要,將新功能菜單加到任意的一級菜單下的任意位置凡怎,也可以自己新增一級菜單校焦,新增的一級菜單也可以放在任何你想要的位置上。
菜單介紹
后臺菜單分為兩級栅贴,如內(nèi)容管理斟湃、文件管理、用戶權(quán)限檐薯、系統(tǒng)管理為一級菜單凝赛,文檔管理、欄目管理坛缕、用戶管理墓猎、角色管理為二級菜單。
后臺菜單目前不支持三級赚楚。
配置文件
/src/main/resources/conf/plugin/plug/menu.yml
菜單配置文件支持通配加載毙沾,符合classpath:conf/**/menu*.yml這個規(guī)則的配置文件都會加載,系統(tǒng)中其它菜單的配置位于/src/main/resources/conf/menu.yml宠页。
加載菜單配置文件由com.jspxcms.core.MenuConfig的appContext.getResources("classpath:conf/**/menu*.yml");代碼加載
一級菜單配置
在/src/main/resources/conf/menu.yml文件中有一級菜單的配置左胞。
600:
? name: navigation.plug
? icon: fa fa-plug
? perms: nav_plug
600:?菜單序號,決定一級菜單排列的位置举户。比如序號500的菜單會排在600的前面烤宙,而序號550則會排在500與600之間。注意:這個序號在所有的菜單配置文件中必須是唯一的俭嘁,如果已經(jīng)有600序號的菜單躺枕,則不能再建一個同樣序號的菜單,可以為610或者558。
name: navigation.plug?菜單名稱拐云。其中navigation.plug是國際化代碼罢猪,在/src/main/resources/messages目錄下的文件里有這個代碼對應(yīng)的中文。也可以直接寫中文叉瘩,如name: 我的菜單膳帕。
icon: fa fa-plug?菜單圖標(biāo)。其中fa fa-plug是圖標(biāo)樣式房揭,請參考:https://getbootstrap.com/docs/3.3/components/备闲。
perms: nav_plug?權(quán)限值。一級菜單通常都是虛擬菜單捅暴,點擊后只是展開二級菜單恬砂,不需要訪問后臺,所以權(quán)限值可以隨意定義蓬痒,只要不和原有權(quán)限值重復(fù)即可泻骤。
二級菜單配置
在/src/main/resources/conf/plugin/plug/menu.yml文件中有二級菜單的配置。
600-1000:
? name:resume.management
? url: plug/resume/list.do
? perms: plug:resume:list
? ops:
??? - create@plug:resume:create
??? - copy@plug:resume:copy
??? - edit@plug:resume:edit
??? - save@plug:resume:save
??? - update@plug:resume:update
??? - delete@plug:resume:delete
600-1100:
? name:weixinMenu.management
? url: plug/weixin_menu/list.do
? perms: plug:weixin_menu:list
? ops:
??? - save@plug:weixin_menu:save
600-1000?菜單序號梧奢。此處為兩級狱掂,所以是二級菜單,其中600是一級菜單的序號亲轨,代表著二級菜單是屬于序號為600的一級菜單之下趋惨。1000是二級菜單的序號,決定二級菜單排列的位置惦蚊,這個序號在所屬的一級菜單里必須是唯一的器虾。注意:對應(yīng)的一級菜單必須存在。
name: resume.management?菜單名稱蹦锋。與一級菜單相同兆沙。
url: plug/resume/list.do?菜單URL地址。點擊這個菜單所訪問的url莉掂。這是一個相對路徑葛圃,以/cmscp/為基礎(chǔ)路徑,所以這個地址實際上是/cmscp/plug/resume/list.do憎妙。這個url地址必須要有相應(yīng)Controller库正,否則點擊這個菜單會找不到頁面。如@RequestMapping("/plug/resume")和@RequestMapping("list.do")厘唾。
perms: plug:resume:list?菜單權(quán)限值诀诊。這個權(quán)限值必須要對應(yīng)url的Controller方法的@RequiresPermissions("plug:resume:list")一致。
ops:?按鈕權(quán)限列表阅嘶。一個模塊除了點擊菜單的權(quán)限,還有其他權(quán)限,比如點擊簡歷管理這個菜單鏈接后讯柔,里面還有新增簡歷抡蛙、修改簡歷、刪除簡歷等功能按鈕魂迄。
create@plug:resume:create?按鈕權(quán)限值粗截。create是國際化代碼。plug:resume:create是權(quán)限值捣炬,必須與Controller方法中的@RequiresPermissions("plug:resume:create")一致熊昌。也可以直接用中文,如新增@plug:resume:create湿酸。
對應(yīng)的Java代碼
packagecom.jspxcms.plug.web.back;
@Controller
@RequestMapping("/plug/resume")
public class ResumeController {
??? @RequiresPermissions("plug:resume:list")
??? @RequestMapping("list.do")
??? public String list(...) {
??????? ...
??????? return "plug/resume/resume_list";
??? }
??? @RequiresPermissions("plug:resume:create")
??? @RequestMapping("create.do")
??? public String create(...) {
??????? ...
??????? return "plug/resume/resume_form";
??? }
??? ...
}
菜單配置與權(quán)限管理
按照上面步驟配置菜單之后婿屹,角色管理的功能權(quán)限樹會讀取配置文件,無需修改角色管理頁面及代碼推溃。
V4-Controller
在Jspxcms中昂利,Controller分為前臺和后臺。前臺是普通用戶瀏覽的頁面铁坎,使用freemarker作為視圖蜂奸,通常不需要登錄,比如網(wǎng)站首頁硬萍、欄目頁扩所、專題頁、搜索頁等朴乖;后臺一般為管理功能祖屏,使用JSP作為視圖,需要管理員登錄后臺并且有相應(yīng)權(quán)限寒砖,才能訪問赐劣。
后臺Controller配置
/src/main/resources/conf/plugin/plug/backend.xml
符合classpath:conf/**/backend*.xml這個規(guī)則的文件會加載為后臺Controller的配置文件。
<context:component-scan base-package="com.jspxcms.plug.web.back" use-default-filters="false">
? <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
? <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
context:component-scan>
這個配置會自動加載com.jspxcms.plug.web.back包下所有帶有Controller或ControllerAdvice注解的類哩都。
后臺Controller返回JSP頁面魁兼,JSP路徑的前后綴分別為/WEB-INF/views/、.jsp漠嵌,相應(yīng)的配置文件是/src/main/resources/application.properties咐汞。
# 后臺JSP地址前綴
spring.mvc.view.prefix=/WEB-INF/views/
# 后臺JSP地址后綴
spring.mvc.view.suffix=.jsp
后臺Controller類
后臺訪問地址以/cmscp為根路徑,@RequestMapping("/plug/resume")和@RequestMapping("list.do")配置的最終訪問地址為/cmscp/plug/resume/list.do儒鹿。
/cmscp路徑由com.jspxcms.core.Application中的new
ServletRegistrationBean(backendDispatcherServlet(), "/cmscp/*");代碼設(shè)定化撕。
后臺Controller返回JSP頁面,如plug/resume/resume_list约炎,加上前后綴植阴,實際文件地址為/WEB-INF/views/plug/resume/resume_list.jsp
packagecom.jspxcms.plug.web.back;
@Controller
@RequestMapping("/plug/resume")
public class ResumeController {
??? @RequiresPermissions("plug:resume:list")
??? @RequestMapping("list.do")
??? public String list(...) {
??????? ...
??????? return "plug/resume/resume_list";
??? }
??? @RequiresPermissions("plug:resume:create")
??? @RequestMapping("create.do")
??? public String create(...) {
??????? ...
??????? return "plug/resume/resume_form";
??? }
??? ...
}
前臺Controller配置
com.jspxcms.plug.ContextConfig中的@ComponentScan({“com.jspxcms.plug.web.fore”})會加載com.jspxcms.plug.web.fore包中的@Controller類.
前臺Controller返回的視圖是FreeMarker,有關(guān)FreeMarker的配置在src/main/resources/context.xml蟹瘾。
其中templateLoaderPath是模板存儲路徑,也就是模板前綴掠手,默認(rèn)為/template憾朴。
<bean id="freeMarkerViewResolver" class="com.jspxcms.common.freemarker.MyFreeMarkerViewResolver">
? <property name="contentType" value="text/html; charset=UTF-8"/>
? <property name="cacheUnresolved" value="false"/>
? <property name="redirectHttp10Compatible" value="false"/>
bean>
<bean id="freemarkerConfig" class="com.jspxcms.common.freemarker.AdapterFreeMarkerConfigurer">
? <property name="freemarkerVariables" value="#{propertiesHelper.getBeanMap('freemarkerVariables.')}"/>
? <property name="templateLoaderPath" value="${templateStorePath}"/>
? <property name="freemarkerSettings">
??? <props>
????? <prop key="tag_syntax">square_bracketprop>
????? <prop key="template_update_delay">${freemarkerConfig.template_update_delay}prop>
????? <prop key="defaultEncoding">UTF-8prop>
????? <prop key="url_escaping_charset">UTF-8prop>
????? <prop key="localized_lookup">falseprop>
????? <prop key="locale">zh_CNprop>
????? <prop key="boolean_format">true,falseprop>
????? <prop key="datetime_format">yyyy-MM-dd'T'HH:mm:ssprop>
????? <prop key="date_format">yyyy-MM-ddprop>
????? <prop key="time_format">HH:mm:ssprop>
????? <prop key="number_format">0.###prop>
????? <prop key="whitespace_stripping">trueprop>
????? <prop key="auto_import">/spring.ftl as sprop>
??? props>
? property>
bean>
前臺Controller類
前臺訪問地址直接以網(wǎng)站根路徑為相對路徑,@RequestMapping(value =
"/resume")配置的訪問地址就為/resume喷鸽。
前臺返回的FreeMarker模板路徑众雷,一般返回當(dāng)前站點的模板路徑,如/1/default/plug_resume.html做祝,加上模板前綴砾省,實際地址是/template/1/default/plug_resume.html;
packagecom.jspxcms.plug.web.fore;
@Controller
public class ResumeController {
??? public static final String TEMPLATE = "plug_resume.html";
??? @RequestMapping(value = "/resume")
??? public String form(HttpServletRequest request, org.springframework.ui.Model modelMap) {
??? ...
??? // 將通用對象放到modelMap里混槐,如ctx dy user site global等
??????? Map data = modelMap.asMap();
??????? ForeContext.setData(data, request);
??? // 獲得當(dāng)前站點對象
??????? Site site = Context.getCurrentSite();
??? // 返回當(dāng)前站點模板路徑编兄。如:/1/default/plug_resume.html。加上模板前綴纵隔,實際地址是 /template/1/default/plug_resume.html
??????? returnsite.getTemplate(TEMPLATE);
??? }
??? @RequestMapping(value = "/resume", method = RequestMethod.POST)
??? public String submit(...) {
??? ...
??? }
}
V5-Entity
配置文件
com.jspxcms.plug.ContextConfig的@EntityScan({
"com.jspxcms.plug.domain" })會自動掃描該包下含有@Entity注解的類衅谷。
數(shù)據(jù)庫表
不使用主鍵自增策略铸本,而是使用JPA的TABLE主鍵生成策略,將主鍵放到數(shù)據(jù)庫中的一個表里,這個表在Hibernate里默認(rèn)為Hibernate_sequences屯吊。所以在建表的時候不要使用主鍵自增吟榴。
create tableplug_resume
(
?? f_resume_id????????? int not null,
?? f_site_id??????????? int not null,
?? f_name?????????????? varchar(100) not null comment '姓名',
?? f_post?????????????? varchar(100) not null comment '應(yīng)聘職位',
?? f_creation_date????? datetimenot null comment '投遞日期',
?? f_gender???????????? char(1) not null default 'M' comment '性別',
?? f_birth_date???????? datetimecomment '出生日期',
?? f_mobile???????????? varchar(100) comment '手機(jī)',
?? f_email????????????? varchar(100) comment '郵箱',
?? f_expected_salary??? int comment '期望薪水',
?? f_education_experience longtextcomment '教育經(jīng)歷',
?? f_work_experience??? longtextcomment '工作經(jīng)歷',
?? f_remark???????????? longtextcomment '備注',
?? primarykey(f_resume_id)
)
engine = innodb;
alter table plug_resume comment '簡歷表';
alter table plug_resume add constraint fk_plug_resume_site foreign key(f_site_id)
????? references cms_site (f_site_id) on delete restrict on update restrict;
實體類
使用JPA的TABLE主鍵生成策略塘幅。
需注意以下三個值:name = "tg_plug_resume",
pkColumnValue = "plug_resume" generator = "tg_plug_resume"骤竹,其中plug_resume為表名,如果表名為abc俄认,則這三個值分別為name =
"tg_abc", pkColumnValue = "abc" generator =
"tg_abc"个少。
initialValue = 1代表主鍵從1開始。allocationSize
= 10代表hibernate一次獲取10個主鍵值眯杏,如果沒有用完系統(tǒng)就重啟了夜焦,那么在數(shù)據(jù)庫中會出現(xiàn)主鍵不連續(xù)的情況。但由于獲取主鍵值要查詢并修改數(shù)據(jù)庫岂贩,對于頻繁插入數(shù)據(jù)的表來說茫经,是一個很大的開銷,所以可以根據(jù)情況適當(dāng)調(diào)整這個值萎津。
如果使用MySQL的主鍵自增卸伞,除了在表主鍵里增加主鍵自增屬性,在Entity里的ID注解也要改為@GeneratedValue(
generation = IDENTITY )或@GeneratedValue(
generation = AUTO )锉屈。
packagecom.jspxcms.plug.domain;
@Entity
@Table(name = "plug_resume")
public class Resume implements java.io.Serializable {
??? privateInteger id;
??? ……
? @Id
??? @Column(name = "f_resume_id", unique = true, nullable = false)
??? @TableGenerator(name = "tg_plug_resume", pkColumnValue = "plug_resume", initialValue = 1, allocationSize = 10)
??? @GeneratedValue(strategy = GenerationType.TABLE, generator = "tg_plug_resume")
??? public Integer getId() {
??????? return this.id;
??? }
??? public void setId(Integer id) {
??????? this.id = id;
??? }
??? ……
}
V6-DAO
配置文件
系統(tǒng)的DAO在com.jspxcms.core.ContextConfig的@EnableJpaRepositories(basePackages
= {"com.jspxcms.core.repository",
"com.jspxcms.ext.repository"}, repositoryFactoryBeanClass =
MyJpaRepositoryFactoryBean.class)配置荤傲。
本例的DAO在com.jspxcms.plug.ContextConfig的@EnableJpaRepositories(basePackages
= { "com.jspxcms.plug.repository" }, repositoryFactoryBeanClass =
MyJpaRepositoryFactoryBean.class)配置
DAO類
packagecom.jspxcms.plug.repository;
public interface ResumeDao extends Repository<Resume, Integer>, ResumeDaoPlus {
??? public Page findAll(Specification spec, Pageable pageable);
??? public List findAll(Specification spec, Limitable limitable);
??? public Resume findOne(Integer id);
??? public Resume save(Resume bean);
??? public void delete(Resume bean);
??? @Modifying
??? @Query("delete from Resume bean where bean.site.id in (?1)")
??? public int deleteBySiteId(Collection siteIds);
}
ResumeDao接口中的方法不用實現(xiàn)。以下接口中的方法均可放到ResumeDao颈渊,且無需實現(xiàn):
org.springframework.data.repository.CrudRepository
org.springframework.data.repository.PagingAndSortingRepository
org.springframework.data.jpa.repository.JpaRepository
com.jspxcms.common.orm.MyJpaRepository
其中public
int deleteBySiteId(Collection siteIds);方法并不在這些接口中遂黍,則需要使用@Query("delete
from Resume bean where bean.site.id in (?1)")指定SQL語句终佛,由于該SQL會修改數(shù)據(jù)庫的數(shù)據(jù),所以要加上@Modifying妓湘。
需要實現(xiàn)的dao方法查蓉,放到ResumeDaoPlus接口中。規(guī)則是在DAO類名后加上Plus榜贴。如DAO類名是AbcDao,則Plus的類名為AbcDaoPlus妹田。
packagecom.jspxcms.plug.repository;
public interface ResumeDaoPlus {
??? public List getList(Integer[] siteId, Limitable limitable);
}
ResumeDaoPlus接口的實現(xiàn)類要放到對應(yīng)的impl包中
packagecom.jspxcms.plug.repository.impl;
public class ResumeDaoImpl implements ResumeDaoPlus {
??? @SuppressWarnings("unchecked")
??? public List getList(Integer[] siteId, Limitable limitable) {
??????? JpqlBuilder jpql =newJpqlBuilder();
??????? jpql.append("from Resume bean where 1=1");
??????? if(ArrayUtils.isNotEmpty(siteId)) {
??????????? jpql.append(" and bean.site.id in (:siteId)");
??????????? jpql.setParameter("siteId", Arrays.asList(siteId));
??????? }
??????? returnjpql.list(em, limitable);
??? }
??? privateEntityManager em;
??? @PersistenceContext
??? public void setEm(EntityManager em) {
??????? this.em = em;
??? }
}
V7-Service
Service用于處理業(yè)務(wù)邏輯和調(diào)用DAO操作數(shù)據(jù)庫唬党。
配置文件
系統(tǒng)的Service在com.jspxcms.core.ContextConfig的@ComponentScan({"com.jspxcms.core.service.impl",
"com.jspxcms.ext.service.impl"})配置。
本例的Service在com.jspxcms.plug.ContextConfig的@ComponentScan({
"com.jspxcms.plug.service.impl"})配置鬼佣。
Service類
package com.jspxcms.plug.service.impl;
@Service
@Transactional(readOnly = true)
public class ResumeServiceImpl implements ResumeService{
??? public Page findAll(Integer siteId, Map params,
??????????? Pageable pageable) {
??????? return dao.findAll(spec(siteId, params), pageable);
??? }
??? public RowSide findSide(Integer siteId,Map params,
??? Resume bean, Integer position, Sort sort) {
??????? if (position == null) {
??????????? return newRowSide();
??????? }
??????? Limitable limit = RowSide.limitable(position, sort);
??????? List list = dao.findAll(spec(siteId,params), limit);
??????? returnRowSide.create(list, bean);
??? }
??? private Specification spec(final Integer siteId,
??????????? Mapparams) {
??????? Collection filters = SearchFilter.parse(params).values();
??????? final Specification fsp = SearchFilter.spec(filters, Resume.class);
??????? Specification sp =newSpecification() {
??????????? public Predicate toPredicate(Root root,
??????????????????? CriteriaQuery query, CriteriaBuilder cb) {
??????????????? Predicate pred = fsp.toPredicate(root, query, cb);
??????????????? if (siteId != null) {
??????????????????? pred = cb.and(pred, cb.equal(root.get("site")
??????????????????????????? .get("id"), siteId));
??????????????? }
??????????????? returnpred;
??????????? }
??????? };
??????? returnsp;
??? }
??? privateResumeDao dao;
??? @Autowired
??? public void setDao(ResumeDao dao) {
??????? this.dao = dao;
??? }
}
該類使用到JPA的Specification查詢方式驶拱。可實現(xiàn)后臺列表點擊表頭晶衷,按任意列排序蓝纲;列表頁按任意字段查詢;編輯頁面上一條晌纫、下一條功能税迷。
V8- FreeMarker標(biāo)簽
在前臺模板使用FreeMarker標(biāo)簽獲取數(shù)據(jù)。
標(biāo)簽類package com.jspxcms.plug.web.directive;
/**
* FreeMarker標(biāo)簽類需實現(xiàn)TemplateDirectiveModel接口
*/
public class ResumeListDirective implements TemplateDirectiveModel{
??? public static final String SITE_ID = "siteId";
??? public void execute(Environment env, Map params, TemplateModel[] loopVars,
??????????????????????? TemplateDirectiveBody body) throws TemplateException, IOException {
??????? // 使用標(biāo)簽時锹漱,返回變量必須存在箭养,如[@ResumeList; result]...[/@ResumeList]中分號后的result。
??????? if (loopVars.length < 1) {
??????????? throw new TemplateModelException("Loop variable is required.");
??????? }
??????? // 標(biāo)簽體必須存在哥牍,即[@ResumeList; list]...[/@ResumeList]中間的部分毕泌。
??????? if (body == null) {
??????????? throw new RuntimeException("missing body");
??????? }
??????? // 獲取標(biāo)簽參數(shù),如[@ResumeList siteId='123'; list]...[/@ResumeList]中的123嗅辣。
??????? Integer[] siteId = Freemarkers.getIntegers(params, SITE_ID);
??????? if (siteId == null && params.get(SITE_ID) == null) {
??????????? // 如果沒有傳入siteId這個參數(shù)撼泛,則獲取當(dāng)前站點的ID。
??????????? siteId =newInteger[]{ForeContext.getSiteId(env)};
??????? }
??????? Sort defSort =new Sort(Direction.DESC, "creationDate", "id");
??????? Limitable limitable = Freemarkers.getLimitable(params, defSort);
??????? List list = service.findList(siteId, limitable);
??????? // 將獲取的數(shù)據(jù)放到返回變量里澡谭。
??????? loopVars[0] = env.getObjectWrapper().wrap(list);
??????? // 執(zhí)行標(biāo)簽體愿题。
??????? body.render(env.getOut());
??? }
??? @Autowired
??? privateResumeService service;
}
配置文件
FreeMarker標(biāo)簽類需要在/src/main/resources/conf/plugin/plug/context.xml文件中聲明
id="PlugResumeList" class="com.jspxcms.plug.web.directive.ResumeListDirective"
/>
然后在/src/main/resources/conf/plugin/plug/conf.properties文件中加入freemarkerVariables.ResumeList=PlugResumeList。在模板中調(diào)用標(biāo)簽的名稱是ResumeList译暂,而非PlugResumeList抠忘。
標(biāo)簽的使用
定義了標(biāo)簽后,在任意的前臺模板中都可以使用這個標(biāo)簽外永,如:
[@ResumeList; result]
[#list result as bean]
??? ${bean.name}, ${bean.mobile}
[/#list]
[/@ResumeList]
V9-國際化
在/src/main/resources/conf/plugin/plug/conf.properties中指定國際化文件位置:
messageSource.basenames.plug=classpath:messages/plugin/plug/plug
messageSource.basenames.plugfore=classpath:messages/plugin/plugfore/plugfore
系統(tǒng)中其他的國際化文件在/src/main/resources/conf/conf.properties文件中指定:
messageSource.basenames.common=classpath:messages/common/common
messageSource.basenames.core=classpath:messages/core/core
messageSource.basenames.corefore=classpath:messages/corefore/corefore
messageSource.basenames.ext=classpath:messages/ext/ext
messageSource.basenames.extfore=classpath:messages/extfore/extfore
這些配置會在/src/main/resources/conf/context.xml文件中加載:
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
??????? value="${messageSource.cacheSeconds}"/>
??????? value="#{propertiesHelper.getList('messageSource.basenames.')}"/>
??????? value="false"/>
??????? value="false"/>
??????? value="UTF-8"/>
V10-定時任務(wù)開發(fā)
系統(tǒng)中有定時任務(wù)功能崎脉,里面有一些系統(tǒng)已經(jīng)定義好的任務(wù)類型。如果系統(tǒng)自帶的任務(wù)類型里沒有自己需要的伯顶,可以開發(fā)一個任務(wù)類型囚灼。
本著無侵入的二次開發(fā)設(shè)計思想骆膝,開發(fā)一個自己的任務(wù)類型也可以做到不修改系統(tǒng)原有代碼和文件。
定時任務(wù)配置文件
/src/main/resources/conf/conf.properties
相關(guān)配置內(nèi)容:
scheduleJob.100=com.jspxcms.core.quartz.InfoPublishJob
scheduleJobPath.com.jspxcms.core.quartz.InfoPublishJob=
scheduleJob.200=com.jspxcms.core.quartz.HtmlHomeJob
scheduleJobPath.com.jspxcms.core.quartz.HtmlHomeJob=
scheduleJob.300=com.jspxcms.ext.quartz.CollectJob
scheduleJobPath.com.jspxcms.ext.quartz.CollectJob=/cmscp/ext/collect/schedule_job.do
自定義的定時任務(wù)類型的配置也可以寫在其它的conf.properties文件中灶体,如/src/main/resources/conf/plugin/plug/conf.properties阅签。
定時任務(wù)類型序號
scheduleJob.300:序號300決定這個類型的排序,即在選擇任務(wù)類型時的前后順序蝎抽。序號不能重復(fù)政钟。
定時任務(wù)名稱
com.jspxcms.ext.quartz.CollectJob既是定時任務(wù)實現(xiàn)類,又是定時任務(wù)類型名稱樟结。需要在國際化文件中定義相應(yīng)的國際化名稱养交。/src/main/resources/messages/ext/ext.properties
scheduleJob.code.com.jspxcms.ext.quartz.CollectJob=采集
需以scheduleJob.code.開頭。國際化名稱也可以寫在其他文件中瓢宦,如/src/main/resources/messages/plugin/plug/plug.properties碎连。
定時任務(wù)實現(xiàn)類
com.jspxcms.ext.quartz.CollectJob是定時任務(wù)實現(xiàn)類。
publicclassCollectJobimplementsJob{
??? privatestaticfinalLogger logger = LoggerFactory
??????????? .getLogger(CollectJob.class);
??? publicstaticfinalString COLLECT_ID ="collectId";
??? publicvoidexecute(JobExecutionContextcontext)
??????????? throwsJobExecutionException{
??????? try{
??????????? ApplicationContext appContext =(ApplicationContext) context
???????????????????.getScheduler().getContext().get(Constants.APP_CONTEXT);
??????????? Collector collector =appContext.getBean(Collector.class);
??????????? JobDataMap map =context.getJobDetail().getJobDataMap();
??????????? Integer collectId =map.getIntegerFromString(COLLECT_ID);
??????????? collector.start(collectId);
????? ??????System.out.println("collect
ok");
??????????? logger.info("run
collect job: "+ collectId);
??????? }catch(SchedulerException e) {
??????????? thrownewJobExecutionException("Cannot get ApplicationContext", e);
??????? }
??? }
}
需要實現(xiàn)org.quartz.Job接口驮履,在public
void execute(JobExecutionContext context)方法中編寫任務(wù)需要執(zhí)行的代碼鱼辙。
ApplicationContext appContext = (ApplicationContext)
context.getScheduler().getContext().get(Constants.APP_CONTEXT);可以獲取Spring的ApplicationContext,通過ApplicationContext可以獲取到Spring管理的對象玫镐,如Collector
collector =?appContext.getBean(Collector.class);倒戏。注意:這個類中必須使用這種方法獲取Spring管理的對象,不能使用@Autowired等其他方式摘悴。
Integer collectId =
map.getIntegerFromString(COLLECT_ID);可以獲取額外的參數(shù)峭梳。
額外的參數(shù)
scheduleJobPath.com.jspxcms.core.quartz.InfoPublishJob=如定時任務(wù)無需額外參數(shù),則等號后面留空蹂喻。
定時任務(wù)有時需要傳遞外的參數(shù)葱椭,比如采集定時任務(wù)需要選擇執(zhí)行哪個采集數(shù)據(jù)源。此時需要在定時任務(wù)新增/修改界面增加相應(yīng)的錄入項口四。
scheduleJobPath.com.jspxcms.ext.quartz.CollectJob=/cmscp/ext/collect/schedule_job.do
錄入界面的Controller
編寫一個獲取錄入界面的地址:/cmscp/ext/collect/schedule_job.do孵运,這里使用相對路徑,相對于定時任務(wù)新增界面的地址蔓彩。
此例中治笨,這個地址的實現(xiàn)類是com.jspxcms.ext.web.back.CollectController。
@Controller
@RequestMapping("/ext/collect")
public class CollectController {
...
??? @RequestMapping("schedule_job.do")
??? public String scheduleJob(HttpServletRequest request, org.springframework.ui.Model modelMap) {
??????? Integer siteId = Context.getCurrentSiteId();
??????? List collectList = service.findList(siteId);
??????? modelMap.addAttribute("collectList", collectList);
???????modelMap.addAttribute("includePage", "../../ext/collect/collect_job.jsp");
??????? return "core/schedule_job/schedule_job_form";
??? }
...
}
modelMap.addAttribute("collectList",
? ? collectList);傳遞數(shù)據(jù)赤嚼。
modelMap.addAttribute("includePage","../../ext/collect/collect_job.jsp");傳遞錄入界面旷赖。
錄入界面的JSP
根據(jù)Controller中傳遞的includePage的值,對應(yīng)JSP頁面為:/WEB-INF/views/ext/collect/collect_job.jsp更卒。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="f" uri="http://www.jspxcms.com/tags/form"%>
? <tr>
??? <td class="in-lab" width="15%"><s:message code="scheduleJob.collectSource"/>:td>
??? <td class="in-ctt" width="85%" colspan="3">
????? <select name="data_collectId">
??????? <c:forEach var="collect" items="${collectList}">
??????? <f:option value="${collect.id}" selected="${dataMap['collectId']}">${collect.name}f:option>
??????? c:forEach>
????? select>
??? td>
? tr>
? ? name="data_collectId">:此處的data_collectId對應(yīng)定時任務(wù)實現(xiàn)類CollectJob中獲取參數(shù)的方法Integer collectId =
? ? map.getIntegerFromString("collectId");
? ? items="${collectList}">:此處的${collectList}對應(yīng)CollectController的modelMap.addAttribute("collectList",
? ? collectList);。
V11-安全框架Shiro
原理簡述
安全框架需要處理的事情:
訪問受保護(hù)頁面必須有權(quán)限果录。如果沒有登錄返弹,則自動重定向至登錄頁面悦施;如果登錄了土陪,沒有權(quán)限則顯示沒有權(quán)限源哩。
登錄的時候需要校驗密碼和用戶狀態(tài)(被鎖定的用戶不能登錄)坛掠,并獲取用戶的權(quán)限信息友多,以判斷用戶是否有權(quán)訪問該頁面。
在頁面里要能判斷用戶是否具有訪問某一頁面的權(quán)限,以便控制是否顯示該功能。
Shiro使用Servlet Filter過濾器保護(hù)受訪的頁面,通過下面介紹的shiroFilterChainDefinitionMap配置需要保護(hù)的頁面路徑。
使用AuthorizingRealm獲取用戶密碼及權(quán)限信息,即下面介紹的com.jspxcms.core.security.ShiroDbRealm。
在JSP頁面中使用標(biāo)簽
name="my:perm:code">判斷是否有訪問Controller中@RequiresPermissions("my:perm:code")標(biāo)識的方法譬重。
配置及源代碼
配置類com.jspxcms.core.ShiroConfig(7.0及之前版本/src/main/resources/conf/context-shiro.xml)
權(quán)限相關(guān)的類包:com.jspxcms.core.security
加密相關(guān)的公用類包:com.jspxcms.common.security
核心類:
com.jspxcms.core.security.CmsAuthenticationFilter?登錄邏輯處理類以现。包括加入驗證碼判斷、記錄登錄日志的邏輯。
com.jspxcms.core.security.ShiroDbRealm?登錄時查詢用戶名技竟、密碼及獲取用戶權(quán)限信息。
過濾器映射配置
ShiroConfig會讀取過濾器映射配置联逻。
@Bean("shiroFilter")
@DependsOn("propertiesHelper")
public ShiroFilterFactoryBean shiroFilterFactoryBean(BeanFactory beanFactory) throws IOException {
? ShiroFilterFactoryBean factoryBean =newShiroFilterFactoryBean();
? ...
? Map filterChainDefinitionMap = propertiesHelper()
????? .getSortedMap("shiroFilterChainDefinitionMap.");
? factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
? ...
}
過濾器映射配置:/src/main/resources/conf/conf.properties
shiroFilterChainDefinitionMap[100]/login=authc
shiroFilterChainDefinitionMap[200]/logout=logout
shiroFilterChainDefinitionMap[300]/cmscp=backSite,anon
shiroFilterChainDefinitionMap[400]/cmscp/=backSite,anon
shiroFilterChainDefinitionMap[500]/cmscp/index.do=backSite,anon
shiroFilterChainDefinitionMap[600]/cmscp/login.do=backSite,authc
shiroFilterChainDefinitionMap[700]/cmscp/logout.do=backSite,logout
shiroFilterChainDefinitionMap[800]/cmscp/**=backSite,user
shiroFilterChainDefinitionMap[900]/my/**=user
shiroFilterChainDefinitionMap[1000]/**=anon
大致描述如下:
/my/**?/cmscp/**?路徑需要登錄后才能訪問搓扯,如未登錄則會重定向至登錄頁面。前者為是前臺會員中心路徑包归,后者為后臺管理路徑锨推。
/login?/cmscp/login.do?是登錄請求。前者為前臺登錄請求公壤,后者為后臺登錄請求爱态。
/logout?/cmscp/logout.do?是退出登錄請求。
/**?其他路徑可以隨便訪問境钟。
密碼加密
將用戶密碼直接使用明文保存在數(shù)據(jù)庫中是極其不安全的。要對密碼進(jìn)行加密后俭识,再保存到數(shù)據(jù)庫慨削,通常的加密方式有md5?sha1?sha256等,md5使用的最為廣泛套媚,但由于安全性較差缚态,已經(jīng)不建議使用。系統(tǒng)中使用sha1作為加密方式堤瘤。
ShiroConfig中的配置如下:
@Bean("credentialsDigest")
public SHA1CredentialsDigest credentialsDigest() {
? return newSHA1CredentialsDigest();
}
這個加密對象會在com.jspxcms.core.security.ShiroDbRealm中注入:
@Autowired
public void setCredentialsDigest(CredentialsDigest credentialsDigest) {
??? this.credentialsDigest = credentialsDigest;
}
V12-頁面動態(tài)查詢
系統(tǒng)后臺的列表頁通常都有查詢功能玫芦,如果通過寫sql語句進(jìn)行查詢非常費時,特別是在查詢條件較多的情況下本辐。如果查詢條件發(fā)生變化桥帆,則需要修改sql語句,非常麻煩慎皱。
jspxcms使用頁面動態(tài)查詢來解決這個問題老虫,直接解析查詢表單的名稱來生成sql查詢條件,只要修改頁面就可以實現(xiàn)查詢茫多,而不需要修改java代碼和寫sql語句祈匙。
/WEB-INF/views/plug/resume/resume_list.jsp(已做簡化):
??? 名稱:
??? 職位:
??? 開始日期:
??? 結(jié)束日期:
??? 搜索
?
com.jspxcms.plug.domain.Resume;
public class Resume {
??? privateInteger id;
??? privateSite site;
??? privateString name;
??? privateString post;
??? privateDate creationDate;
??? privateString gender;
??? privateDate birthDate;
??? privateString mobile;
??? privateString email;
??? ...
}
其中
search_CONTAIN_name?search_CONTAIN_post?search_GTE_creationDate_Date?search_LTE_creationDate_Date 是關(guān)鍵內(nèi)容。
search_?是前綴,代表這個輸入框用于構(gòu)建搜索條件夺欲。
CONTAIN?GTE?LTE?是查詢關(guān)鍵字跪帝。相當(dāng)于?like?>=?<=。
name?post?creationDate?是查詢的字段些阅。是com.jspxcms.plug.domain.Resume實體類中的屬性名伞剑。
_Date?是類型后綴,默認(rèn)是字符串扑眉,所以字符串不需要類型后綴纸泄;creationDate是日期,所以要加上_Date后綴腰素。
最后的效果類似?where name like '%abc%' and post like '%def%' andcreationDate >= 'xxxx-xx-xx' and creationDate <= 'xxxx-xx-xx'
保留頁面查詢條件值
點擊查詢按鈕后聘裁,希望把之前的查詢條件值依然保留在頁面上。
value="${search_CONTAIN_name[0]}"的作用是將查詢條件反填至頁面弓千,也就是點擊查詢按鈕得到查詢結(jié)果后衡便,查詢條件值依然顯示在頁面上,而不會變成空白
如果name里面的值帶有.號洋访,則必須使用這種格式:
name="search_CONTAIN_detail.title"value="${requestScope['search_CONTAIN_detail.title'][0]}"
表連接
上面的例子是只查詢當(dāng)前實體類的字段镣陕。
查詢many-to-one或one-to-one的關(guān)聯(lián)實體類的字段用.分隔。比如search_CONTAIN_site.name姻政。
查詢many-to-many或one-to-many的關(guān)聯(lián)實體類的字段需加上J前綴呆抑。比如Info查詢中的search_CONTAIN_JinfoSpecials.Jspecial.title?search_CONTAIN_JinfoTags.Jtag.name
查詢條件
EQ :代表sql中的?=。
LIKE : 代表sql中的?like汁展。
CONTAIN : 代表sql中的?like鹊碍,且會在查詢內(nèi)容前后加上通配符,如?like
? ? '%abc%'食绿。
STARTWITH : 代表sql中的?like侈咕,且會在查詢內(nèi)容后加上通配符,如?like 'abc%'器紧。
ENDWITH : 代表sql中的?like耀销,且會在查詢內(nèi)容后前上通配符,如?like '%abc'铲汪。
GT : 代表sql中的?>熊尉。
LT : 代表sql中的?<。
GTE : 代表sql中的?>=桥状。
LTE : 代表sql中的?<=帽揪。
IN : 代表sql中的?in
字段類型后綴
String:默認(rèn)類型,不指定類型則默認(rèn)為該類型辅斟。
Integer
Long
Float
Double
BigDecimal
BigInteger
Boolean
Date
Timestamp
Controller
packagecom.jspxcms.plug.web.back;
public class ResumeController {
??? @RequiresPermissions("plug:resume:list")
??? @RequestMapping("list.do")
??? public String list(@PageableDefault(sort = "id", direction = Direction.DESC)Pageable pageable,
?????????????????????? HttpServletRequest request, org.springframework.ui.Model modelMap){
??????? Integer siteId = Context.getCurrentSiteId();
??????? // 獲取`search_`開頭的查詢字段
??????? Map params = Servlets.getParamValuesMap(request, Constants.SEARCH_PREFIX);
??????? Page pagedList = service.findAll(siteId, params, pageable);
??????? modelMap.addAttribute("pagedList", pagedList);
??????? return "plug/resume/resume_list";
??? }
在Controller中需要獲取search_開頭的查詢參數(shù):Map params = Servlets.getParamValuesMap(request,Constants.SEARCH_PREFIX);
Service
packagecom.jspxcms.plug.service.impl;
public class ResumeServiceImpl implements ResumeService, SiteDeleteListener {
??? public Page findAll(Integer siteId, Map params, Pageable pageable) {
??????? returndao.findAll(spec(siteId, params), pageable);
??? }
??? private Specification spec(final Integer siteId, Map params) {
??????? // 解析頁面?zhèn)鬟f過來的params參數(shù)
??????? Collection filters = SearchFilter.parse(params).values();
??????? finalSpecification fsp = SearchFilter.spec(filters, Resume.class);
??????? Specification sp =newSpecification() {
??????????? public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
??????????????? // 此處可以增加額外查詢條件转晰。屬于JPA的用法,具體請查看JPA文檔。
??????????????? Predicate pred = fsp.toPredicate(root, query, cb);
??????????????? if (siteId != null) {
??????????????????? pred = cb.and(pred, cb.equal(root.get("site").get("id"), siteId));
? ??????????????}
??????????????? returnpred;
??????????? }
??????? };
??????? returnsp;
??? }
}
查詢解析類:?com.jspxcms.common.orm.SearchFilter查邢。
V13-開發(fā)環(huán)境下部署的方法和原理
Tomcat部署目錄
Tomcat部署非常簡單蔗崎,直接把程序復(fù)制到{Tomcat安裝目錄}/webapps目錄下即可。該目錄下會有很多默認(rèn)的文件夾扰藕,如docs?examples?host-manager?manager?ROOT缓苛,這些都是Tomcat的文檔、演示以及管理平臺邓深,實際使用中都不需要未桥,如果配置不當(dāng)甚至還會有安全隱患。所以在部署前芥备,要先刪除這些文件夾冬耿。
webapps下有一個特殊的文件夾ROOT,放在該目錄下會作為Tomcat訪問的根目錄萌壳,比如/webapps/ROOT/index.html的訪問地址為http://localhost:8080/index.html亦镶。而/webapps/abc/index.html的訪問地址則為http://localhost:8080/abc/index.html。因此大部分情況下袱瓮,都是部署在ROOT目錄缤骨。
通過修改tomcat的server.xml文件可以改變tomcat的部署位置,但如果不是非常精通Tomcat尺借,我們不建議你這么做绊起。特別是在部署碰到錯誤時,更應(yīng)該放棄這種做法燎斩。我們碰到過很多因為修改tomcat配置導(dǎo)致錯誤的案例勒庄。
maven目錄結(jié)構(gòu)與部署的關(guān)系
/src/main/java/?源碼目錄。編譯后會保存在/target/classes/目錄瘫里。該目錄不用直接參與部署。
/src/main/resources/?資源目錄荡碾。編譯后會也會保存在/target/classes/目錄谨读。該目錄不用直接參與部署。
/src/main/wabapp/?web程序的頁面坛吁、圖片劳殖、css、js等文件拨脉。該目錄的文件可以需要部署在{tomcat}/webapps/ROOT/目錄下哆姻。如/src/main/webapp/index.html則部署到{tomcat}/webapps/ROOT/index.html。
/target/classes/?編譯后會自動生成該目錄玫膀,包含/src/main/java/和/src/main/resources/中的內(nèi)容矛缨。該目錄下的文件需要部署在{tomcat}/webapps/ROOT/WEB-INF/classes/目錄下。
綜上所述,只要將/src/main/wabapp/和/target/classes/復(fù)制到tomcat相應(yīng)目錄即可箕昭,但還缺少依賴包灵妨,即{tomcat}/webapps/ROOT/WEB-INF/lib/目錄下的jar包。
maven打包
使用mvn package或者mav install可以得到完整的war包落竹。直接在命令行使用maven命令泌霍,或者使用eclipse、idea等開發(fā)工具中相應(yīng)的功能述召,都可以實現(xiàn)打包朱转。
打包后在/target目錄下會出現(xiàn)類似jspxcms-9.0.0.war的文件。war文件和zip文件格式是一樣的积暖,可以使用解壓軟件打開藤为。解壓后得到的文件就是上述的/src/main/wabapp/、/target/classes/和依賴包/WEB-INF/lib/組成的呀酸。
war部署
war文件可以直接部署到{Tomcat安裝目錄}/webapps/運行tomcat后會自動解壓凉蜂,解壓的目錄和war文件名是一樣的,比如{Tomcat安裝目錄}/webapps/jspxcms-9.0.0.war會解壓成{Tomcat安裝目錄}/webapps/jspxcms-9.0.0性誉。通過上面的知識可以知道窿吩,要訪問到這個程序,訪問地址應(yīng)該是http://localhost:8080/jspxcms-9.0.0/错览,而這個訪問地址http://localhost:8080/是無法訪問的纫雁。這顯然不是我們想要的。這時需要將jspxcms-9.0.0.war重命名為ROOT.war倾哺,解壓后成{Tomcat安裝目錄}/webapps/ROOT/轧邪,這樣就可以通過http://localhost:8080/訪問了。
有一點需要注意羞海,如果一個目錄已經(jīng)存在{Tomcat安裝目錄}/webapps/ROOT/忌愚,只替換ROOT.war包,這樣不會重新解壓却邓。需要將ROOT目錄刪除再替換ROOT.war文件才會重新解壓硕糊。
該文章內(nèi)容對官網(wǎng)內(nèi)容進(jìn)行整合,如果有不理解的請移步官網(wǎng):http://www.jspxcms.com/documentation/