这篇文章主要介绍了Jspxcms无侵入式二次开发的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Jspxcms无侵入式二次开发的方法是什么文章都会有所收获,下面我们一起来看看吧。
作为通用的网站内容管理系统,只能尽可能的满足大部分需求,却很难满足所有的需求,这时就需要对系统进行二次开发。对于一些可以公用的二次开发程序可以做成插件,提供给其他开发者或客户使用。
/WEB-INF/classes/conf/plugin
二次开发的核心就是配置文件,通过配置文件将不同模块和插件整合到一起。所有的配置文件都必须在这个目录,在该目录下的配置文件会自动加载。
在该目录下新建自己的文件夹,可以是任何名字,如:abc、novel等,本例为plug。即/WEB-INF/classes/conf/plugin/plug。
application.properties:功能菜单、权限、国际化、Entity都与这个文件相关。
context-dao.xml:Dao的加载。
context-service.xml:Service的加载。
controller-back.xml:后台Controller的加载。
controller-fore.xml:前台Controller的加载。
context-directive.xml:标签的加载。
源代码可以放在任何包下,只要与配置文件中设置相对应即可。
本例为:com.jspxcms.plug。
后台JSP文件的根目录为:/WEB-INF/views。
详细的路径取决于Controller返回的地址,如返回plug/resume/resume_list,则对应的JSP文件为:/WEB-INF/views/plug/resume/resume_list.jsp。
本例的后台JSP所在目录:/WEB-INF/views/plug/resume
前台模版使用freemarker技术,模版根目录为:/template/1/default。
其中1是站点ID。default是模版主题,后台“系统-网站设置-基本设置”可以修改本项设置(前提是/template/1目录下有多个文件夹)。
本例的前台模版是:/template/1/default/plug_resume.html。该地址取决于Controller返回地址。
编辑和查看国际化文件,请安装Eclipse的Properties Editor插件。否则不能看到中文,只能看到\u5217\u8868之类的代码;编辑时直接输入中文,则会出现乱码。
国际化文件根目录为:/WEB-INF/messages。
本例的国际化路径为:/WEB-INF/messages/plugin。
菜单和权限是插件(二次)开发的最核心的部分,也是最难做到无缝整合的部分。大部分系统会将菜单信息保存到数据库中,这对升级和维护带来一定的困难。
Jspxcms的菜单和权限信息存放在配置中,方便管理、维护和升级;只需要在一个配置文件中设置好,即可以无缝整合系统的菜单、权限、赋权等问题,无需另外修改代码和页面。
开发时可以根据需要,将新功能菜单加到任意的一级菜单下的任意位置,也可以自己新增一级菜单,新增的一级菜单也可以放在任何你想要的位置上。
Jspxcms系统菜单分为两级。
一级菜单:
二级菜单:
配置文件为:application.properties
在本例中的完整路径:/WEB-INF/classes/conf/plugin/plug/application.properties
与菜单和权限相关的配置内容(分号为分割符):
menu.650=navigation.plug;nav.do?menuId=650;nav_plug
menu.650.100=resume.management;plug/resume/list.do;plug:resume:list;create@plug:resume:create;copy@plug:resume:copy;edit@plug:resume:edit;save@plug:resume:save;update@plug:resume:update;delete@plug:resume:delete
menu.650.200=dbBackup.management;plug/db_backup/list.do;plug:db_backup:list;dbBackup.backup@plug:db_backup:backup;dbBackup.restore@plug:db_backup:restore;delete@plug:workflow_group:delete
配置分为三部分:菜单位置及名称、菜单链接地址、权限值。
菜单位置及名称
menu.650=navigation.plug
650:是后台导航一级菜单的编号,编号大小决定菜单的前后位置。其值可以根据需要调整,如330、970,但不要与系统菜单或其他插件菜单重复。系统菜单通常为menu.100、menu.200、menu.600等,系统菜单定义文件在/WEB-INF/classes/conf/application-menu.properties。
navigation.plug:菜单名称。这里使用了国际化代码,也可以直接用中文,比如“menu.650=我的插件”。(在properties文件中直接写中文,需要安装Properties Editor插件,否则会出现乱码)
菜单链接地址
nav.do?menuId=650
其中650需与menu.650的值一样。
权限值
nav_plug
其中plug通常与配置文件目录名称一样。如目录为abc,则应为nav_abc。也可不一样,但不能与其他一级菜单权限名称一样。
配置分为四部分:菜单位置及名称、菜单链接地址、菜单权限值、功能名称及权限值。
菜单位置及名称
menu.650.100=resume.management
650:二级菜单所属的一级菜单编号。
100:二级菜单编号。其值的意义和一级菜单编号一样,用于确定二级菜单的前后位置。
resume.management:二级菜单名称。可以直接用中文,如“小说管理”。
菜单链接地址
plug/resume/list.do
需与Controller中的地址对应,否则会找不到页面。
菜单权限值
plug:resume:list
需与Controller中list方法的权限对应,否则会报没有权限。
功能名称及权限值
create@plug:resume:create;copy@plug:resume:copy;edit@plug:resume:edit;save@plug:resume:save;update@plug:resume:update;delete@plug:resume:delete
create@plug:resume:create:新增按钮的权限值。其中create是国际化代码,可以用直接用中文,如“新增@plug:resume:create”。其中plug:resume:create是该按钮的权限值,需与Controller中对应的create方法权限值一致。
edit@plug:resume:edit:意义和上面一样,后面的以此类推。
按照上面步骤设置好菜单之后,权限管理部分就会读取配置文件,无需修改权限管理页面及代码。
Controller是MVC中的控制部分,主要的功能是接收客户端提交的请求,然后调用Service的功能及获取数据,最后返回View(视图,即JSP或freemarker页面)。
在Jspxcms中,Controller分为前台和后台。前台是普通用户浏览的页面,使用freemarker作为视图,通常不需要登录,比如网站首页、栏目页、专题页、搜索页等;后台一般为管理功能,使用JSP作为视图,需要管理员登录后台,才能访问。
/WEB-INF/classes/conf/plugin/plug/controller-back.xml。
其中com.jspxcms.plug.web.back是后台Controller所在包,在这个包下的Controller都会被spring扫描到。
/WEB-INF/classes/conf/plugin/plug/controller-fore.xml。
其中com.jspxcms.plug.web.fore是前台Controller所在包,在这个包下的Controller都会被spring扫描到。
package com.jspxcms.plug.web.back;
@Controller
@RequestMapping("/plug/resume")
public class ResumeController {
@RequiresPermissions("plug:resume:list")
@RequestMapping("list.do")
public String list(
@PageableDefaults(sort = "id", sortDir = Direction.DESC) Pageable pageable,
HttpServletRequest request, org.springframework.ui.Model modelMap) {
Integer siteId = Context.getCurrentSiteId(request);
Map<String, String[]> params = Servlets.getParameterValuesMap(request,
Constants.SEARCH_PREFIX);
Page<Resume> pagedList = service.findAll(siteId, params, pageable);
modelMap.addAttribute("pagedList", pagedList);
return "plug/resume/resume_list";
}
@RequiresPermissions("plug:resume:create")
@RequestMapping("create.do")
public String create(Integer id, org.springframework.ui.Model modelMap) {
if (id != null) {
Resume bean = service.get(id);
modelMap.addAttribute("bean", bean);
}
modelMap.addAttribute(OPRT, CREATE);
return "plug/resume/resume_form";
}
@Autowired
private ResumeService service;
}
其中RequestMapping为访问地址,RequiresPermissions为权限代码,需与application.properties中的菜单权限配置相吻合。
package com.jspxcms.plug.web.fore;
@Controller
public class ResumeController {
public static final String TEMPLATE = "plug_resume.html";
@RequestMapping(value = "/resume.jspx")
public String resumeForm(Integer page, HttpServletRequest request,
org.springframework.ui.Model modelMap) {
Site site = Context.getCurrentSite(request);
Map<String, Object> data = modelMap.asMap();
ForeContext.setData(data, request);
ForeContext.setPage(data, page);
return site.getTemplate(TEMPLATE);
}
@RequestMapping(value = "/resume.jspx", method = RequestMethod.POST)
public String resumeSubmit(@Valid Resume bean, HttpServletRequest request,
HttpServletResponse response, org.springframework.ui.Model modelMap) {
Response resp = new Response(request, response, modelMap);
Site site = Context.getCurrentSite(request);
service.save(bean, site.getId());
return resp.post();
}
@Autowired
private ResumeService service;
}
/WEB-INF/classes/conf/plugin/plug/application.properties
entityManagerFactory.packagesToScan.plug=com.jspxcms.plug.domain
plug:通常与配置文件所在目录一样,也可不一样,但不能与其他相关配置同名。
com.jspxcms.plug.domain:Entity所在包名。
不使用主键自增策略,将主键放到数据库中的一个表里。
create table plug_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 '应聘职位',
f_creation_date datetime not null comment '投递日期',
f_gender char(1) not null default 'M' comment '性别',
f_birth_date datetime comment '出生日期',
f_mobile varchar(100) comment '手机',
f_email varchar(100) comment '邮箱',
f_expected_salary int comment '期望薪水',
f_education_experience longtext comment '教育经历',
f_work_experience longtext comment '工作经历',
f_remark longtext comment '备注',
primary key (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;
需注意以下三个值,其中plug_resume为表名:
name = "tg_plug_resume", pkColumnValue = "plug_resume"
generator = "tg_plug_resume"
@Entity
@Table(name = "plug_resume")
public class Resume implements java.io.Serializable {
private Integer id;
……
@Id
@Column(name = "f_resume_id", unique = true, nullable = false)
@TableGenerator(name = "tg_plug_resume", pkColumnValue = "plug_resume", table = "t_id_table", pkColumnName = "f_table", valueColumnName = "f_id_value", initialValue = 1, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "tg_plug_resume")
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
……
}
/WEB-INF/classes/conf/plugin/plug/context-dao.xml
<jpa:repositories base-package="com.jspxcms.plug.repository"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"
factory-class="com.jspxcms.common.orm.MyJpaRepositoryFactoryBean"
repository-impl-postfix="Impl">
</jpa:repositories>
其中com.jspxcms.plug.repository为dao接口所在包。
Dao使用了SpringDataJPA技术。
SpringDataJPA官网:http://projects.spring.io/spring-data-jpa/
package com.jspxcms.plug.repository;
public interface ResumeDao extends Repository<Resume, Integer>, ResumeDaoPlus {
public Page<Resume> findAll(Specification<Resume> spec, Pageable pageable);
public List<Resume> findAll(Specification<Resume> spec, Limitable limitable);
public Resume findOne(Integer id);
public Resume save(Resume bean);
public void delete(Resume bean);
}
ResumeDao接口中的方法不用实现。以下接口中的方法均可放到ResumeDao,且无需实现:
org.springframework.data.repository. CrudRepository
org.springframework.data.repository. PagingAndSortingRepository
org.springframework.data.jpa.repository. JpaRepository
com.jspxcms.common.orm. MyJpaRepository
需要实现的dao方法,放到ResumeDaoPlus接口中。
package com.jspxcms.plug.repository;
public interface ResumeDaoPlus {
public List<Resume> getList(Integer[] siteId, Limitable limitable);
}
package com.jspxcms.plug.repository.impl;
public class ResumeDaoImpl implements ResumeDaoPlus {
@SuppressWarnings("unchecked")
public List<Resume> getList(Integer[] siteId, Limitable limitable) {
JpqlBuilder jpql = new JpqlBuilder();
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));
}
return jpql.list(em, limitable);
}
private EntityManager em;
@PersistenceContext
public void setEm(EntityManager em) {
this.em = em;
}
}
其中JpqlBuilder用于拼装jqpl语句、设置参数,并可处理分页问题。
com.jspxcms.common.orm.JpqlBuilder
/WEB-INF/classes/conf/plugin/plug/context-service.xml
<context:component-scan base-package="com.jspxcms.plug.service.impl">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
其中com.jspxcms.plug.service.impl为Service的实现类所在包。
package com.jspxcms.plug.service.impl;
@Service
@Transactional(readOnly = true)
public class ResumeServiceImpl implements ResumeService {
public Page<Resume> findAll(Integer siteId, Map<String, String[]> params,
Pageable pageable) {
return dao.findAll(spec(siteId, params), pageable);
}
public RowSide<Resume> findSide(Integer siteId,
Map<String, String[]> params, Resume bean, Integer position,
Sort sort) {
if (position == null) {
return new RowSide<Resume>();
}
Limitable limit = RowSide.limitable(position, sort);
List<Resume> list = dao.findAll(spec(siteId, params), limit);
return RowSide.create(list, bean);
}
private Specification<Resume> spec(final Integer siteId,
Map<String, String[]> params) {
Collection<SearchFilter> filters = SearchFilter.parse(params).values();
final Specification<Resume> fsp = SearchFilter.spec(filters,
Resume.class);
Specification<Resume> sp = new Specification<Resume>() {
public Predicate toPredicate(Root<Resume> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate pred = fsp.toPredicate(root, query, cb);
if (siteId != null) {
pred = cb.and(pred, cb.equal(root.get("site")
.<Integer> get("id"), siteId));
}
return pred;
}
};
return sp;
}
private ResumeDao dao;
@Autowired
public void setDao(ResumeDao dao) {
this.dao = dao;
}
}
该类使用到JPA的Specification查询方式。
可实现后台列表点击表头,按任意列排序;列表页按任意字段查询;编辑页面上一条、下一条功能。
/WEB-INF/classes/conf/plugin/plug/controller-back.xml
<bean id="PlugResumeList"
class="com.jspxcms.plug.web.directive.ResumeListDirective" />
在/WEB-INF/classes/conf/plugin/plug/application.properties中加上配置:
freemarkerVariables.ResumeList=PlugResumeList
其中ResumeList为标签名。
package com.jspxcms.plug.web.directive;
public class ResumeListDirective implements TemplateDirectiveModel {
public static final String SITE_ID = "siteId";
@SuppressWarnings({ "rawtypes", "unchecked" })
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
if (loopVars.length < 1) {
throw new TemplateModelException("Loop variable is required.");
}
if (body == null) {
throw new RuntimeException("missing body");
}
Integer[] siteId = Freemarkers.getIntegers(params, SITE_ID);
if (siteId == null && params.get(SITE_ID) == null) {
siteId = new Integer[] { ForeContext.getSiteId(env) };
}
Sort defSort = new Sort(Direction.DESC, "creationDate", "id");
Limitable limitable = Freemarkers.getLimitable(params, defSort);
List<Resume> list = service.findList(siteId, limitable);
loopVars[0] = env.getObjectWrapper().wrap(list);
body.render(env.getOut());
}
@Autowired
private ResumeService service;
}
在/WEB-INF/classes/conf/plugin/plug/application.properties中加上如下配置:
后台国际化文件
messageSource.basenames.plug=/WEB-INF/messages/plugin/plug/plug
前台国际化文件
messageSource.basenames.plugfore=/WEB-INF/messages/plugin/plugfore/plugfore
其中messageSource.basenames.plug中的plug通常为配置文件所在文件夹的名称,可以改为其他名字,但不能与其他相关配置重名。
/WEB-INF/messages/plugin/plug/plug为国际化文件所在文件夹和文件名。
关于“Jspxcms无侵入式二次开发的方法是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Jspxcms无侵入式二次开发的方法是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。