2009年3月6日
#
http://www.iteye.com/topic/930648
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。(如下图)
角色是什么?可以理解为一定数量的权限的集合,权限的载体。例如:一个论坛系统,“超级管理员”、“版主”都是角色。版主可管理版内的帖子、可管理版内的用户等,这些是权限。要给某个用户授予这些权限,不需要直接将权限授予用户,可将“版主”这个角色赋予该用户。
当用户的数量非常大时,要给系统每个用户逐一授权(授角色),是件非常烦琐的事情。这时,就需要给用户分组,每个用户组内有多个用户。除了可给用户授权外,还可以给用户组授权。这样一来,用户拥有的所有权限,就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。(下图为用户组、用户与角色三者的关联关系)
在应用系统中,权限表现成什么?对功能模块的操作,对上传文件的删改,菜单的访问,甚至页面上某个按钮、某个图片的可见性控制,都可属于权限的范畴。有些权限设计,会把功能操作作为一类,而把文件、菜单、页面元素等作为另一类,这样构成“用户-角色-权限-资源”的授权模型。而在做数据表建模时,可把功能操作和资源统一管理,也就是都直接与权限表进行关联,这样可能更具便捷性和易扩展性。(见下图)
请留意权限表中有一列“权限类型”,我们根据它的取值来区分是哪一类权限,如“MENU”表示菜单的访问权限、“OPERATION”表示功能模块的操作权限、“FILE”表示文件的修改权限、“ELEMENT”表示页面元素的可见性控制等。
这样设计的好处有二。其一,不需要区分哪些是权限操作,哪些是资源,(实际上,有时候也不好区分,如菜单,把它理解为资源呢还是功能模块权限呢?)。其二,方便扩展,当系统要对新的东西进行权限控制时,我只需要建立一个新的关联表“权限XX关联表”,并确定这类权限的权限类型字符串。
这里要注意的是,权限表与权限菜单关联表、权限菜单关联表与菜单表都是一对一的关系。(文件、页面权限点、功能操作等同理)。也就是每添加一个菜单,就得同时往这三个表中各插入一条记录。这样,可以不需要权限菜单关联表,让权限表与菜单表直接关联,此时,须在权限表中新增一列用来保存菜单的ID,权限表通过“权限类型”和这个ID来区分是种类型下的哪条记录。
到这里,RBAC权限模型的扩展模型的完整设计图如下:
随着系统的日益庞大,为了方便管理,可引入角色组对角色进行分类管理,跟用户组不同,角色组不参与授权。例如:某电网系统的权限管理模块中,角色就是挂在区局下,而区局在这里可当作角色组,它不参于权限分配。另外,为方便上面各主表自身的管理与查找,可采用树型结构,如菜单树、功能树等,当然这些可不需要参于权限分配。
http://developer.51cto.com/art/200907/136668.htmspring 中已经提供了很好的实现,所以这又省去了很多的功夫,接下来看看
iBATIS是如何支持
Clob和blob的。
iBATIS提供了TypeHandler接口,用于处理数据类型,基本的实现类为BaseTypeHandler
在spring 中,提供了AbstractLobTypeHandler作为基础类,并且提供了相应的模版方法,所有的工作由LobHandler处理。
BlobByteArrayTypeHandler 主要用于处理blob类型数据,使用byte[]来映射相应的Blob
ClobStringTypeHandler 用于处理Clob类型数据,使用字符串来映射Clob
有一点需要注意的是,AbstractLobTypeHandler中实现了事务支持,需要用来释放相应的资源,所以一定需要在事务环境中进行。
下面是一个简单的例子:
- public class Food {
- private String content;
-
- private String id;
-
- private byte[] image;
-
- private String name;
- ...
- }
xml如下:说明一下,在resultMap中可以通过typeHandler来指定具体的handler.在inline变量中,可以通过handler来定义相应的typeHandler
- ﹤sqlMap namespace="Food"﹥
-
- ﹤typeAlias alias="Food" type="org.esoft.hdb.bo.Food"/﹥
- ﹤resultMap id="foodResult" class="Food"﹥
- ﹤result property="id" column="C_ID"/﹥
- ﹤result property="name" column="C_NAME"/﹥
- ﹤result property="content" column="C_content"
- typeHandler="org.springframework.orm.ibatis.support.ClobStringTypeHandler"/﹥
- ﹤result property="image" column="C_image"
- typeHandler="org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler"/﹥
- ﹤/resultMap﹥
- ﹤sql id="foodFragment"﹥select C_ID,C_NAME,C_CONTENT,C_IMAGE from T_FOOD﹤/sql﹥
- ﹤select id="getAll" resultMap="foodResult"﹥
- ﹤include refid="foodFragment"/﹥
- ﹤/select﹥
- ﹤select id="selectById" parameterClass="string" resultMap="foodResult"﹥
- ﹤include refid="foodFragment"/﹥ where C_ID=#id#﹤/select﹥
-
- ﹤insert id="insert" parameterClass="Food"﹥ insert into T_FOOD ( C_ID,
- C_NAME,C_CONTENT, C_IMAGE) values ( #id#,
- #name#,#content,handler=org.springframework.orm.ibatis.support.ClobStringTypeHandler#,
- #image,handler=org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler#)
- ﹤/insert﹥
-
- ﹤update id="update" parameterClass="Food"﹥ update T_FOOD set C_NAME = #name#,
- C_CONTENT =
- #content,handler=org.springframework.orm.ibatis.support.ClobStringTypeHandler#,
- C_IMAGE =
- #image,handler=org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler#
- where C_ID = #id# ﹤/update﹥
-
- ﹤delete id="deleteById" parameterClass="string"﹥ delete from T_FOOD where C_ID = #id#
- ﹤/delete﹥
-
- ﹤/sqlMap﹥
-
-
- public interface FoodService {
-
-
- void save(Food food);
- Food get(String id);
- /**
- * @param food
- */
- void update(Food food);
- }
-
- public class FoodServiceImpl implements FoodService {
- private FoodDAO foodDAO;
-
- private DaoCreator creator;
-
- public void setCreator(DaoCreator creator) {
- this.creator = creator;
- }
-
- protected FoodDAO getFoodDAO() {
- if (foodDAO == null) {
- foodDAO = (FoodDAO) creator.createDao(FoodDAO.class, Food.class);
- }
- return foodDAO;
- }
-
- public Food get(String id) {
- return getFoodDAO().get(id);
- }
- public void save(Food food) {
- getFoodDAO().save(food);
- }
- public void update(Food food) {
- getFoodDAO().update(food);
- }
-
- }
-
- spring xml 配置:
-
- 。。。
- ﹤bean id="lobHandler"
- class="org.springframework.jdbc.support.lob.DefaultLobHandler"/﹥
-
- ﹤bean id="transactionManager"
- class="org.springframework.jdbc.datasource.DataSourceTransactionManager"﹥
- ﹤property name="dataSource" ref="dataSource"/﹥
- ﹤/bean﹥
-
- ﹤bean id="sqlMapClient"
- class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"﹥
- ﹤property name="dataSource" ref="dataSource"/﹥
- ﹤property name="configLocation"﹥
- ﹤value﹥SqlMapConfig.xml﹤/value﹥
- ﹤/property﹥
- ﹤property name="lobHandler" ref="lobHandler"/﹥
- ﹤/bean﹥
-
- ﹤bean id="daoCreate" class="org.esoft.hdb.ibatis.IbatisDaoCreator"﹥
- ﹤property name="sqlMapClient" ref="sqlMapClient"/﹥
- ﹤/bean﹥
-
- ﹤bean id="foodService" class="org.esoft.hdb.service.FoodServiceImpl"﹥
- ﹤property name="creator" ref="daoCreate"/﹥
- ﹤/bean﹥
-
-
- ﹤aop:config﹥
- ﹤aop:pointcut id="foodServiceMethods"
- expression="execution(* org.esoft.hdb.service.FoodService.*(..))"/﹥
- ﹤aop:advisor advice-ref="txAdvice" pointcut-ref="foodServiceMethods"/﹥
- ﹤/aop:config﹥
- ﹤tx:advice id="txAdvice" transaction-manager="transactionManager"﹥
- ﹤tx:attributes﹥
- ﹤tx:method name="*" propagation="REQUIRED"/﹥
- ﹤/tx:attributes﹥
- ﹤/tx:advice﹥
简单的测试:
- save :
- Food food = new Food();
- food.setPk("1");
- food.setName("food1");
- BufferedInputStream in = new BufferedInputStream(getClass()
- .getResourceAsStream("/1.gif"));
- byte[] b = FileCopyUtils.copyToByteArray(in);
- food.setImage(b);
- in = new BufferedInputStream(getClass().getResourceAsStream(
- "/hibernate.cfg.xml"));
- b = FileCopyUtils.copyToByteArray(in);
- food.setContent(new String(b));
- foodService.save(food);
- update:
- Food food = foodService.get("1");
- BufferedInputStream in = new BufferedInputStream(getClass()
- .getResourceAsStream("/jdbc.properties"));
- byte[] b = FileCopyUtils.copyToByteArray(in);
- food.setContent(new String(b));
- foodService.update(food);
- food = foodService.get("1");
- assertNotNull(food.getImage());
select sess.sid,
sess.serial#,
lo.oracle_username,
lo.os_user_name,
ao.object_name,
lo.locked_mode
from v$locked_object lo,
dba_objects ao,
v$session sess
where ao.object_id = lo.object_id and lo.session_id = sess.SID; 获得未提交的事物的列表和基础信息
然后根据 sessionID和serial#号强制关闭事物:
ALTER SYSTEM KILL SESSION '9,108';
--'9,108'为sessionID和serial#号,逗号分开
http://www.javaeye.com/topic/37302
类与类之间的关系对于理解面向对象具有很重要的作用,以前在面试的时候也经常被问到这个问题,在这里我就介绍一下。
类与类之间存在以下关系:
(1)泛化(Generalization)
(2)关联(Association)
(3)依赖(Dependency)
(4)聚合(Aggregation)
UML图与应用代码例子:
1.泛化(Generalization)
[泛化]
表示类与类之间的继承关系,接口与接口之间的继承关系,或类对接口的实现关系。一般化的关系是从子类指向父类的,与继承或实现的方法相反。
[具体表现]
父类 父类实例=new 子类()
[UML图](图1.1)
图1.1 Animal类与Tiger类,Dog类的泛化关系
[代码表现]
- class Animal{}
- class Tiger extends Animal{}
- public class Test
- {
- public void test()
- {
- Animal a=new Tiger();
- }
- }
2.依赖(Dependency)
[依赖]
对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。
[具体表现]
依赖关系表现在局部变量,方法的参数,以及对静态方法的调用
[现实例子]
比如说你要去拧螺丝,你是不是要借助(也就是依赖)螺丝刀(Screwdriver)来帮助你完成拧螺丝(screw)的工作
[UML表现](图1.2)
图1.2 Person类与Screwdriver类的依赖关系
[代码表现]
- public class Person{
-
- public void screw(Screwdriver screwdriver){
- screwdriver.screw();
- }
- }
3.关联(Association)
[关联]
对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。
[具体表现]
关联关系是使用实例变量来实现
[现实例子]
比如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单;再例如公司和员工,每个公司对应一些特定的员工,每个员工对应一特定的公司
[UML图] (图1.3)
图1.3 公司和员工的关联关系
[代码表现]
- public class Company{
- private Employee employee;
- public Employee getEmployee(){
- return employee;
- }
- public void setEmployee(Employee employee){
- this.employee=employee;
- }
-
- public void run(){
- employee.startWorking();
- }
- }
(4)聚合(Aggregation)
[聚合]
当对象A被加入到对象B中,成为对象B的组成部分时,对象B和对象A之间为聚集关系。聚合是关联关系的一种,是较强的关联关系,强调的是整体与部分之间的关系。
[具体表现]
与关联关系一样,聚合关系也是通过实例变量来实现这样关系的。关联关系和聚合关系来语法上是没办法区分的,从语义上才能更好的区分两者的区别。
[关联与聚合的区别]
(1)关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的。
聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚集关系,因为主板是电脑的组成部分。
(2)对于具有聚集关系(尤其是强聚集关系)的两个对象,整体对象会制约它的组成对象的生命周期。部分类的对象不能单独存在,它的生命周期依赖于整体类的对象的生命周期,当整体消失,部分也就随之消失。比如张三的电脑被偷了,那么电脑的所有组件也不存在了,除非张三事先把一些电脑的组件(比如硬盘和内存)拆了下来。
[UML图](图1.4)
图1.3 电脑和组件的聚合关系
[代码表现]
- public class Computer{
- private CPU cpu;
- public CPU getCPU(){
- return cpu;
- }
- public void setCPU(CPU cpu){
- this.cpu=cpu;
- }
-
- public void start(){
-
- cpu.run();
- }
- }
在快捷方式属性-目标里加入 -vm "%JAVA_HOME%/jre/bin/javaw.exe"
Oracle 10g Express Edition是Oracle专门为小型用户提供的免费版本。Oracle XE十分小巧,安装简单,可供第三方软件开发商部署较小的应用。
不过Oracle XE目前的beta2缺省安装的字符集是WE8MSWIN1252,不是中文字符集,并且不能通过直接运行 alter database character set ZHS16GBK ; 来修改,因为ZHS16GBK不是缺省字符集的超集。过去流传很广的直接修改sys用户下的PROPS$表的方法,也会给字符集的变更留下很多潜在的问题.
不过在安装完Oracle XE后,可以在sqlplus(即Oracle XE的run SQL command line)中, 进行如下的操作来修改字符集:
connect system/oracle9i as sysdba
shutdown immediate
startup mount
alter system enable restricted session ;
alter system set JOB_QUEUE_PROCESSES=0;
alter system set AQ_TM_PROCESSES=0;
alter database open ;
alter database character set internal_use ZHS16GBK ;
shutdown immediate
startup
这样字符集的修改就完成了
摘自红色黑客联盟(www.7747.net) 原文:http://www.7747.net/px/200902/34068.html
在数据库服务器上运行 sqlplus system/password@xe (其中 system 是数据库用户无需改变;password 是数据库密码应指定为实际密码;xe 是数据库实例名称) ,然后执行:
alter system set session_cached_cursors=200 scope=spfile;
alter system set session_max_open_files=200 scope=spfile;
alter system set sessions=20 scope=spfile;
alter system set license_max_sessions=200 scope=spfile;
alter system set license_sessions_warning=200 scope=spfile;
alter system set processes=200 scope=spfile;
执行后,重启 Oracle XE 数据库实例即可。要重启 Oracle XE 数据库实例:
1. 如安装于 Windows 上,先运行 net stop oracleservicexe,再运行 net start oracleservicexe 即可。也可通过“服务”管理控制台重启 OracleServiceXE 服务。
2. 如安装于 Linux 上,先运行 /etc/init.d/oracle-xe start,再运行 /etc/init.d/oracle-xe stop 即可。
此时,可以支持 179 个额外的连接会话。
选择“运行SQL命令”,进入如下提示符
SQL>
首先连接到服务器
connect 用户名/密码
登陆后输入如下命令:
sql 代码
1.call dbms_xdb.cfg_update(updateXML(dbms_xdb.cfg_get(),'/xdbconfig/sysconfig/protocolconfig/httpconfig/http-port/text()',8081));
其中8081是修改后的端口,可以任意。
这样你就不会与Tomcat的默认端口冲突了,方便开发。
ftp服务占用2100端口,更改命令是:
sql 代码
1.call dbms_xdb.cfg_update(updateXML(dbms_xdb.cfg_get() , '/xdbconfig/sysconfig/protocolconfig/ftpconfig/ftp-port/text()', 2111));
1. 把插件文件直接覆盖到eclipse目录里
2. 使用link文件,就是把插件存放到任一的地方(例如/eclipse/MyPuls),然后 在eclipse的文件夹里新建一个links的文件,在里面添加一些后追名为.link的文件(例如emfPlugins.link)结构是这样的:
/eclipse/
links/
emfPlugins.link
webtools.link
updateManager.link
...
...
link文件的里包含这样一条 “path=D:\\JavaDev\\plugins\\vssplugin”这个路径就是插件的存放路径。
3. 使用eclipse自带的图形界面的插件安装方法:选择Help > Software Updates > Manager Configuration
在选择Add > Extension Location 找到你要安装插件的目录就可以了。强烈推荐这种方法,优点很多比如可以方便的添加删除,也不用自己写link文件!
备注:Eclipse插件的目录结构
/eclipse-plugins/
eclipse/
.eclipseextension
features/
plugins/
第2、3种方法所指向的目录都指的是"eclipse"目录,
如果用第3种方法,在eclipse这个目录下必须有文件.eclipseextension,如果你下的插件没有这个文件,那就随便eclipse安装目录下的那个文件靠过去就行了!只有有这么个文件就可以了,内容没什么用,主要是一些版本信息!例如:
id=org.eclipse.platform name=Eclipse Platformversion=3.3.1
创建XMLHtttRequest对象(针对mozilla,IE8,FF,IE5,IE5.5,IE6,IE7)
//2、创建XMLHttpRequest对象
//这其实是XMLHttpReqest对象使用最复杂的一步
//针对IE和其他类型浏览器建立这个对象的不同方式写不同的代码
if(window.XMLHttpRequest){
//针对firefox,mozillaz,opera,IE7,IE8
xmlhttp = new XMLHttpRequest();
//用于修复某些Mozillaz浏览器bug
if(xmlhttp.overrideMimeType)
{
xmlhttp.overrideMimeType("text/xml");
}
}
else if(window.ActiveXObject){
//针对IE6,IE5.5,IE5
//两个都可以创建XMLHttpRequest对象,保存在js数组中
var activeName=["MSXML2.XMLHTTP","Microsoft.XMLHTTP"];
//var activeName= ['MSXML2.XMLHTTP.6.0','MSXML2.XMLHTTP.5.0', //'MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0', //'MSXML2.XMLHTTP','Microsoft.XMLHTTP'];
for(var i =0;i<activeName.length;i++)
{//取出一个控件名来创建XMLHttpRequest对象,创建成功就终止循环,如果创建失败可以继续创建
//可以抛出异常,继续创建
try{
xmlhttp= new ActiveXObject(activeName[i]);
break;
}catch(e){
}
}
}
//确认XMLHttpRequest对象已经创建成功
if(!xmlhttp)
{
alert("XmlHttpRequest创建失败");
return;
}
//3、注册回调函数,函数名后面不要加括号
//如果加了括号,就变成了调用函数,会把函数的返回值发挥给xmlhttp,没有达到我们的目的
xmlhttp.onreadystatechange=callback;
//4、设置连接信息
//第一个参数设置http请求方式,主要是get和post两种方式
//第二个参数是uri地址
//第三个参数表示异步交互还是同步交互方式,true表示异步,false表示同步
xmlhttp.open("GET","/JQuery/servlet/AjaxServlet?name="+username,true);
//5、发送数据开始和服务器端交互
//同步方式下,send这句话会在服务器端的数据回来后,才执行完
//异步方式下,send会立即执行完
xmlhttp.send(null);
}
//5、写回调函数
//回调函数
function callback(){
//接收相应的相应数据
//判断交互状态已经完成
if(xmlhttp.readyState ==4)
{
//判断http的交互状态
if(xmlhttp.status == 200)
{
//获取服务器端返回的数据
//获取服务器端纯文本数据
var responseText = xmlhttp.responseText;
//将数据显示在页面上
//通过dom获取div元素节点
var divNode =document.getElementById("result");
//设置元素节点的html内容
divNode.innerHTML=responseText;
}
}
1,<meta name="Robots" contect="all|none|index|noindex|follow|nofollow"> 默认是all
其中的属性说明如下:
设定为all:文件将被检索,且页面上的链接可以被查询;
设定为none:文件将不被检索,且页面上的链接不可以被查询;
设定为index:文件将被检索;
设定为follow:页面上的链接可以被查询;
设定为noindex:文件将不被检索,但页面上的链接可以被查询;
设定为nofollow:文件将不被检索,页面上的链接可以被查询。
2,revisit-after (重访)
<META name="revisit-after" CONTENT="7 days" >
通知搜索引擎多少天访问一次
其他的:
<META NAME="GENERATOR" CONTENT="Macromedia Dreamweaver MX">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<Meta http-equiv="Content-Language" Content="zh-CN">
<Meta http-equiv="Refresh" Content="5; Url=http://hi.baidu.com/pihi">
<Meta http-equiv="Expires" Content="Wed, 26 Feb 1997 08:21:57 GMT">
<meta http-equiv="cache-control" content="no-cache">
<META name="keywords" content="关键字">
<meta name="description" content="描述">
<meta name="author" content="作者">
<meta name="build" content="日期">
<meta name="coprright" content="版权">
<meta name="reply-to" content="email">
<meta name="robots" content="all">
<meta http-equiv="Page-Enter" content="RevealTrans (Duration=3, Transition=23)">
<meta http-equiv="Page-Exit" content="RevealTrans (Duration=3, Transition=23)">
<link rel="shortcut icon" href="favicon.ico">
------------------------------------------------------------------------------------------------------------
meta标签分两大部分:HTTP标题信息(HTTP-EQUIV)和页面描述信息(NAME)。
1、Content-Type和Content-Language (显示字符集的设定)
说明:设定页面使用的字符集,用以说明主页制作所使用的文字已经语言,浏览器会根据此来调用相应的字符集显示page内容。
注意: 该meta标签定义了HTML页面所使用的字符集为GB2132,就是国标汉字码。如果将其中的“charset=GB2312”替换成“BIG5”,则该页面所用的字符集就是繁体中文Big5码。当你浏览一些国外的站点时,IE浏览器会提示你要正确显示该页面需要下载xx语支持。这个功能就是通过读取HTML页面Meta标签的Content-Type属性而得知需要使用哪种字符集显示该页面的。如果系统里没有装相应的字符集,则IE就提示下载。其他的语言也对应不同的charset,比如日文的字符集是“iso-2022-jp ”,韩文的是“ks_c_5601”。
Charset选项:ISO-8859-1(英文)、BIG5、UTF-8、SHIFT-Jis、Euc、Koi8-2、us-ascii, x-mac-roman, iso-8859-2, x-mac-ce, iso-2022-jp, x-sjis, x-euc-jp,euc-kr, iso-2022-kr, gb2312, gb_2312-80, x-euc-tw, x-cns11643-1,x-cns11643-2等字符集;Content-Language的Content还可以是:EN、FR等语言代码。
2、Refresh (刷新)
3、Expires (期限)
说明:指定网页在缓存中的过期时间,一旦网页过期,必须到服务器上重新调阅。
注意:必须使用GMT的时间格式,或直接设为0(数字表示多少时间后过期)。
4、Pragma (cach模式)
说明:禁止浏览器从本地机的缓存中调阅页面内容。
注意:网页不保存在缓存中,每次访问都刷新页面。这样设定,访问者将无法脱机浏览。
5、Set-Cookie (cookie设定)
说明:浏览器访问某个页面时会将它存在缓存中,下次再次访问时就可从缓存中读取,以提高速度。当你希望访问者每次都刷新你广告的图标,或每次都刷新你的计数器,就要禁用缓存了。通常HTML文件没有必要禁用缓存,对于ASP等页面,就可以使用禁用缓存,因为每次看到的页面都是在服务器动态生成的,缓存就失去意义。如果网页过期,那么存盘的cookie将被删除。
用法:<Meta http-equiv="Set-Cookie" Content="cookievalue=xxx; expires=Wednesday,
21-Oct-98 16:14:21 GMT; path=/">
注意:必须使用GMT的时间格式。
6、Window-target (显示窗口的设定)
说明:强制页面在当前窗口以独立页面显示。
用法:<Meta http-equiv="Widow-target" Content="_top">
注意:这个属性是用来防止别人在框架里调用你的页面。Content选项:_blank、_top、_self、_parent。
7、Pics-label (网页RSAC等级评定)
说明:在IE的Internet选项中有一项内容设置,可以防止浏览一些受限制的网站,而网站的限制级
别就是通过该参数来设置的。
用法:<META http-equiv="Pics-label" Contect=
"(PICS-1.1'http://www.rsac.org/ratingsv01.html'
I gen comment 'RSACi North America Sever' by 'inet@microsoft.com'
for 'http://www.microsoft.com' on '1997.06.30T14:21-0500' r(n0 s0 v0 l0))">
注意:不要将级别设置的太高。RSAC的评估系统提供了一种用来评价Web站点内容的标准。用户可以设置Microsoft Internet Explorer(IE3.0以上)来排除包含有色情和暴力内容的站点。上面这个例子中的HTML取自Microsoft的主页。代码中的(n 0 s 0 v 0 l 0)表示该站点不包含不健康内容。级别的评定是由RSAC,即美国娱乐委员会的评级机构评定的,如果你想进一步了解RSAC评估系统的等级内容,或者你需要评价自己的网站,可以访问RSAC的站点:http://www.rsac.org/。
8、Page-Enter、Page-Exit (进入与退出)
说明:这个是页面被载入和调出时的一些特效。
用法:<Meta http-equiv="Page-Enter" Content="blendTrans(Duration=0.5)">
<Meta http-equiv="Page-Exit" Content="blendTrans(Duration=0.5)">
注意:blendTrans是动态滤镜的一种,产生渐隐效果。另一种动态滤镜RevealTrans也可以用于页面进入与退出效果:
<Meta http-equiv="Page-Enter" Content="revealTrans(duration=x, transition=y)">
<Meta http-equiv="Page-Exit" Content="revealTrans(duration=x, transition=y)">
Duration 表示滤镜特效的持续时间(单位:秒)
Transition 滤镜类型。表示使用哪种特效,取值为0-23。
0 矩形缩小
1 矩形扩大
2 圆形缩小
3 圆形扩大
4 下到上刷新
5 上到下刷新
6 左到右刷新
7 右到左刷新
8 竖百叶窗
9 横百叶窗
10 错位横百叶窗
11 错位竖百叶窗
12 点扩散
13 左右到中间刷新
14 中间到左右刷新
15 中间到上下
16 上下到中间
17 右下到左上
18 右上到左下
19 左上到右下
20 左下到右上
21 横条
22 竖条
23 以上22种随机选择一种
9、MSThemeCompatible (XP主题)
说明:是否在IE中关闭 xp 的主题
用法:<Meta http-equiv="MSThemeCompatible" Content="Yes">
注意:关闭 xp 的蓝色立体按钮系统显示样式,从而和win2k 很象。
10、IE6 (页面生成器)
说明:页面生成器generator,是ie6
用法:<Meta http-equiv="IE6" Content="Generator">
注意:用什么东西做的,类似商品出厂厂商。
11、Content-Script-Type (脚本相关)
说明:这是近来W3C的规范,指明页面中脚本的类型。
用法:<Meta http-equiv="Content-Script-Type" Content="text/javascript">
★NAME变量
name是描述网页的,对应于Content(网页内容),以便于搜索引擎机器人查找、分类(目前几乎所有的搜索引擎都使用网上机器人自动查找meta值来给网页分类)。
name的value值(name="")指定所提供信息的类型。有些值是已经定义好的。例如description(说明)、keyword(关键字)、refresh(刷新)等。还可以指定其他任意值,如:creationdate(创建日期) 、
document ID(文档编号)和level(等级)等。
name的content指定实际内容。如:如果指定level(等级)为value(值),则Content可能是beginner(初级)、intermediate(中级)、advanced(高级)。
1、Keywords (关键字)
说明:为搜索引擎提供的关键字列表
用法:<Meta name="Keywords" Content="关键词1,关键词2,关键词3,关键词4,……">
注意:各关键词间用英文逗号“,”隔开。META的通常用处是指定搜索引擎用来提高搜索质量的关键词。当数个META元素提供文档语言从属信息时,搜索引擎会使用lang特性来过滤并通过用户的语言优先参照来显示搜索结果。例如:
<Meta name="Kyewords" Lang="EN" Content="vacation,greece,sunshine">
<Meta name="Kyewords" Lang="FR" Content="vacances,grè:ce,soleil">
2、Description (简介)
说明:Description用来告诉搜索引擎你的网站主要内容。
用法:<Meta name="Description" Content="你网页的简述">
注意:
3、Robots (机器人向导)
说明:Robots用来告诉搜索机器人哪些页面需要索引,哪些页面不需要索引。Content的参数有all、none、index、noindex、follow、nofollow。默认是all。
用法:<Meta name="Robots" Content="All|None|Index|Noindex|Follow|Nofollow">
注意:许多搜索引擎都通过放出robot/spider搜索来登录网站,这些robot/spider就要用到meta元素的一些特性来决定怎样登录。
all:文件将被检索,且页面上的链接可以被查询;
none:文件将不被检索,且页面上的链接不可以被查询;(和 "noindex, no follow" 起相同作用)
index:文件将被检索;(让robot/spider登录)
follow:页面上的链接可以被查询;
noindex:文件将不被检索,但页面上的链接可以被查询;(不让robot/spider登录)
nofollow:文件将不被检索,页面上的链接可以被查询。(不让robot/spider顺着此页的连接往下探找)
4、Author (作者)
说明:标注网页的作者或制作组
用法:<Meta name="Author" Content="张三,abc@sina.com">
注意:Content可以是:你或你的制作组的名字,或Email
5、Copyright (版权)
说明:标注版权
用法:<Meta name="Copyright" Content="本页版权归Zerospace所有。All Rights Reserved">
注意:
6、Generator (编辑器)
说明:编辑器的说明
用法:<Meta name="Generator" Content="PCDATA|FrontPage|">
注意:Content="你所用编辑器"
7、revisit-after (重访)
说明:
用法:<META name="revisit-after" CONTENT="7 days" >
PostgreSQL支持管理员直接手动安装数据库,给用户提供了更大的方便。
1. 在PostgreSQL官方网站上下载免安装二进制的包,名字类似于postgresql-*.*.*.*-binaries-no-installer.tar.gz之类的。
下载到本地,解压到某路径PATH下。
2. 在PATH目录中创建data文件夹,用于存放数据。
3. 开始-->运行-->cmd 进入命令行。并cd到PATH\bin目录下执行下面的命令用来初始化数据库:
PATH\bin> initdb.exe -D PATH\data -E UTF8 --locale=C
4. 将pgsql注册为windows服务,便于操作和控制:
PATH\bin> pg_ctl.exe register -D PATH\data -N pgsql
其中-N参数用来设置pgsql作为windows服务的名称。
5. 启动和关闭数据库,在命令行下:
> net start pgsql (开启数据库)
> net stop pgsql (关闭数据库)
注:为避免以后可能发生的亚州大字符集的乱码问题,统一使用UTF8编码。
需要一个非管理员帐号来运行PostgreSQL?
当一个骇客通过软件的缺陷获得了侵入一台计算机的入口时,她获得的是这个程序运行所用用户帐号的对应权限。由于我们无法预知PostgreSQL中是否还存在这样的bug,所以我们强制使用一个非管理员的服务帐号来最小化潜在的骇客利用此类漏洞对系统进行破坏的风险。这样的设置已是Unix界的惯例做法,同时在Windows世界中,Microsoft以及其他供应商也开始采用这样的做法来改进他们系统的安全性。补充: 自PostgreSQL 8.2发行后,从管理帐号启动变得可行。PostgreSQL 8.2及后续版本会在启动后不可撤销地放弃管理权限,从而保证了当极端不可能事件,当PostgreSQL受到入侵时,系统的安全性。
<script type="text/javascript">
//** iframe自动适应页面 **//
//输入你希望根据页面高度自动调整高度的iframe的名称的列表
//用逗号把每个iframe的ID分隔. 例如: ["myframe1", "myframe2"],可以只有一个窗体,则不用逗号。
//定义iframe的ID
var iframeids=["test"]
//如果用户的浏览器不支持iframe是否将iframe隐藏 yes 表示隐藏,no表示不隐藏
var iframehide="yes"
function dyniframesize()
{
var dyniframe=new Array()
for (i=0; i<iframeids.length; i++)
{
if (document.getElementById)
{
//自动调整iframe高度
dyniframe[dyniframe.length] = document.getElementById(iframeids[i]);
if (dyniframe[i] && !window.opera)
{
dyniframe[i].style.display="block"
if (dyniframe[i].contentDocument && dyniframe[i].contentDocument.body.offsetHeight) //如果用户的浏览器是NetScape
dyniframe[i].height = dyniframe[i].contentDocument.body.offsetHeight;
else if (dyniframe[i].Document && dyniframe[i].Document.body.scrollHeight) //如果用户的浏览器是IE
dyniframe[i].height = dyniframe[i].Document.body.scrollHeight;
}
}
//根据设定的参数来处理不支持iframe的浏览器的显示问题
if ((document.all || document.getElementById) && iframehide=="no")
{
var tempobj=document.all? document.all[iframeids[i]] : document.getElementById(iframeids[i])
tempobj.style.display="block"
}
}
}
if (window.addEventListener)
window.addEventListener("load", dyniframesize, false)
else if (window.attachEvent)
window.attachEvent("onload", dyniframesize)
else
window.onload=dyniframesize
</script>
使用的时候只要贴在<head></head>里面就可以
欢迎各位大侠加入IT精英俱乐部(QQ群:11672321)
一、使用mod_jk.so方式
1.无集群方式
1)下载、安装Apache2.2.11,Tomcat6.0.20略
2)下载mod_jk-1.2.28-httpd-2.2.3.so改名为mod_jk.so放到%APACHE_HOME%\modules下(不改名会出错,不知道为什么)
3)打开%APACHE_HOME%\conf\httpd.conf,找到最末一个Include节点,加入mod-jk_nocluster.conf
4)在%APACHE_HOME%\conf\新建mod-jk_nocluster.conf,加入以下内容:
LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers_nocluster.properties
JkLogFile logs/mod_jk_nocluster.log
JkLogLevel info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"
JkMount /* ajp13
#关掉主机Lookup,如果为on,很影响性能,可以有10多秒钟的延迟。
HostnameLookups Off
5)在%APACHE_HOME%\conf\新建workers_nocluster.properties,加入以下内容:
worker.list=ajp13
worker.maintain=60
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.lbfactor=1
6)启动Apache,tomcat,在地址栏输入http://localhost看到tomcat画面成功了
2.使用集群(以两个tomcat为例)
1)、2)同上
3)打开%APACHE_HOME%\conf\httpd.conf,找到最末一个Include节点,更改mod-jk_nocluster.conf为mod-jk_cluster.conf
4)在%APACHE_HOME%\conf\新建mod-jk_cluster.conf,加入以下内容:
LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers_cluster.properties
JkLogFile logs/mod_jk_cluster.log
JkLogLevel info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"
JkMount /* controller
HostnameLookups Off
5)在%APACHE_HOME%\conf\新建workers_cluster.properties,加入以下内容:
worker.list = controller,tomcat1,tomcat2 #server 列表
#========tomcat1========
worker.tomcat1.port=18109 #ajp13 端口号,在tomcat下server.xml配置,默认8009
worker.tomcat1.host=localhost #tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor = 1 #server的加权比重,值越高,分得的请求越多
#worker.tomcat1.redirect=tomcat2 #是在cluster环境之下,当tomcat1挂点或无回应,jk会将request导向这个指令指定的其他worker作处理。
#========tomcat2========
worker.tomcat2.port=18209 #ajp13 端口号,在tomcat下server.xml配置,默认8009
worker.tomcat2.host=localhost #tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor = 1 #server的加权比重,值越高,分得的请求越多
#worker.tomcat2.activation=disabled #
#========controller,负载均衡控制器========
worker.controller.type=lb
worker.controller.balanced_workers=tomcat1,tomcat2 #指定分担请求的tomcat
worker.controller.sticky_session=1
6)修改tomcat1配置文件server.xml,tomcat2配置修改地方同tomcat1,仅需注意相关端口号,在同一台服务器上时不能重复
以下列出需修改节点地方:
#默认为8005
<Server port="8105" shutdown="SHUTDOWN">
#默认8080,此处可根据需求修改线程并发等
<Connector port="8180" ...>
#默认8009,
<Connector port="8109" protocol="AJP/1.3" redirectPort="8443" />
#name可为Standalone
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="6">
<Manager
className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false" notifyListenersOnReplication="true"
mapSendOptions="6" />
<!--
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
-->
<Channel
className="org.apache.catalina.tribes.group.GroupChannel">
<Membership
className="org.apache.catalina.tribes.membership.McastService"
mcastBindAddress="127.0.0.1" #安装了VPN、svn等,会导致绑定失败需加上此句
address="228.0.0.4" port="45564"
frequency="500" dropTime="3000" />
<Receiver
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4001" selectorTimeout="100" maxThreads="6" />
<Sender
className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
</Sender>
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" />
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor" />
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;" />
<ClusterListener
className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>
在<Host>节点加入
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/xxx/"
deployDir="/tmp/war-deploy/xxx/"
watchDir="/tmp/war-listen/xxx/"
watchEnabled="false"/>
7)将Web应用打成war包放到每一个webapps下,确保web.xml中加了
<display-name>xxx<display-name>
<distributable />
或者直接放到tomcat的web.xml中
至此mod-jk.so方式全配好了,启动服务就OK了!
待续Apache新增的配置方式。。。。。。
1.安装Apache-httpd-2.2.11
2.下载svn-win32-1.6.3.zip for Apache-httpd-2.2.11,解压到某个目录
3.添加环境变量%SVN_HOME%,修改%PATH%,增加%SVN_HOME%\bin
4.新建空目录x:\Respository\test\
5.进入命令行:svnadmin create --fs-type fsfs x:\Respository\test\
6.进入x:\Respository\test\conf\svnserve.conf
去掉注释
auth-access = write
password-db = passwd
在本目录passwd文件中新建用户 xxx = xxx
7.新增服务
sc create svnservice binPath= "%SVN_HOME%\bin\svnserve.exe --service -r x:\Respository" displayname= "SVNService" depend= Tcpip start= auto
删除服务运行"sc delete svnservice"
8.复制%SVN_HOME%\bin中的文件mod_dav_svn.so和mod_authz_svn.so到%APACHE_HOME%\modules目录,
复制%SVN_HOME%\bin\libdb44.dll、libeay32.dll、 ssleay32.dll到%APACHE_HOME%\bin
9.%APACHE_HOME%\conf\httd.conf做如下修改:
去掉以下行的注释(将开头的#删除):
#LoadModule dav_fs_module modules/mod_dav_fs.so
#LoadModule dav_module modules/mod_dav.so
在LoadModule节的最后添加以下两行:
LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so
在文件末尾加入:
<Location /svn>
DAV svn
SVNParentPath x:\Respository
AuthType Basic
AuthName "Subversion repositories"
AuthUserFile x:\Respository\conf\passwd
#AuthzSVNAccessFile x:\Respository\conf\authz
Require valid-user
</Location>
可是现在启动不了Apache ,那位大侠能帮帮我,网上找了好多都试过了还是不行
<script language="javascript">
var oPopup=null; //弹出菜单
var popWidth=110; //弹出菜单的宽度
var popHeight=140; //弹出菜单的高度
var curRow=null; //记录弹出菜单最后指向的行
function init(){
oPopup = window.createPopup();
var oPopBody = oPopup.document.body;
//设置菜单样式
oPopBody.style.backgroundColor = "scrollbar";
oPopBody.style.border = "2px solid";
oPopBody.style.borderColor = "buttonhighlight buttonshadow buttonshadow buttonhighlight"
var strHTML=""
strHTML+='<table oncontextmenu="return false;" onselectstart="return false;" id="tbMenu" ';
strHTML+=' style="cursor:default; width:100%; height:100%;font-size:12px;" border=0 cellpadding=0 cellspacing=2>';
//在这里扩展菜单的选项start
strHTML+='<tr operation="edit"><td> 编辑</td></tr>';
strHTML+='<tr operation="refresh"><td> 刷新</td></tr>';
strHTML+='<tr operation="addrowup"><td> 添加新行(上)</td></tr>';
strHTML+='<tr operation="addrowdown"><td> 添加新行(下)</td></tr>';
strHTML+='<tr operation="delrow"><td> 删除该行</td></tr>';
strHTML+='<tr operation="moveup"><td> 向上移动一行</td></tr>';
strHTML+='<tr operation="movedown"><td> 向下移动一行</td></tr>';
//在这里扩展菜单的选项end
strHTML+='</table>';
oPopBody.innerHTML=strHTML;
var tb = oPopup.document.getElementById("tbMenu");
var rs=tb.rows;
for(var i=0;i<rs.length;i++){
var row=rs[i];
addEvent(row);
}
}
function showMenu(){
var e=window.event;
var src=e.srcElement;
oPopup.show(window.event.clientX, window.event.clientY, popWidth, popHeight, document.body);
window.event.returnValue=false;
}
function addEvent(row){
row.attachEvent("onmouseover",function(){selRow(row)});
row.attachEvent("onclick",function(){onEvent(row)});
}
function selRow(src){
if(curRow!=null){ curRow.style.backgroundColor="scrollbar"; curRow.style.color="black";}
curRow=src; curRow.style.backgroundColor="midnightblue"; curRow.style.color="white";
}
function onEvent(src){
oPopup.hide();
switch(src.operation){
//在这里为菜单项的点击事件添加处理方法
//本示例的方法只供参考,没有实现
case "edit": example(src.operation); break;
case "refresh": example(src.operation); break;
case "addrowup": example(src.operation); break;
case "addrowdown": example(src.operation); break;
case "delrow": example(src.operation); break;
case "moveup": example(src.operation); break;
case "movedown": example(src.operation); break;
default: return;
}
src.style.backgroundColor="scrollbar";
src.style.color="black";
curRow=null;
}
function example(str){
alert("您选择了"+str+"操作!");
}
</script>
<body onload="init();showMenu()">
</body>
我们在数据库插入一条数据的时候,经常是需要返回插入这条数据的主键。但是数据库供应商之间生成主键的方式都不一样。
有些是预先生成(pre-generate)主键的,如Oracle和PostgreSQL;有些是事后生成(post-generate)主键的,如MySQL和SQL Server。但是不管是哪种方式,我们都可以用iBATIS的节点来获取语句所产生的主键。
例子如下:
xml 代码
<insert id="insertProduct-ORACLE" parameterClass="product">
<selectKey resultClass="int" type="pre" keyProperty="Id" >
SELECT YOURPKSEQUENCE.NEXTVAL AS VALUE FROM DUAL
<selectKey>
insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)
<insert>
<insert id="insertProduct-MS-SQL" parameterClass="product">
insert into PRODUCT (PRD_DESCRIPTION) values (#description#)
<selectKey resultClass="int" type="post" keyProperty="id" >
select @@IDENTITY as value
<selectKey>
<insert>
<insert id="insertProduct-MYSQL" parameterClass="product">
insert into PRODUCT (PRD_DESCRIPTION) values (#description#)
<selectKey resultClass="int" type="post" keyProperty="id" >
select LAST_INSERT_ID() as value
<selectKey>
<insert>
alter database datafile 'D:\DevTools\Oracle\oradata\ora92\USERS01.DBF' offline drop;
alter database open;
在Oracle9i中执行下面的SQL重建exu81rls视图即可。
CREATE OR REPLACE view exu81rls
(objown,objnam,policy,polown,polsch,polfun,stmts,chkopt,enabled,spolicy)
AS select u.name, o.name, r.pname, r.pfschma, r.ppname, r.pfname,
decode(bitand(r.stmt_type,1), 0,'', 'SELECT,')
|| decode(bitand(r.stmt_type,2), 0,'', 'INSERT,')
|| decode(bitand(r.stmt_type,4), 0,'', 'UPDATE,')
|| decode(bitand(r.stmt_type,8), 0,'', 'DELETE,'),
r.check_opt, r.enable_flag,
DECODE(BITAND(r.stmt_type, 16), 0, 0, 1)
from user$ u, obj$ o, rls$ r
where u.user# = o.owner#
and r.obj# = o.obj#
and (uid = 0 or
uid = o.owner# or
exists ( select * from session_roles where role='SELECT_CATALOG_ROLE')
)
/
grant select on sys.exu81rls to public;
/
-- Change the HTTP/WEBDAV port from 8080 to 8081
SQL> call dbms_xdb.cfg_update(updateXML( dbms_xdb.cfg_get() ,
'/xdbconfig/sysconfig/protocolconfig/httpconfig/http-port/text()'
8081))
/
-- Change the FTP port from 2100 to 2111
SQL> call dbms_xdb.cfg_update(updateXML( dbms_xdb.cfg_get() ,
'/xdbconfig/sysconfig/protocolconfig/ftpconfig/ftp-port/text()' , 2111))
SQL> COMMIT;
SQL> EXEC dbms_xdb.cfg_refresh
@echo off
if '%1=='## goto ENVSET
rem 要启动的类名
SET CLSNAME=com.main
rem 设定CLSPATH
SET CLSPATH=.
FOR %%c IN (.\lib\*.jar) DO CALL %0 ## %%c
rem 运行
GOTO RUN
:RUN
java -cp %CLSPATH% %CLSNAME%
goto END
:ENVSET
set CLSPATH=%CLSPATH%;%2
goto END
:END
在query的时候加上binary,select * from usertest where username like binary '%夏%'
一、同步问题提出
线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。
public class Foo {
private int x = 100;
public int getX() {
return x;
}
public int fix(int y) {
x = x - y;
return x;
}
}
public class MyRunnable implements Runnable {
private Foo foo = new Foo();
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread ta = new Thread(r, "Thread-A");
Thread tb = new Thread(r, "Thread-B");
ta.start();
tb.start();
}
public void run() {
for (int i = 0; i < 3; i++) {
this.fix(30);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());
}
}
public int fix(int y) {
return foo.fix(y);
}
}
运行结果:
Thread-A : 当前foo对象的x值= 40
Thread-B : 当前foo对象的x值= 40
Thread-B : 当前foo对象的x值= -20
Thread-A : 当前foo对象的x值= -50
Thread-A : 当前foo对象的x值= -80
Thread-B : 当前foo对象的x值= -80
Process finished with exit code 0
从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。
如果要保持结果的合理性,只需要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。
在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源类Foo变量x标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
二、同步和锁定
1、锁的原理
Java中每个对象都有一个内置锁
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:
public int fix(int y) {
synchronized (this) {
x = x - y;
}
return x;
}
当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:
public synchronized int getX() {
return x++;
}
与
public int getX() {
synchronized (this) {
return x;
}
}
效果是完全一样的。
三、静态方法同步
要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。
例如:
public static synchronized int setName(String name){
Xxx.name = name;
}
等价于
public static int setName(String name){
synchronized(Xxx.class){
Xxx.name = name;
}
}
四、如果线程不能不能获得锁会怎么样
如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。
当考虑阻塞时,一定要注意哪个对象正被用于锁定:
1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。
五、何时需要同步
在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。
对于非静态字段中可更改的数据,通常使用非静态方法访问。
对于静态字段中可更改的数据,通常使用静态方法访问。
如果需要在非静态方法中使用静态字段,或者在静态字段中调用非静态方法,问题将变得非常复杂。已经超出SJCP考试范围了。
六、线程安全类
当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。
即使是线程安全类,也应该特别小心,因为操作的线程是间仍然不一定安全。
举个形象的例子,比如一个集合是线程安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作,也许在第一个线程查询后,第二个线程也查询出集合非空,但是当第一个执行清除后,第二个再执行删除显然是不对的,因为此时集合已经为空了。
看个代码:
public class NameList {
private List nameList = Collections.synchronizedList(new LinkedList());
public void add(String name) {
nameList.add(name);
}
public String removeFirst() {
if (nameList.size() > 0) {
return (String) nameList.remove(0);
} else {
return null;
}
}
}
public class Test {
public static void main(String[] args) {
final NameList nl = new NameList();
nl.add("aaa");
class NameDropper extends Thread{
public void run(){
String name = nl.removeFirst();
System.out.println(name);
}
}
Thread t1 = new NameDropper();
Thread t2 = new NameDropper();
t1.start();
t2.start();
}
}
虽然集合对象
private List nameList = Collections.synchronizedList(new LinkedList());
是同步的,但是程序还不是线程安全的。
出现这种事件的原因是,上例中一个线程操作列表过程中无法阻止另外一个线程对列表的其他操作。
解决上面问题的办法是,在操作集合对象的NameList上面做一个同步。改写后的代码如下:
public class NameList {
private List nameList = Collections.synchronizedList(new LinkedList());
public synchronized void add(String name) {
nameList.add(name);
}
public synchronized String removeFirst() {
if (nameList.size() > 0) {
return (String) nameList.remove(0);
} else {
return null;
}
}
}
这样,当一个线程访问其中一个同步方法时,其他线程只有等待。
七、线程死锁
死锁对Java程序来说,是很复杂的,也很难发现问题。当两个线程被阻塞,每个线程在等待另一个线程时就发生死锁。
还是看一个比较直观的死锁例子:
public class DeadlockRisk {
private static class Resource {
public int value;
}
private Resource resourceA = new Resource();
private Resource resourceB = new Resource();
public int read() {
synchronized (resourceA) {
synchronized (resourceB) {
return resourceB.value + resourceA.value;
}
}
}
public void write(int a, int b) {
synchronized (resourceB) {
synchronized (resourceA) {
resourceA.value = a;
resourceB.value = b;
}
}
}
}
假设read()方法由一个线程启动,write()方法由另外一个线程启动。读线程将拥有resourceA锁,写线程将拥有resourceB锁,两者都坚持等待的话就出现死锁。
实际上,上面这个例子发生死锁的概率很小。因为在代码内的某个点,CPU必须从读线程切换到写线程,所以,死锁基本上不能发生。
但是,无论代码中发生死锁的概率有多小,一旦发生死锁,程序就死掉。有一些设计方法能帮助避免死锁,包括始终按照预定义的顺序获取锁这一策略。已经超出SCJP的考试范围。
八、线程同步小结
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
测试通过的版本如下:
Eclipse:3.3.2
jdk:1.6
junit:3.8
ant:1.7(1.7之前的版本好像还不提供mail功能。。。)
<!-- JUnit build script using ant 1.7 -->
<project name="JunitTestProject" default="mail" basedir=".">
<property name="app.name" value="JunitTestProject" />
<property name="build.dir" value="bin" />
<!-- ====================" path define " ================================ -->
<path id="cobertura.classpath">
<fileset dir="lib/coberture">
<include name="cobertura.jar" />
<include name="*.jar" />
</fileset>
</path>
<!-- ====================" cobertura task define " Target ================================ -->
<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
<!-- ==================== "clean " Target ================================ -->
<target name="clean">
<available property="junit.present" classname="junit.framework.TestCase" />
<delete dir="${build.dir}" quiet="true" />
<delete file="report" quiet="true" />
</target>
<!-- ==================== "copy xml resource " Target ================================ -->
<target name="copyxml" depends="clean">
<copy todir="${build.dir}/testcases">
<fileset dir="WEB-INF" />
</copy>
</target>
<!-- ==================== "compile src" Target ================================ -->
<target name="compile" depends="copyxml">
<mkdir dir="${build.dir}" />
<javac srcdir="src" destdir="${build.dir}" debug="yes">
<classpath>
<fileset dir="lib" casesensitive="yes">
<include name="**/*.jar" />
</fileset>
</classpath>
<include name="**/*.java" />
</javac>
<javac srcdir="WEB-INF" destdir="${build.dir}">
<classpath>
<fileset dir="lib" casesensitive="yes">
<include name="**/*.jar" />
</fileset>
</classpath>
<include name="*.jsp" />
</javac>
</target>
<!-- ==================== jar" Target ================================ -->
<target name="jar" depends="compile">
<mkdir dir="dist/lib" />
<jar jarfile="dist/lib/${app.name}.jar" basedir="${build.dir}" includes="com/**" />
</target>
<!-- ==================== compile test src" Target ================================ -->
<target name="compiletests" depends="jar">
<mkdir dir="${build.dir}/testcases" />
<javac srcdir="test" destdir="${build.dir}/testcases" >
<classpath>
<fileset dir="lib" casesensitive="yes">
<include name="**/*.jar" />
</fileset>
<fileset dir="dist/lib" casesensitive="yes">
<include name="**/*.jar" />
</fileset>
</classpath>
<include name="**/*.java" />
</javac>
</target>
<!-- ==================== instrumented" Target ================================ -->
<target name="instrumented" depends="compiletests">
<cobertura-instrument todir="bin/instrumented-classes">
<ignore regex="org.apache.log4j.*" />
<fileset dir="bin">
<include name="com/**/*.class" />
</fileset>
</cobertura-instrument>
</target>
<!-- ==================== junit-test" Target ================================ -->
<target name="runtests" depends="instrumented">
<mkdir dir="report" />
<property name="tests" value="*Test" />
<junit printsummary="yes" haltonerror="yes" haltonfailure="yes" fork="yes">
<formatter type="plain" usefile="false" />
<formatter type="xml" />
<batchtest todir="report">
<fileset dir="test">
<include name="**/${tests}.java" />
<exclude name="**/ConfigTest.java" />
</fileset>
</batchtest>
<!--
Note the classpath order: instrumented classes are before the
original (uninstrumented) classes. This is important.
-->
<classpath location="bin/instrumented-classes" />
<!--
src classpath
-->
<classpath location="bin" />
<!--
The instrumented classes reference classes used by the
Cobertura runtime, so Cobertura and its dependencies
must be on your classpath.
-->
<classpath refid="cobertura.classpath" />
<!--
test case class path define
-->
<classpath>
<fileset dir="lib" casesensitive="yes">
<include name="**/*.jar" />
</fileset>
<pathelement location="bin/testcases" />
</classpath>
</junit>
</target>
<!-- ==================== junit-report" Target ================================ -->
<target name="report" depends="runtests">
<mkdir dir="report/html" />
<junitreport todir="report">
<fileset dir="report">
<include name="TEST-*.xml" />
</fileset>
<report todir="report/html" />
</junitreport>
</target>
<!-- ==================== "coverage-report" Target ================================ -->
<target name="coverage-report" depends="report">
<cobertura-report format="html" destdir="cobertura" >
<fileset dir="src">
<include name="**/*.java" />
</fileset>
</cobertura-report>
<echo>The execution of coverage-report is complete. Coverage Reports are available in /${coverage-report}</echo>
</target>
<!-- ==================== "make file to zip" Target ================================ -->
<target name="make_data_zip" depends="coverage-report">
<tstamp>
<format property="date" pattern="yyyy-MM-dd HH-mm" />
</tstamp>
<jar jarfile="dist/lib/cobertura${date}.zip" basedir="cobertura" />
<jar jarfile="dist/lib/report${date}.zip" basedir="report" excludes="*.xml"/>
</target>
<!-- ==================== "mail" Target ================================ -->
<target name="mail" depends="make_data_zip">
<!-- <taskdef name="mail" classname="org.apache.tools.ant.taskdefs.optional.mail.MimeMail"/> -->
<tstamp/>
<mail messageMimeType="text/html"
messageFile="message.txt"
tolist="bpcjy@hotmail.com"
mailhost="mailsvr or mail IPAddress"
subject="JUnit Test Results: ${TODAY}"
from="bpcjy@hotmail.com">
<fileset dir=".">
<include name="dist/lib/*.zip"/>
</fileset>
</mail>
</target>
</project>
struts2.0:(6)
lib/antlr-2.7.6.jar
lib/struts2-core-2.0.14.jar
lib/struts2-spring-plugin-2.0.14.jar
lib/freemarker-2.3.8.jar
lib/ognl-2.6.11.jar
lib/xwork-2.0.7.jar
(注意单用时必须还要用lib/commons-logging-1.0.4.jar)
spring2.5:(10)
dist/modules/spring-aop.jar
dist/modules/spring-beans.jar
dist/modules/spring-context.jar
dist/modules/spring-context-support.jar
dist/modules/spring-core.jar
dist/modules/spring-jdbc.jar
dist/modules/spring-orm.jar
dist/modules/spring-tx.jar
dist/modules/spring-web.jar
dist/modules/spring-webmvc-struts.jar
hibernate3.2:(10)
hibernate3.jar
lib/asm.jar
lib/asm-attrs.jar
lib/cglib-2.1.3.jar
lib/commons-collections-2.1.1.jar
lib/commons-logging-1.0.4.jar
lib/dom4j-1.6.1.jar
lib/ehcache-1.2.3.jar
lib/jta.jar
lib/xml-apis.jar
其它jar:(6)
servlet.jar
sqljdbc.jar
jsonplugin-0.30.jar(jsonplugin-0.30于6月6号发布,修复了一直很头疼的cglib序列化问题)
serializer.jar
xalan.jar
(Xalan是将可扩展标记语言(XML)转换为超文本链接标识语言(HTML)或其它类型XML文档的规范,添加操作XML的jar,一般要添加xalan.jar、serializer.jar这两个包,如有特殊需求可将下载的所有jar全部添加
(下载地址:http://apache.seekmeup.com/xml/xalan-j里面的一个xalan-j_2_7_0-bin.zip))
xercesImpl.jar
( 实现解析XML文件的功能很方便,我们可以通过下载第三方的一些工具包如xml-apis.jar和xercesImpl.jar 等.Xerces是一个与可扩展标记语言(XML)兼容的语法分析器。Xerces分析器可处理Java和C++,它采用互联网联盟XML、文件对象...Xerces-C是用可移植的C++子集编写的XML分析器。Xerces-C允许对XML数据进行读写操作)