2024年2月24日发(作者:)
SCM框架情况
目录
SCM框架情况 ................................................................................................................. 1
1
2
3
开发工具.................................................................................................................. 3
编程工作空间文件夹 ............................................................................................... 3
框架技术特点 .......................................................................................................... 6
3.1
Hibernate3 ......................................................................................................... 6
entity类 ..................................................................................................... 6
DAO类 ....................................................................................................... 7
分页查询与HQL查询 ................................................................................. 8
3.1.1
3.1.2
3.1.3
3.2
Service ............................................................................................................... 8
业务类........................................................................................................ 8
业务层bean定义 ....................................................................................... 9
业务层事务控制与定义 .............................................................................. 9
业务层基类定义 ....................................................................................... 12
3.2.1
3.2.2
3.2.3
3.2.4
3.3
Web层 ............................................................................................................ 12
struts2 ...................................................................................................... 12
皮肤sitemesh ........................................................................................... 16
客户端校验 .............................................................................................. 16
3.3.1
3.3.2
3.3.3
3.4
授权控制 ......................................................................................................... 18
菜单使用 .................................................................................................. 19
验证码集成 .............................................................................................. 19
SCM的封装 .............................................................................................. 19
19
负载均衡的Spring注销............................................................................ 19
3.4.1
3.4.2
3.4.3
3.4.4
3.4.5
3.5
Session复制技术 ............................................................................................. 21
简介 ......................................................................................................... 21
配置Terracotta服务器 ............................................................................. 21
3.5.1
3.5.2
3.5.3
3.6
Tomcat连接Terracotta服务器................................................................. 23
其他辅助技术.................................................................................................. 23
日志技术:logback ................................................................................... 23
邮件模板技术........................................................................................... 24
自动任务 .................................................................................................. 25
缓存 ......................................................................................................... 25
Spring HTTP Invoker远程访问 ................................................................... 26
. 30
3.6.1
3.6.2
3.6.3
3.6.4
3.6.5
3.6.6
1 开发工具
统一的开发环境目录为D:irisWork
特别的一些辅助开发工具目录为D:irisWorktools
内容包括:
JDK6: D:irisWorktoolsjdk1.6,(必备)
特殊用途的Lib:D:irisWorktoolslib(必备)
Maven:打包与jar管理工具(必备)
Nexus:建立自己的Maven Jar管理库(可选,实际公司内部已有192.168.0.18:80/nexus/)
Stylecheck:公司自己的代码样式检查约束(必备)
Tomcat:统一的tomcat程序环境D:irisWorktoolstomcat(必备)
Geronimo:一个开源的javaEE服务器(可选)
IDE工具:eclipse 3.5,可以从网站下载最新的3.5版本,然后添加以下插件:
svn版本控制工具(必须安装):/update_1.6.x
maven项目管理工具(必须安装)/update
springIDE工具(必须安装)/release/IDE
汉化包(有细微bug)
/technology/babel/update-site/galileo;
属性资源文件编辑器:/eclipse(有bug,影响java编码)
Hibernate的IDE工具、FreeMarker模板的IDE工具
/jbosstools/updates/stable/
Checkstyle代码样式检查器:/update/ 限安装5.0版本
Oracle工具(不稳定)/otn_software/oepe/galileo
2 编程工作空间文件夹
主要工作空间: D:irisWorkscholarmate3
项目介绍
CAS 单点登录使用的主程序
scm3-parent 一个统一处理pom的东西
scm3-core version3的公共类,包括访问数据库、权限、菜单、邮件等封装
scmwebsns version3的web程序项目,对应的资源项目为scmwebsnsres
scmcodesns version3 web对应的后台代码,同时也是其他调度任务、接口等后台代码
scmwebrol version3的ROL程序项目,对应的资源项目为scmwebrolres
scmwebjob 未实现:基于quartz的调度程序
scmsearch
jrun
scmjmssns
通用目录结构
几乎完全采用maven的默认布局。
src main
main/java
main/resources
main/webapp
test
主源码目录
未实现:基于搜索引擎的项目
未实现:论坛。
scholar对外接口使用的进程,用于给ROL站点调用使用。
java源文件
配置文件、属性文件
web应用目录
target
测试目录,结构与主源码目录相同
maven编译目录
scm3-core 包介绍
ion
2
ty
e
zation
ty
各个层次的异常包
struts2的CRUDAction封装包与工具包
权限控制代码
数据库访问与业务层基础代码
邮件发送接口
菜单呈现代码
项目特别需要的工具类
自行对display标签的国际化支持改进
自动根据文件夹选择spirng环境配置
针对多域名和权限方面进行特别的项目定制
scmsnsweb 目录简介
资源类目录
srcmainresources
config
ehcache
mailtemplate
resource
spring
scmtheme
开发与测试环境特定的变量
缓存配置文件路径
邮件模板目录
国际化使用的资源目录
spring配置文件
struts2使用的模板
log配置
struts2核心配置文件
3 框架技术特点
3.1 Hibernate3
JPA是未来趋势,但目前JPA还未完全大规模使用,由于HibernateAPI提供的功能要强于基于JPA,本项目还继续使用Hibernate的开发接口。
3.1.1 entity类
3.1.1.1 annoattion:
Hibernate同样使用JPA annotation,简单的可以手写,利用默认值几乎不用写什么东西。
定义实体
@Entity
@Table(name = "PERSON")
@Cache(usage = _WRITE)
public class Person {
定义主键
@SequenceGenerator(name="generator",sequenceName="SEQ_PERSON",allocationSize=1)
@Id
@GeneratedValue(strategy =
SEQUENCE, generator = "generator")
@Column(name = "PSN_ID", unique = true, nullable = false)
public Long getPsnId() { return ; }
特别情况下,我们将采取特定的自定义主键方法,
定义普通列
@Column(name = "FORGET_PASSWORD_URL", length = 300)
public String getForgetPasswordUrl() {return PasswordUrl;}
详细关于Hibernate 中annoattion的使用方法参考hibernate JPA的相关章节
3.1.1.2 命名策略:
在sessinFactory中使用ImprovedNamingStrategy,即可将LOGIN_NAME 这种带下划线的列名自动映射为loginName的属性,数据库字段命名也应该按LOGIN_NAME方式命名。
@Column(name = "NAME_ABBR")
public String getNameAbbr() {return br; }
3.1.1.3 二级缓存:
在Entity类声明Hibernate缓存策略。需搭配使用ehcache等cache方案,在做进一步的配置,否则使用ehcache默认配置。
注意:并不是所有对象都可以缓存,请按需要选择。
缓存方法:在类定义中编写 @Cache
@Entity
@Table(name = "SYS_USER", uniqueConstraints = { @UniqueConstraint(columnNames = { "LOGIN" }) })
@Cache(usage = _WRITE)
public class User {......}
3.1.1.4 自动序列
目前自动序列在本项目中可能支持2种数据库:ORACLE和MySQL,对于Oracle,可以直接使用Oracle提供的序列对象,如下样本定义:
@SequenceGenerator(name="generator",sequenceName="SEQ_PERSON",allocationSize=1)
@Id
@GeneratedValue(strategy =
SEQUENCE, generator = "generator")
@Column(name = "PSN_ID", unique = true, nullable = false)
public Long getPsnId() { return ; }
对于MySQL数据库,如果需要序列的类似功能并达到灵活定义的效果,需要按如下定义:
@Id
@GeneratedValue(strategy = ,generator="table_gen")
@TableGenerator(name = "table_gen", allocationSize=1 )
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {return ; }
上面中的MySQL序列的值保存在数据库中的一个表,表名字与实体名相同。
3.1.2 DAO类
DAO本结构中DAO不再使用接口,原因:本DAO与业务层联系较为紧密。
每一个DAO在spring中的声明通过@Repository("personDAO")注释进行声明,无需在文件中定义。有关@Repository的说明看网站spring开发手册的“自动检测组件的命名”一节。
声明为持久层类
@Repository
public class UserDAO extends HibernateDao
3.1.3 分页查询与HQL查询
HibernateDao提供的find接口函数提供了支持包括分页查询、选择缓存级别功能在内各种HQL查询能力,如果默认提供的查询能力不足使用,可以自行在DAO实现自定义的H查询功能,比如查询缓存、二级缓存、特定的大数据量查询
方法摘要
3.2 Service
所有业务逻辑编写都在这里进行,service的类依赖spring实现DAO的注入,依赖spring提供的事务配置定义事务范围。
3.2.1 业务类
接口声明:每个业务类接口继承SnsEntityManager基类。业务层实现类也必须继承SnsEntityManagerImpl实现类,该实现类提供通用的业务实现方法,提高代码复用。
注意:以上的基类可能根据项目的不同会有所变化。
为了减少webAction层调用和业务层互相调用的偶合,业务层也有一个ServiceFactory类,多次使用的service类应该在这里进行定义。
业务工厂类定义
public class ServiceFactoryImpl implements ServiceFactory {
private UserInfoDemoService userInfoDemoService;
private PersonManagerService personManagerService;
private ConstantDictionaryService constantDictionaryService;
3.2.2 业务层bean定义
以往spring的版本都在spring的xml中定义 bean,在spring2.5中可以通过@Service的方式进行定义,具体详细看spirng开发指南中的“自动检测组件的命名”。
业务层声明示例
//Spring Service Bean的标识.
@Service("UserManager")
//默认将类中的所有函数纳入事务管理.
@Transactional
public class UserManagerImpl extends DefaultEntityManagerImpl
UserManager {
@Autowired
private UserDAO userDAO;
3.2.3 业务层事务控制与定义
事务控制在spring2.5中可以通过@Transactional注释进行处理。如果在类上面定义,则可影响到整个类的所有方法默认事务定义,如果在方法定义,则以方法的事务定义为主。
定义事务一般有:
@Transactional(readOnly = true)
@Transactional(readOnly = false, propagation = ED)
3.2.4 Spring事务传播
Spring的局部事务管理策略是基于PlatformTransactionManager(PTM)的, 这个接口比较单纯, 只有如下三个方法。
1. TransactionStatus getTransaction(TransactionDefinition
throws TransactionException;
2. void commit(TransactionStatus status) throws TransactionException;
3. void rollback(TransactionStatus status) throws TransactionException;
definition)
3.2.4.1 PROPAGATION_REQUIRED
设想有个Service(A), 它采用声明式事务, 事务传播行为是PROPAGATION_REQUIRED. 同时使用DataSourceTransactionManager(DTM)作为事务管理器. 那么在调用Service(A)之前,
TransactionInterceptor会构造TransactionInfo(A), 并把它绑定到当前线程上, 同时调用nsaction方法, 以下是nsaction方法调用完毕后一种可能的事务状态。
TransactionInfo(A)
|->oldTransactionInfo = null
|->TransactionStatus(A)
|->newTransaction=true
|->savepoint=null
|->DataSourceTransactionObject(A)
|->newConnectionHolder=true
|->SuspendedResourcesHolder(A)
| |->suspendedResources=null
|->ConnectionHolder(A)
|->connectionHandle=SimpleConnectionHandle(nection())
|->transactionActive=true
假设在Service(A)中调用了Service(B), 它的事务传播行为也是PROPAGATION_REQUIRED.
以下是为Service(B)调用了nsaction方法后一种可能的事务状态。
TransactionInfo(B)
|->oldTransactionInfo=TransactionInfo(A)
|->TransactionStatus(B)
|->newTransaction=false
|->savepoint=null
|->DataSourceTransactionObject(B)
|->newConnectionHolder=false
|->SuspendedResourcesHolder(B)
| |->suspendedResources=null
|->ConnectionHolder(A)
TransactionInfo(A)和TransactionInfo(B)是绑定到当前线程的, 而且从以上两个状态描述中可以看出来, TransactionInfo采用了单向链表结构, 表头永远是当前堆栈栈顶的方法对应的TransactionInfo对象. 由于Service(A)和Service(B)的事务传播行为都是PROPAGATION_REQUIRED, 所以TransactionStatus(B)的newTransaction属性是false, 也就是在对Service(B)的调用中并没有开启新的事务. 同时DataSourceTransactionObject(B)的ConnectionHolder引用是指向ConnectionHolder(A)的, 也就是说在Service(B)中将和Service(A)使用同一个JDBC连接。
3.2.4.2 PROPAGATION_REQUIRE_NEW
假设在Service(A)中调用了Service(B), 它的事务传播行为是PROPAGATION_REQUIRE_NEW. 以下是为Service(B)调用了nsaction方法后一种可能的事务状态。
TransactionInfo(B)
|->oldTransactionInfo=TransactionInfo(A)
|->TransactionStatus(B)
|->newTransaction=true
|->savepoint=null
|->DataSourceTransactionObject(B)
|->newConnectionHolder=true
|->SuspendedResourcesHolder(B)
| |->suspendedResources=ConnectionHolder(A)
|->ConnectionHolder(B)
|->connectionHandle=SimpleConnectionHandle(nection())
|->transactionActive=true
由于Service(B)的事务传播行为是PROPAGATION_REQUIRE_NEW, 那么Service(A)的事务将被挂起, 在SuspendedResourcesHolder(B)的suspendedResources属性中保存了被挂起的ConnectionHolder(A). ConnectionHolder(B)也不再指向ConnectionHolder(A), 而是获得一个新的JDBC连接, 同时DTM会把ConnectionHolder(B)绑定到当前线程上. 这意味着Service(B)的事务会在一个不同于Service(A)的JDBC连接中进行. 直到Service(B)方法执行完毕后, DTM会把ConnectionHolder(A)重新绑定到当前线程上, 也就是说接下来的Service(A)方法中仍然会使用ConnectionHolder(A)中保存的JDBC连接。
3.2.4.3 PROPAGATION_NOT_SUPPORTED
如果Service(B)的事务传播行为是PROPAGATION_NOT_SUPPORTED, 那么Service(A)的事务将被挂起, 同时DTM会解除当前线程上ConnectionHolder(A)的绑定. 但是并不会获得一个新的JDBC连接(因为不需要为ServiceB开启事务, 同时TransactionStatus(B)的newTransaction属性是false). 但是如果在Service(B)中使用了nection()方法, 那么在Service(B)中会获得一个新的JDBC连接, 并绑定一个新的ConnectionHolder到当前线程上。
3.2.4.4 PROPAGATION_NESTED
如果Service(B)的事务传播行为是PROPAGATION_NESTED, 那么会开启一个嵌套事务. 首先引用一下Juergen Hoeller关于嵌套事务的描述:
PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true
subtransaction of the existing one. What will happen is that a savepoint will be taken at the start
of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The
nested transaction is part of of the outer transaction, so it will only be committed at the end of of
the outer transaction。
Nested transactions essentially allow to try some execution subpaths as subtransactions:
rolling back to the state at the beginning of the failed subpath, continuing with another subpath
or with the main execution path there - all within one isolated transaction, and not losing any
previous work done within the outer transaction。
也就是说, 嵌套事务和外层事务使用的是同一个JDBC连接上的事务, 嵌套事务伴随着外层事务的提交而提交, 如果嵌套事务rollback, 那么嵌套事务会回滚到嵌套事务开启时的Save Point处, 而外层事务不受影响. 因此嵌套事务需要支持JDBC 3.0 Save Point特性的数据库以及JDBC驱动程序(用aData().supportsSavepoints()判断, MySql5.1支持). 以下是为Service(B)调用了nsaction方法调用完毕后相关的事务状态. 其中TransactionStatus(B)的savepoint属性保存了epoint()返回的Savepoint对象。
TransactionInfo(B)
|->oldTransactionInfo=TransactionInfo(A)
|->TransactionStatus(B)
|->newTransaction=false
|->savepoint=Savepoint(B)
|->DataSourceTransactionObject(B)
|->newConnectionHolder=false
|->SuspendedResourcesHolder(B)
| |->suspendedResources=null
|->ConnectionHolder(A)
3.2.5 业务层基类定义
所有业务类的实现类继承SnsEntityManagerImpl,业务类的接口继承SnsEntityManager接口。
3.3 Web层
3.3.1 struts2
3.3.1.1 总体变化
MVC框架最主要的两个功能是页面参数与Action Model的转换 和 页面流转控制,我们考察框架时也多从这两点入手。
Struts2模式上的变动:
一、Action 从singleton bean 改为了per request的prototype bean。之前Struts1为了线程安全,action里面没有任何成员变量,在一个处理函数里输入输出都靠request,response参数搞定。
二、将很多处理透明的分解到了Interceptor Chain中,
因此,Struts2中Action在MVC里的位置,从Controller转为了Model(有属性、有行为且不依赖Request/Response的POJO),而Controller的角色则由Struts2亲自担当。
Struts2实际上的简化:
FormBean与Action合一,直接读写成员变量,不再需要FormBean或LazyBean,不再需要从request读写数据,每个函数不再需要长长的Request/Response参数。
实用的Prepareable接口和ModelDriven接口,比在在update时先从数据库载入拥有10个属性的对象,然后从页面绑定其中5个属性,整个过程很完美。
Convention,基于约定与annotation实现零配置文件。
3.3.1.2 参数绑定,ModelDriven, Prepareable
注意:该方法在struts2中不推荐大量使用,这些属于老struts1的写法了。
无论是将Action中的变量渲染页面中,或者从request中将内容回传到Action中变量的过程,统称参数绑定。
1. 最原始的Struts2会直接赋值Action中的变量。 如?id=1,会将action中的id属性赋值。
2. 如果参数较多,而且都属于同一个对象的,可以将所有属性都放入一个对象中,比如? 会为action中的User对象的id属性赋值。
3. ModelDriven接口,如果不想写太多"user."前缀,如${},可以实现ModelDriven接口的getModel函数,返回user对象。则Struts2碰到{id}时,就会尝试调用getModel() 获得user对象再获取其id属性。
4. Prepareable接口,还有一种情况Hibernate常用的情况,一个对象可能有很多属性(比如有10个属性),但页面上可能只显示5个属性的输入框。如果按上面的方法,先new一个User类,然后从页面上赋值。保存此对象时就会将不在页面上修改的5个属性清空了。这时就需要两次的binding,一开始user变量为空,只绑定了action的id属性,然后在prepare()函数中查出有完整10个属性的对象,然后二次绑定时再将页面的那5个属性复制到user对象中。 prepare()函数有两种作用,一种专门为了二次binding,一种是作为公共的数据准备函数。但是,一个action内有多个method,不是每一个method都需要执行prepare,比如list()方法,如果这种method较多,或者会造成冲突时,还有另外一种方式来定义二次binding函数。比如prepareSave() 函数,就会默认的在执行save()前执行。
3.3.1.3
零配置文件
Convention Plugin是2.1.6最新坐正的零配置Plugin,取代了原来的CodeBehind等插件 。
请看 官方文档。
1. 能自动扫描所有Action类(默认为struts2 下的所有子package,SCM约定)
2. 所有扫到的Action,Convention猜测其NameSpace,如的路径为 /hello/,package名如不符合这个规则也可以在action中用@Namespace重新指定。
3. jsp名同样以默认规则定义,对于UserAction返回return SUCCESS,默认访问/WEB-INF/jsp/user/ 或 . 返回 "input" ,默认访问/WEB-INF/jsp/user/
4.如果有特殊的结果指向(如redirect类型的结果),在Action处用@Result配置。
5.如有package级的配置(如使用非默认的Interceptor栈),仍在中定义package,用@ParentPackage指定。也可以使用Convention Plugin提供的其他annotation进行配置。
6.可以用 /user/的URL 打开 /WEB-INF/content/user/ ,而LoginAction无需实际编写。
3.3.1.4 Theme设定
虽然没有用多少Struts2的taglib,但还是避免不了需要修改它自己的标签,否则难以适应公司自己的交互设计师的特别要求。
1.在classpath的/template目录中新增自己的theme目录,如/template/scmtheme,从struts2的默认simple theme中复制出进行修改。
2.在jsp中使用新的theme
如果需要默认都使用新的theme:
1.在classpath:/template/scmtheme 中放置ties,里面放一句parent =
css_xhtml,即所有未重新实现的ftl,都使用xhtml默认的模板。
2.修改,增加
3.3.1.5 一些扩展:JSON
详细看demo关于 JSON方面。
3.3.1.6 信息与异常显示
1.配置使用store interceptor,可以在redirect页面时,将信息存储在session中.
AUTOMATIC
2. 注意:使用addActionMessage来添加信息可以按指定要求跳转, 如果用addActionError会自动跳到input页,可能违背代码逻辑流程。
3.3.1.7 关于Taglib的规则
1. 使用原版的html语法,使用JSP2.0 EL 输出动态变量。
2. 不使用Struts2的UI taglib,部分一定需要使用的,只使用自定义的样式如scmtheme。
3. 控制语句统一使用Struts2的taglib
4. Struts2的特殊tag:
a.
theme="simple"/>
b.
c.
"/>
VS:
d.
3.3.1.8 输入校验与界面国际化
Struts2的validate框架优点是可以在客户端和服务端同时进行校验。这里推荐使用。
也可以用JQuery的validate plugin,见demo例子,但背景一定也需要struts validate进行后台校验。后台校验参考例子:Changepassword
国际化方面使用资源文件放置在action类相同的位置,使用 其中国际化方面有3种,下表列出国际化的简要方法: STRUTS2 定义全局资源路径资源定义 业务层 spirng bean定义的资源路径 DBMS 数据字段 ces Action文件所在的位置 id="messageSource" Action_zh_ties getText( key ) getMessage(key,locale) 自定义编码 使用方式 3.3.1.9 Struts2Utils基类: 绕过jsp/freemarker直接输出字符串/JSON/XML的便捷函数,用于ajax请求等简单输出结果,支持encoding,no-cache等参数。 获取http request/response/session的简化方法. 当然还有其他后期加入的工具方法。 3.3.1.10 CRUDAction基类(涉及CRUD时的可选基类): 规范了CRUD函数的名称,规定使用ModelDriven和Preparedable接口,并规范了prepare二次绑定接口只在input和save函数中的使用 流程如下: 1. 用户打开用户列表页,访问/ a.执行默认的execute() 函数,实际执行list() 函数。 () 函数查询列表放入某list变量,返回SUCCESS,默认跳转到 取出action中的list变量进行渲染。 2. 用户新增对象时,访问/user! a.首先将id放入id变量,执行prepareInput()函数创建一个新的user变量 () 函数,返回INPUT,默认跳转到 执行getModel()获得user变量渲染input框 3. 用户提交Form,访问/user! a.执行preparedSave()函数创建新的user变量,将input框的内容绑定到getModel()获得的user变量 b.执行save()函数保存user,返回RELOAD,跳转到@Result中定义RELOAD页面,以redirect方式重新打开/ 4. 用户修改对象,访问/user!?id=1 a.绑定id=1到id变量,执行prepareInput()从数据库查询出user。 b.下同2. 5. 用户提交Form,访问/user! a.绑定id=1到id变量,执行prepareSave()从数据库查询出user。 b.下同3. 3.3.2 皮肤sitemesh 使用sitemesh组件。 使用时只需要在进行配置。本功能由于大家都已经简要使用过,可以参考历史项目方法实现。 3.3.3 客户端校验 Validation是著名的客户端输入校验plugin,可进行非空、数字等常规校验,也可以远程ajax判断用户名是否唯一,将光标停在出错的输入框,并提示出错信息,当用户重新输入时即时重新进行校验。 详细示例见Remember Me Signup Form remote校验,远端URL只需返回true或者false字符串。 错误信息默认按messages_zh_ 中的内容 校验规则可以用class="required email" minlength="3"快速定制校验。 也可以用javascript定制校验规则和错误信息。快速定制与javascript可结合使用。为了不扰 乱class属性,除非只有很少的fileld需要验证,希望快速解决时使用快速定制,否则都用javascript来定制。 这里是定制的校验脚本样本: 如果输入框名称含".",则加上双引号 $("#myform").validate({ rules: { "user[email]": "email", "": "required" } }); 如果希望在一些网站前台页面填对后出绿色的图标: 在定制的js增加,增加checked class。 success: function(label) { ss("checked"); } 3.4 授权控制 在数据库中配置URL访问控制。 在JSP中使用taglib控制页面显示内容。 在POJO中使用annotation控制函数访问. 另授权名称默认前缀为ROLE_,为了改为A_以免被误认为是角色名,中很是做了一番配置。 授权与URL的关系放在数据库中,实现ResourceDetailService接口从数据库中获取。 授权关系表有变化: sys_resource sys_authoritie sys_role sys_user 资源表,存放菜单或者URL的链接与层次 权限表,与资源表主键形成多对多关系 角色表,一个角色拥有多个权限,与权限表形成多对多关系 用户表,与角色形成多对多关系 SYS_USERPK IDLOGIN_NAMEPASSWORDENABLEDTOKEN_CHANGEDAUTH_LOGIN_IPGUIDNAMEPK SYS_ROLEIDNAMEPARENT_IDDESCRIPTIONSTATETYPESYS_AUTHORITIEPK IDNAMEDISPLAY_NAMEPK SYS_RESOURCEIDRESOURCE_TYPEVALUEORDER_NUMPARENT_IDTARGETNAMESTATUSSYS_USER_ROLESYS_ROLE_AUTHORITIESYS_RESOURCE_AUTHORITIEFK2FK1 USER_IDROLE_IDFL_NAMEFK2FK1ROLE_IDAUTHORITY_IDFK2FK1AUTHORITY_IDRESOURCE_ID 3.4.1 菜单使用 菜单的资源放在资源表sys_resource中并通过resource_type和其他URL资源加以区别,这个和之前的sys_menu取消了。 添加菜单时,在sys_resource加入URL与连接,在sys_authoritie加入权限定义,并在角色权限表进行授权。 菜单需要进行国际化,国际化资源存放在resources/resource/menuResource_*.properties;添加菜单时需要同时添加资源对应的国际化数据 3.4.2 验证码集成 暂时未实现 计划在CAS服务端实现 3.4.3 SCM的封装 DefinitionSourceFactoryBean 不同于网上常见的重写FilterInvocationDefinitionSource的做法,只是新写了一个FactoryBean,向默认的DefaultFilterInvocationDefinitionSource注入从ResourceDetailService中返回的RequestMap. 3.4.4 SpringSecurityUtils 这里加入自己使用的权限工具类,封装了获取当前用户的简便函数,日后根据需要添加方法。 3.4.5 负载均衡的Spring注销 Scholarmate由于使用负载均衡技术,而spring的注销采用的是由CAS端主动发送请求到WEB端注销,可能导致请求到其他WEB站点的情况,出现注销失败,改进方案如下: 1、登录成功后发送标识数据往缓存服务器(casKey,sessionId,currentLogoutUrl) 1、用户无效请求登录2、登录成功跳转CASapacheSCM1SCM23、发送验证通过的标识数据cache容器缓存服务器 2、用户注销 1、注销请求2、发送注销CASapachelogoutFilterSCM1logoutFilterSCM25、发送注销4、发送注销cache容器缓存服务器3、注销请求 1、 SCM1向Cas发送注销请求,Cas端根据用户的CasKey发送一个Http请求往SCM服务器; 2、 3、 apache可能选择SCM1、SCM2中的一个,假设选择的是SCM2,SCM2此时拦截到注销请求以及CasKey,则向缓存服务器发送注销请求 缓存服务器根据CasKey找到对应的sessionId以及Url,之后发起一个冒充的Http注销请求(请求cookie中的sessionId冒充) 3.5 Session复制技术 3.5.1 简介 使用Terracotta实现跨Tomcat节点的session复制,原理:Terracotta的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta只把变化的部分发送给Terracotta服务器,然后由服务器把它转发给真正需要这个数据的节点。这样对网络的压力就非常小,各个节点也不必浪费CPU时间和内存进行大量的序列化操作。把这种集群间数据共享的机制应用在session同步上,相当于对tomcat第二种集群实现机制进行了优化,既避免了对数据库的依赖,又能达到负载均衡和灾难恢复的效果。在对比测试中,采用Terracotta搭建Tomcat集群,节点达到8个时候,整个集群的吞吐量还一直是线性增长的。 (见文章/lima01/archive/2009/07/25/) 注:由于teracotta还存在很多不稳定性因素,系统中需要使用session的部分还是使用nDataCacheService进行数据存储,方便将来进行切换,但是其内部实现还是使用传入的session进行操作的 3.5.2 配置Terracotta服务器 1、从zhengbinirisWorktools拷贝terracotta目录到本地d: irisWorktools目录下 2、在控制台进入d: irisWorktoolsterracottaterracotta-3.2.0执行(安装相应的tomcat模块) upgrade 3、在控制台进入d: irisWorktoolsterracottaterracotta-3.2.0执行(生成客户端启动模块) -f 执行后可以进入目录D:irisWorktoolsterracottaterracotta-3.2.0libdso-boot查看已生成dso-boot-hotspot_win32_160_文件(注意,根据本地JDK版本号的不同,生成的文件会不一样,这在稍后配置tomcat启动环境会有影响) 4、此时在控制台进入d: irisWorktoolsterracottaterracotta-3.2.0执行以下命令应该可以启动服务器了: 5、进入目录d: irisWorktoolsterracottaterracotta-3.2.0bin运行能监视查看已启动的服务器: 3.5.3 Tomcat连接Terracotta服务器 1、配置tomcat连接Terracotta服务器很简单,只需要在服务器的自定义变量添加一段代码就行(注意jar地址): -Xbootclasspath/p:"D:irisWorktoolsterracottaterracotta-3.2.0libdso-bootdso-boot-hotspot_win32_160_" -l-root="D:irisWorktoolsterracottaterracotta-3.2.0" -=localhost:9510 配置完成后启动tomcat,能看到tomcat连接服务器的信息: 2、另一个集群SNS tomcat也进行同样的配置 3.6 其他辅助技术 3.6.1 日志技术:logback log4j已经很早了,配置有点不算太方便,新的logback在配置上就较为简单。而且以前 的老代码可不用重新编写即可平滑升级,直接支持旧的log4j代码使用。 配置日志打印可以通过如下设置: 3.6.2 邮件模板技术 旧邮件功能缺陷: 之前IRIS的做法在一个邮件出现多行记录的时候,需要采取另外合并邮件的途径去解决,无法通过配置SQL方式去解决问题. 常规的做法是通过模板文件来定义邮件,详细使用方法看FreeMarkerTemplate 的使用,具体样本在里面,模板文件看 ${content} <#list users as being> #list> 业务类sendMailMimeMailServiceexecute线程池Freemarker邮件模板Scm-codeSTMP 使用邮件接口的方法: 很简单,直接引用MailService接口,然后注入配置,调用相应的发送邮件接口即可。 模板的开发统一放在模板文件夹里面,由业务类指定使用的模板即可。 3.6.3 自动任务 由于自动任务程序尽量保证单例方式运行,避免多线程运行,之后会使用 如果要执行Cron式计划的任务,选择Quartz。 如果要执行简单的Timer式定时任务,Quartz或JDK5的ScheduleExecutor都是可行的。但两者之间又有区别,如果前一任务执行时间太长了,超过了时间间隔时-- ScheduleExecutor是不可以并发执行的,fixedRate为属性为true时,ScheduleExecutor会等到前一个任务完成时才立刻补回之前欠下的任务。fixedRate默认为false时更加会等到前一任务完成时才开始时间间隔计时。 Quartz可设置线程池,无状态的任务会并发的执行。 随着任务性质不同时,三种Timer都可能会用上。 无论是Quartz与ShceduleExecutor,都使用了Spring所做的封装。 3.6.4 缓存 Ehcache是最近发展势头最好的开源轻量级Cache方案,涵盖了普通缓存,轻量级缓存,URL/页面内容缓存,中央缓存服务器等方面,而JBossCache是另一个可选的目标。memcache性能最好,但是较为复杂实现。 目前主要作为Hibernate的二级缓存,而自定义缓存根据需要开发时再添加。 3.6.4.1 DefaultCache 对象的默认配置,相对于中的设置, 加大了timeToIdleSeconds,timeToLiveSecond, diskExpiryThreadIntervalSeconds 三项的值。 在内存中最多10000个对象,Idle 1小时,存在100000秒之后过期。对象达到10000个后,按最近访问算法切换到硬盘中,硬盘中最多10000000个对象,每600秒扫描一次磁盘中的过期对象。重启时内存不持久化到硬盘。 memoryStoreEvictionPolicy="LRU" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="600" timeToIdleSeconds="3600" timeToLiveSeconds="100000" diskPersistent="false" /> 3.6.4.2 Cache Cache一般会在默认配置基础上进行配置,比如User对象的配置,就因为用户对象原本就不多,所以设最多500个对象,永不过期,到达500后按默认的最近访问算法切换到硬盘中,硬盘大小无限。 3.6.4.3 分布式缓存 见resources/ehcache/ 和 resources/ehcache/,采用相对成熟的RMI机制,日后会切换到JGroups. 使用同步发送失效消息的方式实现服务器间二级缓存的同步。失效消息告诉对方该缓存已失效。如果需要时重新从数据库等地方载入。 maxElementsInMemory="500" overflowToDisk="true" eternal="true"> class="heReplicatorFactory" properties="replicatePuts=false,replicateUpdatesViaCopy=false" /> 3.6.4.4 远程缓存 Scholarmate在早期设计中使用远程集中缓存来管理Session数据,后被terracotta服务器(session复制)替代,这里指做简要介绍: 远程集中缓存的访问通过使用Spring HTTP invoker进行远程调用(具体见远程访问部分),为了提高效率,远程集中服务器中只存储对象的字节码,即远程存储前需要将存储的对象序列化,访问远程缓存数据时需要将对象反序列化。为了降低成本,远程缓存服务器使用的是EHCACHE进行缓存,当内存不足时可以利用硬盘进行存储。 3.6.5 Spring HTTP Invoker远程访问 Spring开发小组意识到在RMI服务和基于HTTP的服务(如Hessian和Burlap)之间的空白。一方面,RMI使用Java标准的对象序列化,但很难穿越防火墙;另一方面,Hessian/Burlap能很好地穿过防火墙工作,但使用自己私有的一套对象序列化机制。HTTP invoker是一个新的远程调用模型,作为Spring框架的一部分,来执行基于HTTP的远程调用(让防火墙高兴的事),并使用Java的序列化机制(让程序员高兴的事)。―――引自《Spring in Action中文版 》。 当使用RMI时,通过HTTP协议访问对象是不可能的,除非你用HTTP包裹RMI流。RMI是一种很重的协议,因为他支持完全的对象序列化,这样的序列化在要求复杂数据结构在远程传输时是非常重要的。然而,RMI-JRMP只能绑定到Java客户端:它是一种Java-to-Java的远程访问的方案。 如果你需要基于HTTP的远程访问而且还要求使用Java序列化,Spring的HTTP调用器是一个很好的选择。它和RMI调用器使用相同的基础设施,仅仅使用HTTP作为传输方式。注意HTTP调用器不仅只能用在Java-to-Java的远程访问,而且在客户端和服务器端都必须使用Spring。(Spring为非RMI接口提供的RMI调用器也要求客户端和服务器端都使用Spring) 当在异构环境中,Hessian和Burlap就非常有用了。因为它们可以使用在非Java的客户端。然而,对非Java支持仍然是有限制的。已知的问题包括含有延迟初始化的collection对象的Hibernate对象的序列化。如果你有一个这样的数据结构,考虑使用RMI或HTTP调用器,而不是Hessian。 Scholarmate将系统的JEE层与WEB层拆分,WEB层访问JEE层需要通过远程调用的方式进行。目前远程JEE远程调用的方式有很多,此处采用Spring Http invoker进行访问。 3.6.5.1 简单配置方式: 1、要访问一个HTTP invoker服务,你需要使用HttpInvokerProxyFactoryBean。要让一个服务作为一个HTTP invoker服务公开,得配置一个Bean,用HttpInvokerProxyFactoryBean来 代理它,如下所示: 2、让我们转到HTTP invoker对话的另一面,看看如何把Bean的功能作为基于HTTP invoker的服务输出,下面的Bean的定义展示了如何把paymentService Bean作为一个远程的基于HTTP invoker的服务输出。 3、基于HTTP invoker的服务,顾名思义,是基于HTTP的,HttpInvokerServiceExporter也是一个Spring的Controller。这就意味着你需要建立一个URL处理器,把HTTP URL映射到服务上: 4、同时你也需要把这个支付服务部署到web应用中,通过在中配置Spring的DispatcherServlet: cherServlet 3.6.5.2 改进方式: 根据scholarmate特殊业务要求,系统必须有一个更加简便的方式进行远程调用,具体要求是: 1、 客户端动态的提供一个URL,一个接口或远程beanId,就能访问远程JEE服务; 2、 服务器端根据指定的beanID,找到spring中的bean以及暴露给远程调用的接口提供给远程调用请求。 具体实现: 1、 对于客户端动态访问远程JEE服务的改造,需要重写HttpInvokerProxyFactoryBean,见tpInvokerProxyFactoryBean; 2、 JEE服务器端需要自定义一个servlet过滤远程调用请求,根据请求的URL获取请求的beanId,动态的生成一个HttpInvokerServiceExporter实例处理请求,详情见ngCallServlet。 3.6.6 JMS 3.6.6.1 ActiveMQ简介 当前,CORBA、DCOM、RMI等RPC中间件技术已广泛应用于各个领域。但是面对规模和复杂度都越来越高的分布式系统,这些技术也显示出其局限性:(1)同步通信:客户发出调用后,必须等待服务对象完成处理并返回结果后才能继续执行;(2)客户和服务对象的生命周期紧密耦合:客户进程和服务对象进程都必须正常运行;如果由于服务对象崩溃或者网络故障导致客户的请求不可达,客户会接收到异常;(3)点对点通信:客户的一次调用只发送给某个单独的目标对象。 面向消息的中间件(Message Oriented Middleware,MOM)较好的解决了以上问题。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接收者。这种模式下,发送和接收是异步的,发送者无需等待;二者的生命周期未必相同:发送消息的时候接收者不一定运行,接收消息的时候发送者也不一定运行;一对多通信:对于一个消息可以有多个接收者。 JAVA 消息服务(JMS)定义了Java 中访问消息中间件的接口。JMS 只是接口,并没有给予实现,实现JMS 接口的消息中间件称为JMS Provider,Scholarmate使用的JMS Provider是ActiveMQ,ActiveMQ 是开源的JMS实现,Geronimo应用服务器就是使用的ActiveMQ提供JMS服务。ActiveMQ5.0相比以前版本提供了一些非常有用的新功能: 1、 AMQ Message Store (Faster Persistence!) 2、 Cursors (To handle very large number of stored messages) 3、 Blob Messages 4、 Command Agent 5、 Enterprise Integration Patterns via Camel Integration 6、 Logging a warning if you forget to start a Connection 7、 Message Transformation 8、 Mirrored Queues 9、 Flow Control 通过一个实例介绍使用spring发送,消费topic, queue类型消息的方法。 如图示, TOPIC和QUEUE分别代表一个topic和一个queue消息通道. 1、 TopicMessageProducer向topic发送消息, TopicConsumerA和TopicConsumerB则从topic消费消息。JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息,这些节点被称作主题(topic)。主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe) 从主题订阅消息。主题使得消息订阅者和消息发布者保持互相独立,不需要接触即可保证消息的传送。 2、 QueueMessageProducer向Queue发送消息, QueueConsumer从Queue中消费消息,PTP(Point-to-Point)模型是基于队列的,发送方发消息到队列,接收方从队列接收消息,队列的存在使得消息的异步传输成为可能。和邮件系统中的邮箱一样,队列可以包含各种消息,JMS Provider 提供工具管理队列的创建、删除。JMS PTP 模型定义了客户端如何向队列发送消息,从队列接收消息,浏览队列中的消息。 3.6.6.2 Spring整合JMS 就像对orm, web的支持一样, spring同样支持jms, 为整合jms到已有的项目提供了很多便利的方法. 本篇主要讲实战, 是所以先从配置开始, spring配置jms基本上需要8个部分. 1、 ConnectionFactory. 和jms服务器的连接, 可以是外部的jms server, 也可以使用embedded ActiveMQ Broker. 2、 3、 4、 5、 Destination. 有topic和queue两种方式. JmsTemplate. spring提供的jms模板. MessageConverter. 消息转换器. MessageProducer. 消息生产者. 6、 7、 8、 MessageConsumer. 消息消费者. MessageListener. 消息监听器 MessageListenerContainer. 消息监听容器 下面以实例的方式介绍上面8个部分,以下配置不完全正确,主要是为了方便理解,针对JMS部分,scholarmate在实际应用中进行了很多改进。 1. ConnectionFactory class="MQConnectionFactory"> value="tcp://localhost:61616" /> class="ConnectionFactoryBean"> brokerURL是指要连接的activeMQ server的地址, activeMQ提供了多种brokerURL, 集体可参见文档.一般我们使用嵌套的ActiveMQ server. 配置如下, 这个配置使用消息的存储机制, 服务器重启也不会丢失消息。 2. Destination 在实例中我们使用了两种destination 3. JmsTemplate 4. MessageConverter MessageConverter实现的是eConverter接口, 提供消息的转换功能。 class="MessageConverter" /> 5. MessageProducer 实例拥有两个消息生产者, 消息生产者都是POJO. 6. MessageConsumer TOPIC通道有两个消息消费者, QUEUE有一个消息消费者 7. MessageListener 每一个消息消费者都对应一个MessageListener 8. MessageListenerContainer 有几个MessageListener既有几个MessageListenerContainer 9. Summary 写spring配置文件的时候, 要把MessageProducer, MessageConsumer,MessageListener,MessageListenerContainer几个地方弄清楚: 1、 可以有一个或者多个消息生产者向同一个destination发送消息; 2、 queue类型的只能有一个消息消费者; 3、 topic类型的可以有多个消息消费者; 4、 每个消费者对应一个MessageListener和一个MessageListenerContainer。
发布评论