2008年4月1日
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
-
IDE Settings
IDE settings are stored in the dedicated directories under the product home directory, depending on the platform. The product home directory name is composed of the product name and version.
For IntelliJ IDEA Community edition the folder name is .IdeaICXX.
For example:
- Windows
-
- <User home>\.IntelliJIdeaXX\config that contains user-specific settings.
- <User home>\.IntelliJIdeaXX\system that stores IntelliJ IDEA data caches.
<User home> in WindowsXP is C:\Documents and Settings\<User name>\; in Windows Vista it is C:\Users\<User name>\
- Linux
-
- ~/.IntelliJIdeaXX/config that contains user-specific settings.
- ~/.IntelliJIdeaXX/system that stores IntelliJ IDEA data caches.
- Mac OS
-
- ~/Library/Application Support/IntelliJIdeaXX contains the catalog with plugins.
- ~/Library/Preferences/IntelliJIdeaXX contains the rest of the configuration settings.
- ~/Library/Caches/IntelliJIdeaXX contains data caches, logs, local history, etc. These files can be quite significant in size.
- 9.0+~/Library/Logs/IntelliJIdeaXX contains logs
The config directory has several subfolders that contain xml files with your personal settings. You can easily share your preferred keymaps, color schemes, etc. by copying these files into the corresponding folders on another IntelliJ IDEA installation. Prior to copying, make sure that IntelliJ IDEA is not running, because it can erase the newly transferred files before shutting down.
The following is the list of some of the subfolders under the config folder, and the settings contained therein.
Locations of the Config, System, and Plugins directories can be modified in IntelliJ IDEA_home\bin\idea.properties file.
You will need to adjust the following parameters:
- idea.config.path
- idea.system.path
- idea.plugins.path
Increasing productivity
To increase productivity of IntelliJ IDEA, you can change settings that reside in the following locations (depending on your operating system):
Example. increasing heap size
For example, to increase IntelliJ IDEA heap size, you should copy the original .vmoptions file from /Applications/IntelliJ IDEA.app/bin/idea.vmoptions to~/Library/Preferences/IntelliJIdeaXX/idea.vmoptions, then modify the -Xmx setting.
For the older versions, the settings are stored in:
/Applications/IntelliJ IDEA.app/Contents/Info.plist
Managing case of unicode literals
11.1+
IntelliJ IDEA allows defining whether non-ascii characters should use literals like '\u00AB' or '\00ab'.
This behavior is controlled by the system property idea.native2ascii.lowercase. By default, upper case characters are used.
If it is desirable to use lower case characters, do the following (depending on your platform)
- 12.0+On Windows and *NIX: add the line
idea.native2ascii.lowercase=true
to the bin/idea.properties file, located under the product installation.
- On Mac OS:
Copy the file /Applications/IntelliJ IDEA.app/bin/idea.properties to ~/Library/Preferences/IntelliJIdeaXX/, open it for editing, and add the line
idea.native2ascii.lowercase=true
It is essential to create a copy, since the settings are replaced rather than added.
For the older versions of IntelliJ IDEA, open for editing the file /Applications/IntelliJ IDEA.app/Contents/Info.plist, and add the following code:
<key>idea.native2ascii.lowercase</key>
<string>true</string>
to the section <key>Properties</key> <dict> ... <dict>
To change IDEA running JDK, set <JVMVersion> to 1.7* in /Applications/IntelliJ IDEA 13.app/Contents/Info.plist
VM arguments settings:
-Xms512m
-Xmx512m
-Xmn164m
-XX:MaxPermSize=250m
-XX:ReservedCodeCacheSize=64m
-Xverify:none
-Xnoclassgc
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=85
-ea
from: http://blog.braegger.pw/5-ways-to-burn-out-programming/
I've only recently come out of my burnout, despite it happening years ago. It sucks. It sucks bad. But looking back, I can see many of the causes crystal clearly, that weren't so apparent at the time. Here's a list: 1. Think about your project and only the project Let's face it. Business wants you to make the best product you can "for our customers". You put off fun features for the sake of missing a deadline. You plan and analyze and break a project into sets of deliverables that then must be coded by a monkey (you). You demo it, gather feedback, iterate. All without thinking anything for yourself. But newsflash: you started programming because you thought it was fun, why not keep programming because it's fun? Take that little extra time to put in a feature you want. Challenge yourself a little bit in doing something you didn't think you could. Show it to everyone you know, and don't just ask for feedback, but brag about what you've done. 2. Have a negative attitude toward everything. You know Docker? It sucks. Who would trust their production environment to a new, unstable, toy. Go? Do I look like I want to write every library myself? Everything I need is already in PyPI. This project I'm working on is so caught up in office politics, it's never going to work. Jenkins? 2008 wants their tech back. It's really easy to fall into the "being critical" trap. It's easy to tell other people what the "wrong" choice is. I imagine it's because as software engineers, our job is so find faults in our applications and fix them. And if we don't find them, someone else finds them for us. But I don't think we need to be negative about our job, decisions that are being made (even if it's not our decision) and what we're working on. Some of the best projects I've worked on worked out that way because we had a great, positive team. We enjoyed showing up every day to work, told each other when we did awesome things, held back heavy-handed criticism and phrased it in a productive manner. 3. Use the tools you know, because you're faster that way So you're an uber expert in Java + Spring + Hibernate. Nobody can touch your python skillz. Every personal project you do should be in these, because all that matters is the business side of things, right? Wrong. While it definitely makes good business sense, you should prototype, play around, and become an expert in new tech, even if it's unvetted. While this might seem like obvious advice (it's repeated alllll the time), it becomes a lot harder to do as you grow more experienced. 4. Switch jobs often Otherwise known as "chasing butterflies". Getting bored with what you're working on? Have an itch? Time to dust off that resume! This is bad, bad, bad. When you have several short employments, it can usually help boost your salary quite a bit, but you are robbing yourself of: - Growing in the company (developer -> manager -> director)
- Gaining an expertise in a specific area. Considering it takes 4-6 years for a PhD student to get their PhD, that's a lot of time you need for learnin.
- You are having to start from scratch often.
- If you are a good developer, you have to "prove" yourself (people listen to you) all over again.
So how do these contribute to burnout? Your career stagnates, you don't develop your skills as deeply (only breadth), people dont trust you'll stay employed for a while, and you're constantly having to prove yourself. 5. Work long hours, ignore your life "You don't have to work a lot of hours, but some people choose to." You want to impress your boss. Hell, you want to impress yourself. So you go die-hard to meet an impossible deadline. You delivered the project on time, with all the extra features you wanted. You are the hero. High fives all around. And if you're lucky, you'll get that bonus. That's great the first time. But how about the second. And the third. It's a bomb, and you dont know how short the fuse is. Summary In short, it's easy to burnout. Do these 5 things, and you can burnout too.
0. 安装一系列支持工具
网络工具wget、curl、axel等
代码:
sudo apt-get install wget curl axel
其他常见工具
代码:
sudo apt-get install subversion git sysstat linux-base linux-tools-generic build-essential vim emacs unrar p7zip synaptic
安装常用数据库和NoSQL
代码:
sudo apt-get install mysql-client mysql-server mysql-workbench memcached redis-server
1. 首先添加webupd8的java源:
代码:
sudo add-apt-repository ppa:webupd8team/java
2. 更新源中的软件数据:
代码:
3. 安装java6或者java7, 当然也可以两者都装
代码:
sudo apt-get install oracle-java8-installer
sudo apt-get install oracle-java9-installer
4. 安装maven和ant
代码:
sudo apt-get install maven ant
本方法在UBUNTU 13.04版本中默认安装的是maven 3.0.4版本和ant 1.8版本。如有特殊版本嗜好,请自行到apache.org下载安装。
5. 配置Maven
功夫网内用户可参照下面的链接进行配置:
http://maven.oschina.net/help.html
其他地区用户可自行到股沟上问百度。
6. 下载安装一种主流 JAVA IDE。
(1) 下载安装 IntelliJ IDEA
访问, 根据需要选择版本。推荐选择Free 30-day trial的Ultimate版本,至于激活码嘛,问度娘,你懂的~
axel -n 10 "http://download.jetbrains.com/idea/ideaIU-14.1.3.tar.gz"
tar xzvf ideaIU-14.1.3.tar.gz
cd idea-IU-141.1010.3/bin
sh idea.sh
最后要提醒一句。对于在校学生,拥有.edu邮箱的朋友,可以用你们的.edu邮箱获取一年的jetbrains旗下所有IDE产品的一年免费使用权。详见
https://www.jetbrains.com/student/
一年过后,只要你的邮箱还能用,可以再次申请。
(2) 下载安装eclipse IDE。(alternative step)
访问http://www.eclipse.org/downloads/,根据需要选择IDE版本。推荐选择Eclipse IDE for Java EE Developers版本。可以根据系统情况选择安装32位或者64位
这里以下载64位为例
代码:
axel -n 10 "http://ftp.daum.net/eclipse//technology/epp/downloads/release/mars/R/eclipse-jee-mars-R-linux-gtk-x86_64.tar.gz" tar -zxvf eclipse-jee-luna-SR2-linux-gtk-x86_64.tar.gz
然后就是自己创建快捷方式到桌面之类的。
(3) 下载netbeans IDE. (alternative step)
访问www.netbeans.org,然后找到下载地址。
这里以下载8.0.2版本为例:
代码:
axel -n 10 "http://dlc-cdn.sun.com/netbeans/8.0.2/final/bundles/netbeans-8.0.2-linux.sh"
sh netbeans-8.0.2-linux.sh
把netbeans配置为全屏反锯齿模式:
代码:
cp netbeans.conf netbeans.conf.bak; awk -F'=' '{if($1=="netbeans_default_options"){print index($0,"useSystemAAFontSettings")?$0:substr($0,0,length($0))" -J-Dawt.useSystemAAFontSettings=on\""}else{print $0}}' netbeans.conf.bak > netbeans.conf
7. 如果想做快速原型或者玩一玩的话,也可以安装一下nodejs。很有意思。
代码:
sudo curl --silent --location https://deb.nodesource.com/setup_0.12 | sudo bash - sudo apt-get install nodejs
8. GraphDB 最近很火。装个neo4j试试看。
代码:
wget -O - http://debian.neo4j.org/neotechnology.gpg.key | apt-key add -
sudo echo 'deb http://debian.neo4j.org/repo stable/' > /etc/apt/sources.list.d/neo4j.list
sudo apt-get update
sudo apt-get install neo4j
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
eclipse官方是有标准的mac版下载的,随便copy到一个目录就可以直接启动了(当然必须安装java运行环境)。但是,假如我们想把应用放到mac的标准目录下(也就是finder里的应用程序目录下),就必须自己手动来做了,方法如下:
1.在finder下,copy eclipse目录下的Eclipse.app(在finder里是看不到app这个后缀名的,必须用Command+i,才能看到这个后缀名)文件到“应用程序”
2.然后,点击右键,选择“显示包内容”
3.修改eclipse.ini文件
修改文件的开头部分
原始内容为:
-startup ../../../plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
--launcher.library ../../../plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.200.v20130521-0416
修改后的内容为:
-startup /Users/johnny/work/eclipse/plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
--launcher.library /Users/johnny/work/eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.200.v20130521-0416
我mac的用户名johnny,在我的用户名下有个work目录,我把eclipse的原始目录放在work下面。
这样就可以在“应用程序”下,启动eclipse了
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
from: http://segmentfault.com/q/1010000000124379
如果你想从别的 Git 托管服务那里复制一份源代码到新的 Git 托管服务器上的话,可以通过以下步骤来操作。 1). 从原地址克隆一份裸版本库,比如原本托管于 GitHub。 git clone --bare git://github.com/username/project.git 2). 然后到新的 Git 服务器上创建一个新项目,比如 GitCafe。 3). 以镜像推送的方式上传代码到 GitCafe 服务器上。 cd project.git git push --mirror git@gitcafe.com/username/newproject.git 4). 删除本地代码 cd .. rm -rf project.git 5). 到新服务器 GitCafe 上找到 Clone 地址,直接 Clone 到本地就可以了。 git clone git@gitcafe.com/username/newproject.git 这种方式可以保留原版本库中的所有内容。
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
转自:http://blog.csdn.net/fenglibing/article/details/7083071
一个服务(service)通常指的是已知的接口或者抽象类,服务提供方就是对这个接口或者抽象类的实现,然后按spi标准存放到资源路径META-INF/services目录下,文件的命名为该服务接口的全限定名。如有一个服务接口com.test.Service,其服务实现类为com.test.ChildService,那此时需要在META-INF/services中放置文件com.test.Service,其中的内容就为该实现类的全限定名com.test.ChildService,有多个服务实现,每一行写一个服务实现,#后面的内容为注释,并且该文件只能够是以UTF-8编码。
这种实现方式,感觉和我们通常的开发方式差不多,都是定义一个接口,然后子类实现父类中定义的方法,为什么要搞这么一套标准以及单独搞一个配置文件?这种方式主要是针对不同的服务提供厂商,对不同场景的提供不同的解决方案制定的一套标准,举个简单的例子,如现在的JDK中有支持音乐播放,假设只支持mp3的播放,有些厂商想在这个基础之上支持mp4的播放,有的想支持mp5,而这些厂商都是第三方厂商,如果没有提供SPI这种实现标准,那就只有修改JAVA的源代码了,那这个弊端也是显而易见的,也就是不能够随着JDK的升级而升级现在的应用了,而有了SPI标准,SUN公司只需要提供一个播放接口,在实现播放的功能上通过ServiceLoad的方式加载服务,那么第三方只需要实现这个播放接口,再按SPI标准进行打包成jar,再放到classpath下面就OK了,没有一点代码的侵入性。
以下是找到的几篇文章:
1、http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html,这个是官方的文档,有对service的详细介绍,包括规范以及一个简单的示例,这个是学习SPI必须看的文档;
注:http://docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Service%20Provider,这个是1.4中对Service Provider的介绍,加载服务是通过sun.misc.Service进行加载的,这个也有相应的示例,照做就OK;
2、Java的SPI机制:http://www.2cto.com/kf/201012/79868.html,这个是国人写的一篇示例文章,也挺不错,里面也有一个简单的示例;
3、Developing a Service Provider using Java API(Service Provider Interface):http://blog.csdn.net/fenglibing/article/details/7083526,这篇文章是转的alexa发表在blogspot上面的,也是一个开发SPI的示例,有兴趣的也可以看看;
4、Add Mp3 capabilities to Java Sound with SPI:http://www.javaworld.com/javaworld/jw-11-2000/jw-1103-mp3.html,这是一个比较老的例子,基于jdk1.3的,因为在jdk1.3的时候还没有支持mp3格式,只支持AU, AIF, MIDI, and WAV等格式,也是一个值得参考的示例。
我这边也写了一个简单得不能够再简单的示例,源码可以这里下载:http://download.csdn.net/detail/fenglibing/3939882
最后很重要一点:
如果想要覆盖某个Provider,可以在对应的META-INF/services的配置文件中加上新service的一行,或者也可以写在另一个有依赖关系的jar包中,只要和原来的Provider命名不同即可。加载顺序上可以考虑使用@Priority注解来调整加载的优先级。
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
转自: http://blog.jobbole.com/59790/
对于系统和网络管理员来说每天监控和调试Linux系统的性能问题是一项繁重的工作。在IT领域作为一名Linux系统的管理员工作5年后,我逐渐 认识到监控和保持系统启动并运行是多么的不容易。基于此原因,我们已编写了最常使用的18个命令行工具列表,这些工具将有助于每个Linux/Unix 系统管理员的工作。这些命令行工具可以在各种Linux系统下使用,可以用于监控和查找产生性能问题的原因。这个命令行工具列表提供了足够的工具,您可以 挑选适用于您的监控场景的工具。 1.Top-Linux进程监控 Linux下的Top命令是一个性能监控程序,许多系统管理员常常用它来监控Linux性能,在许多Linux或者类Unix操作系统里都有这个命令。Top命令用于按一定的顺序显示所有正在运行而且处于活动状态的实时进程,而且会定期更新显示结果。这条命令显示了CPU的使用率、内存使用率、交换内存使用大小、高速缓存使用大小、缓冲区使用大小,进程PID、所使用命令以及其他。它还可以显示正在运行进程的内存和CPU占用多的情况。对系统管理员来说,top命令式是一个非常有用的,它可用于监控系统并在需要的时候采取正确的处理动作。让我们看看实际中的top命令。 # top Top命令举例 有关Top命令更多的例子,请阅读 :Linux下12个使用Top命令的例子。 2. VmStat – 虚拟内存统计 Linux 的 VmStat 命令用于显示虚拟内存、内核线程、磁盘、系统进程、I/O 块、中断、CPU 活动 等的统计信息。缺省情况下, vmstat 命令在 Linux 系统下不可用,你需要安装一个包含了 vmstat 程序的 sysstat 软件包。命令格式的常见用法是: 1 2 3 4 | # vmstat
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free inact active si so bi bo in cs us sy id wa st
1 0 0 810420 97380 70628 0 0 115 4 89 79 1 6 90 3 0
|
更多的 vmstat 例子,请阅读 : 6 Linux 下的 Vmstat 命令实例 3.Lsof-列出打开的文件 在许多Linux或者类Unix系统里都有lsof命令,它常用于以列表的形式显示所有打开的文件和进程。打开的文件包括磁盘文件、网络套接字、管道、设备和进程。使用这条命令的主要情形之一就是在无法挂载磁盘和显示正在使用或者打开某个文件的错误信息的时候。使用这条命令,你可以很容易地看到正在使用哪个文件。这条命令最常用的格式如下: 1 2 3 4 5 6 7 8 9 10 11 | # lsof
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
init 1 root cwd DIR 104,2 4096 2 /
init 1 root rtd DIR 104,2 4096 2 /
init 1 root txt REG 104,2 38652 17710339 /sbin/init
init 1 root mem REG 104,2 129900 196453 /lib/ld-2.5.so
init 1 root mem REG 104,2 1693812 196454 /lib/libc-2.5.so
init 1 root mem REG 104,2 20668 196479 /lib/libdl-2.5.so
init 1 root mem REG 104,2 245376 196419 /lib/libsepol.so.1
init 1 root mem REG 104,2 93508 196431 /lib/libselinux.so.1
init 1 root 10u FIFO 0,17 953 /dev/initctl
|
有关lsof命令的用法和例子的更多信息,请参考: Linux下10个使用lsof命令的例子。 4.Tcpdump-网络包分析器 Tcpdump是最广泛使用的网络包分析器或者包监控程序之一,它用于捕捉或者过滤网络上指定接口上接收或者传输的TCP/IP包。它还有一个选项用于把捕捉到的包保存到文件里,以便以后进行分析。在几乎所有主要的Linux发布里,tcpdump都可以使用。 1 2 3 4 5 | # tcpdump -i eth0tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
22:08:59.617628 IP tecmint.com.ssh > 115.113.134.3.static-mumbai.vsnl.net.in.28472: P 2532133365:2532133481(116) ack 3561562349 win 9648
22:09:07.653466 IP tecmint.com.ssh > 115.113.134.3.static-mumbai.vsnl.net.in.28472: P 116:232(116) ack 1 win 9648
22:08:59.617916 IP 115.113.134.3.static-mumbai.vsnl.net.in.28472 > tecmint.com.ssh: . ack 116 win 64347
|
要想获得更多有关tcpdump用法的信息,请参阅: Linux下12个使用Tcpdump命令的例子。 5.Netstat-网络状态统计 Netstat是一个用于监控进出网络的包和网络接口统计的命令行工具。它是一个非常有用的工具,系统管理员可以用来监控网络性能,定位并解决网络相关问题。 1 2 3 4 5 6 7 8 9 10 11 12 13 | # netstat -a | moreActive Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 *:mysql *:* LISTEN
tcp 0 0 *:sunrpc *:* LISTEN
tcp 0 0 *:realm-rusd *:* LISTEN
tcp 0 0 *:ftp *:* LISTEN
tcp 0 0 localhost.localdomain:ipp *:* LISTEN
tcp 0 0 localhost.localdomain:smtp *:* LISTEN
tcp 0 0 localhost.localdomain:smtp localhost.localdomain:42709 TIME_WAIT
tcp 0 0 localhost.localdomain:smtp localhost.localdomain:42710 TIME_WAIT
tcp 0 0 *:http *:* LISTEN
tcp 0 0 *:ssh *:* LISTEN
tcp 0 0 *:https *:* LISTEN
|
有关Netstat更多的例子,请参阅: Linux下20个使用Netstat命令的例子。 6. Htop – Linux进程监控 Htop 是一个非常高级的交互式的实时linux进程监控工具。 它和top命令十分相似,但是它具有更丰富的特性,例如用户可以友好地管理进程,快捷键,垂直和水平方式显示进程等等。 Htop是一个第三方工具,它不包含在linux系统中,你需要使用YUM包管理工具去安装它。 关于安装的更多信息,请阅读下文. # htop Htop 命令示例截图 对于Htop的安装,请读 : 在Linux安装Htop(Linux进程监控) 7.Iotop-监控Linux磁盘I/O Iotop命令同样也非常类似于top命令和Htop程序,不过它具有监控并显示实时磁盘I/O和进程的统计功能。在查找具体进程和大量使用磁盘读写进程的时候,这个工具就非常有用。 # iotop Iotop命令举例的截图 有关如何安装和使用iotop的信息,请阅读: 在Linux下安装Iotop。 8.Iostat-输入/输出统计 Iostat是一个用于收集显示系统存储设备输入和输出状态统计的简单工具。这个工具常常用来追踪存储设备的性能问题,其中存储设备包括设备、本地磁盘,以及诸如使用NFS等的远端磁盘。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # iostat
Linux 2.6.18-238.9.1.el5 (tecmint.com) 09/13/2012
avg-cpu: %user %nice %system %iowait %steal %idle
2.60 3.65 1.04 4.29 0.00 88.42
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
cciss/c0d0 17.79 545.80 256.52 855159769 401914750
cciss/c0d0p1 0.00 0.00 0.00 5459 3518
cciss/c0d0p2 16.45 533.97 245.18 836631746 384153384
cciss/c0d0p3 0.63 5.58 3.97 8737650 6215544
cciss/c0d0p4 0.00 0.00 0.00 8 0
cciss/c0d0p5 0.63 3.79 5.03 5936778 7882528
cciss/c0d0p6 0.08 2.46 2.34 3847771 3659776
|
有关iostat用法和举例的更多信息,请访问: Linux下6个使用iostat命令的例子。 9.IPTraf-实时局域网IP监控 IPTraf是一个在Linux控制台运行的、开放源代码的实时网络(局域网)监控应用。它采集了大量信息,比如通过网络的IP流量监控,包括TCP标记、ICMP详细信息、TCP/UDP流量分离、TCP连接包和字节数。同时还采集有关接口状态的常见信息和详细信息:TCP、UDP、IP、ICMP、非IP,IP校验和错误,接口活动等。 IP流量监控 有关IPTraf工具用法以及其他更多信息,请访问: IPTraf网络监控工具。 10. psacct 或者 acct – 监视用户活动 psacct或者acct工具用于监视系统里每个用户的活动状况。这两个服务进程运行在后台,它们对系统上运行的每个用户的所有活动进行近距离监视,同时还监视这些活动所使用的资源情况。 系统管理员可以使用这两个工具跟踪每个用户的活动,比如用户正在做什么,他们提交了那些命令,他们使用了多少资源,他们在系统上持续了多长时间等等。 有关这些命令的安装和用法举例信息,请参阅文章:使用psacct或者acct监视用户活动。 11.Monit – Linux进程和服务监控工具 Monit是一个免费的开源软件,也是一个基于网络的进程监控工具。它能自动监控和管理系统进程,程序,文件,文件夹,权限,总和验证码和文件系统。 这个软件能监控像Apache, MySQL, Mail, FTP, ProFTP, Nginx, SSH这样的服务。你可以通过命令行或者这个软件提供的网络借口来查看系统状态。 Monit Linux系统监控 更多内容请参阅:用Monit监控Linux进程 12.NetHogs-监视每个进程使用的网络带宽 NetHogs是一个开放源源代码的很小程序(与Linux下的top命令很相似),它密切监视着系统上每个进程的网络活动。同时还追踪着每个程序或者应用所使用的实时网络带宽。 NetHogs:Linux下的带宽监视 更多信息请参阅: 使用NetHogs监视Linux的网络带宽使用状况。 13.iftop-监视网络带宽 iftop是另一个在控制台运行的开放源代码系统监控应用,它显示了系统上通过网络接口的应用网络带宽使用(源主机或者目的主机)的列表,这个列表定期更新。iftop用于监视网络的使用情况,而‘top’用于监视CPU的使用情况。iftop是‘top’工具系列中的一员,它用于监视所选接口,并显示两个主机间当前网络带宽的使用情况。 iftop-监视网络带宽。 更多信息请参阅:iftop-监视网络带宽的使用情况。 14 Monitorix-系统和网络监控 Monitorix 是一个免费的轻量级应用工具,它的设计初衷是运行和监控Linux/Unix服务器系统和资源等。它有一个HTTP 网络服务器,这个服务器有规律的收集系统和网络的信息并以图形化的形式展示出来。它监控系统的平均负载和使用,内存分配、磁盘健康状况、系统服务、网络端 口、邮件统计(Sendmail,Postfix,Dovecot等),MySQL统计,等等。它就是用来监控系统的总体性能,帮助发现失误、瓶颈和异常 活动的。 15. Arpwatch – 以太网活动监视器 Arpwatch被设计用来监控Linux上的以太网地址解析 (MAC和IP地址的变化)。他在一段时间内持续监控以太网活动并输出IP和MAC地址配对变动的日志。它还可以向管理员发送邮件通知,对地址配对的增改发出警告。这对于检测网络上的ARP攻击很有用。 更多信息请参阅 : Arpwatch to Monitor Ethernet Activity 16. Suricata – 网络安全监控 Suricata 是一个开源的高性能网络安全、入侵检测和反监测工具,可以运行Linux、FreeBSD和Windows上。非营利组织OISF (Open Information Security Foundation)开发并拥有其版权。 更多信息请参阅 : Suricata – A Network Intrusion Detection and Prevention System 17. VnStat PHP – 网络流量监控 VnStat PHP 是流行网络工具”vnstat”的基于web的前端呈现。VnStat PHP 将网络使用情况呈现在漂亮的图形界面中。他可以显示以小时、日、月计的上传和下载流量并输出总结报告。 更多信息请参阅 : VnStat PHP – Monitoring Network Bandwidth 18. Nagios – 网络/服务器监控 Nagios是领先而强大的开源监控系统,他可以让网络/系统管理员在问题影响到正常的业务之前发现并解决它们。有了Nagios系统,管理员可以 在单个窗口内远程检测Linux、Windows、开关、路由器和打印机。它可以危险警告并指出系统/服务器是否有异常,这可以间接帮助你在问题发生之前 采取抢救措施。 更多信息请参阅 : Install Nagios Monitoring System to Monitor Remote Linux/Windows Hosts 我们想知道:你在用什么监控程序来监控Linux服务器的性能呢?如果我们在上面错过了你认为重要的工具,请在评论中告诉我们,不要忘了分享它!
#意识 ASAP (As Soon As Possible)原则 当线上出现诡异问题, 当你意识到靠现有的日志无法定位问题时, 当现象难以在你的开发环境重现时, 请不要执著于枯坐肉眼看代码,因为:一)不一定是你代码逻辑问题,可能是脏数据造成的,是老业务数据造成的,是分布式环境造成的,是其他子系统造成的;二)线上业务处于不稳定中,条件不允许问题定位无限期。 此时,请立即在问题相关的调用链条上,一次性: - 在函数的入口和出口打印日志,同时打印输入、输出参数
- catch(){……}里打印stacktrace,同时打印try块中关键变量的值(避免你发现某个异常是问题第一原因,却不知道是什么变量传入导致的)
- 与其他模块交互的接口入口处打印输入参数,
即, 解决线上问题归根结底要靠log、a lot of log output! 在logging的力度上切勿犹犹豫豫,我们的工程师习惯于吝啬地找两个函数打印日志、打包部署一把、没看出来、再找几个函数打印、再部署、等着现象重现再观察、……,一来二去时间流逝,闲庭信步,从客服知道的小事故变成了全国皆知的大事故。 所以,再强调一遍:在你的调用链条上,逐层调用的函数入口和出口都打印详细日志,不怕多只怕少,然后部署,等待现象重现,毕其功于一役! 通过它可以跟踪为什么系统响应变慢或者太快 - 处理完一个incoming request所耗费的时间,精确到毫秒
- 执行数据库查询的时间
- 从磁盘或者存储介质获取数据的时间
- 等等
2)异常和堆栈跟踪 3)Sessions 知道一个问题是由谁引起的非常重要,因此在日志中使用会话标识符就变得必不可少。它可以简单到是一个 IP 地址或者是一个更复杂的 UUID,只要能区分不同的请求者就足够。 4)版本号 #工具 推荐的Java Logging框架 1)log4j:我们的配置是,log4j.appender.CONSOLE.layout.ConversionPattern= [%-d{yyyy-MM-dd HH\:mm\:ss.SSS}] [%p] [%c] [%m]%n;%p是日志优先级,%c是类目名,%m是输出信息,%n是回车换行符。 2)logback:log4j创建人Ceki Gülcü后续推出了SLF4J+logback。SLF4J(Simple Logging Facade for Java)作为commons-logging的替代,为各种logging APIs提供了一个简单的统一接口,使得最终用户能够在部署的时候配置所希望的logging APIs的实现。logback胜在性能,据称“某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在logback 中需要3纳秒,而在 log4j 中则需要30纳 秒。 logback 创建记录器(logger)的速度也更快:13毫秒,而在 log4j 中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒, 而 log4j 需要2234纳秒,时间减少到了1/23。跟java.util.logging(JUL)相比性能提高也是显著的”。 #配置 不要随便从网上找一个log4j的配置文件,请确认你理解每一个配置项 我们既然输出日志,自然期望在面对“这个问题是否从过去几天开始出现?”这样的疑问时,不至于发现你的rollingPolicy错误设置导致只能看到最近几小时的日志,或者日志发生时间没有精确到毫秒。 #理念 可用grep抽取的日志:独立的行! 我们总是希望能用grep处理日志文件。这意味着:一个日志条目永远不应该跨多行,除非你是堆栈打印。 我们会用grep问日志什么问题呢?如: - 用手机号13910******下单的顾客最近三天内都来自于哪些IP?
- 浏览地址是****?from=kfapi的顾客,但referral却是搜索引擎域名,最近三天有多少次?
- 最近一周内,订单中心执行的所有事务,耗时最长的一次是多长时间?
- ××××的接口是否真的于18:00发送了一个请求,我们收到的参数是什么?
确保你的日志能回答这样的问题。 不同关注领域写不同的日志文件 当访问和调用极其频繁,有时候你会发现把你的工程里什么信息都打印到一个日志文件里,会让你看得头昏脑胀。 最简单的示范就是Apache的访问日志和错误日志是分开的。 同样,你也可以把更加安静的事件(偶尔出现)与更加喧闹的事件分开存储。 如,对外的开放平台可以打印三种日志文件:connection log(建立链接和关闭链接,附带接入参数),message log(内部调用链),stacktrace log(异常的堆栈打印)。 #具体实现 至少精确到毫秒 日志必须包含时间戳,精确到至少毫秒级。 如果只是记录到秒级,我们曾明知代码因缺乏并发控制而产生BUG,却只能郁闷地看着精确到秒级的日志。 对Java来说,最好配置为:yyyy-MM-dd/HH:mm:ss.SSS。 请尽可能打印明确的会话标识 日志条目里打印一个会话标识(A certain session identifier),当有许多并发请求打过来时,你就能基于此字段过滤 client 了。比如,我们日志会补充打印一个浏览器 cookies 里种下的 UUID 。 log4j的isDebugEnabled判断 如果打印信息是常量字符串或简单字符串拼接,那么不需要if ( log.isDebugEnabled() )。 如果你拼装的动作比较耗资源,请用if ( log.isDebugEnabled() )。 如有可能,请将性能数据标准化输出 这样更方便grep或hadoop做性能数据抽取和挖掘,从而能很轻松地转换为图形监控。 比如,订单中心的性能数据格式为:树枝标志 当前节点起始时间 [当前节点持续时间, 当前节点自身消耗时间, 在父节点中所占的时间比例] 哪些位置需要部署性能检测点 (1)访问数据库的dao层; (2)访问外部资源的ext层; (3)访问mq的方法; (4)等等,一切不在你自己负责的工程掌握的部分(外部),或一切你认为自己工程的性能危险点,都需要加入性能监控日志。 #Sample 打印了应用的版本号,客户端的会话标识,关键步骤的执行时长。 一个好的堆栈跟踪日志
摘要: 前面我们介绍了Java当中多个线程抢占一个共享资源的问题。但不论是同步还是重入锁,都不能实实在在的解决资源紧缺的情况,这些方案只是靠制定规则来约束线程的行为,让它们不再拼命的争抢,而不是真正从实质上解决他们对资源的需求。
在JDK 1.2当中,引入了java.lang.ThreadLocal。它为我们提供了一种全新的思路来解决线程并发的问题。但是他的名字难免让我们望文生义:本地线程?
... 阅读全文
摘要: Java监视器支持两种线程:互斥和协作。
前面我们介绍了采用对象锁和重入锁来实现的互斥。这一篇中,我们来看一看线程的协作。
举个例子:有一家汉堡店举办吃汉堡比赛,决赛时有3个顾客来吃,3个厨师来做,一个服务员负责协调汉堡的数量。为了避免浪费,制作好的汉堡被放进一个能装有10个汉堡的长条状容器中,按照先进先出的原则取汉堡。如果容器被装满,则厨师停止做汉堡,如果顾客发现容器内的汉堡吃完了,... 阅读全文
原文出自 http://blog.csdn.net/woshichenxu/archive/2006/01/22/586361.aspx
1. 关于hibernate缓
存的问题:
1.1.1. 基本的缓存原理
Hibernate缓存分为二级,第一级存放于session中称为一级缓存,默认带有且不能卸载。
第二级是由sessionFactory控制的进程级缓存。是全局共享的缓存,凡是会调用二级缓存的查
询方法都会从中受益。只有经正确的配置后二级缓存才会发挥作用。同时
在进行条件查询时必须使用相应的方法才能从缓存中获取数据。比如Query.iterate()方法、load、get方法等。必须注意的是session.find方法永远是从数据库中获取数据,不会从二级缓存中获取数据,即
便其中有其所需要的数据也是如此。
查询时使用缓存的实现过程为:首先查询一级缓存中是否具有需要
的数据,如果没有,查询二级缓存,如果二级缓存中也没有,此时再执行查询数据库的工作。要注意的是:此3种方式的查询速度是依次降低的。
1.2. 存在的问题
1.2.1. 一级缓存的问题以及使用二级缓存
的原因
因为Session的生命期往往很短,存在于Session内部的第一级最快缓存的生命期当然也很短,所以第一级缓存的命中率是很低的。其对系统性能的改善也是很有限的。当然,这个Session内部缓存的主要作用是保持Session内部数据状态同步。并非是hibernate为了大幅提高系统性能所提供的。
为了提高使用hibernate的性能,除了常规的一些需要注意的方法比如:
使用延迟加载、迫切外连接、查询过滤等以外,还需要配置hibernate的二级缓存。其对系统整体性能的改善往往具有立竿见影的效果!
(经过自己以前作项目的经验,一般会有3~4倍的性能提高)
1.2.2. N+1次
查询的问题
执行条件查询时,iterate()方法具有著名的“n+1”次查询的问题,也就是说在第一
次查询时iterate方法会执行满足条件的查询结果数再加一次(n+1)的查询。但是此问题只存在于第一次查询时,在后面执行相同查
询时性能会得到极大的改善。此方法适合于查询数据量较大的业务数据。
但是注意:当数据量特别大时(比如流水线数据等)需要针对此持
久化对象配置其具体的缓存策略,比如设置其存在于缓存中的最大记录数、缓存存在的时间等参数,以避
免系统将大量的数据同时装载入内存中引起内存资源的迅速耗尽,反而降低系统的性能!!!
1.3. 使用hibernate二级缓存的其他注意
事项:
1.3.1. 关于数据的有效性
另外,hibernate会自行维护二级缓存中的数据,以保证缓存中的数据和数据库中的真实数据的一致性!无论何时,当你调用save()、update()或 saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。
也就是说删除、更新、增加数据的时候,同时更新缓存。当然这也
包括二级缓存!
只要是调用hibernate API执行数据库相关的工作。hibernate都会为你自动保证缓存数据的有效性!!
但是,如果你使用了JDBC绕过hibernate直接执行对数据库的操作。此时,Hibernate不会/也不可能自行感知到数据库被进行的变化改动,也就不能再保证缓
存中数据的有效性!!
这也是所有的ORM产品共同具有的问题。幸运的是,Hibernate为我们暴露了Cache的清除方法,这给我们提供了一个手动保证数据有效性的机会!!
一级缓存,二级缓存都有相应的清除方法。
其中二级缓存提供的清除方法为:
按对象class清空缓存
按对象class和对象的主键id清空缓存
清空对象的集合中的缓存数据等。
1.3.2. 适合使用的情况
并非所有的情况都适合于使用二级缓存,需要根据具体情况来决
定。同时可以针对某一个持久化对象配置其具体的缓存策略。
适合于使用二级缓存的情况:
1、数据不会被第三方修改;
一般情况下,会被hibernate以外修改的数据最好不要配置二级缓存,以免引起不一致的数据。
但是如果此数据因为性能的原因需要被缓存,同时又有可能被第3方比如SQL修改,也可以为其配置二级缓存。只是此时需要在sql执行修改后手动调用cache的清除方法。以保证数据的一致性
2、数据大小在可接收范围之内;
如果数据表数据量特别巨大,此时不适合于二级缓存。原因是缓存的数据量过大可能会引起内存资源紧张,反而降低性能。
如果数据表数据量特别巨大,但是经常使用的往往只是较新的那部
分数据。此时,也可为其配置二级缓存。但是必须单独配置其持久化类的缓存策略,比如最大缓存数、缓存过期时间等,将这些参数降低至一个合理的范围(太高会
引起内存资源紧张,太低了缓存的意义不大)。
3、数据更新频率低;
对于数据更新频率过高的数据,频繁同步缓存中数据的代价可能和查询缓存中的数据从中获得的好处相当,坏处益处相抵消。此时缓
存的意义也不大。
4、非关键数据(不是财务数据等)
财务数据等是非常重要的数据,绝对不允许出现或使用无效的数据,所以此时为了安全起见最好不要使用二级缓存。
因为此时“正确性”的重要性远远大于“高性能”的重要性。
2. 目前系统中使用hibernate缓
存的建议
1.4. 目前情况
一般系统中有三种情况会绕开hibernate执行数据库操作:
此种情况使用hibernate二级缓存会不可避免的造成数据不一致的问题,
详细的设计。比如在设计上
避免对同一数据表的同时的写入操作,
使用数据库各种级别的锁定机制等。
2、动态表相关
所谓“动态表”是指在系统运行时根据用户的操作系统自动建
立的数据表。
比如“自定义表单”等属于用户自定义扩展开发性质的功能模块,因为此时数据表是运行时建立的,所以不能进行hibernate的映射。因此对它的操作只能
是绕开hibernate的直接数据库JDBC操作。
如果此时动态表中的数据没有设计缓存,就不存在数据不一致的问
题。
如果此时自行设计了缓存机制,则调用自己的缓存同步方法即
可。
3、使用sql对
hibernate持久化对象表进行批量删除时
此时执行批量删除后,缓存中会存在已被删除的数据。
分
析:
当执行了第3条(sql批量删除)后,后续的查询只可能是以下三种方式:
a.
session.find()方法:
根据前面的总结,find方法不会查询二级缓存的数据,而是直接查询数据库。
所以不存在数据有效性的问题。
b. 调用iterate方法执行条件查询时:
根据iterate查询方法的执行方式,其每次都会到数据库中查询满足条件的id值,然后再根据此id 到缓存中获取数据,当缓存中没有此id的数据才会执行数据库查询;
如果此记录已被sql直接删除,则iterate在执行id查询时不会将此id查询出来。所以,即便缓存中有此条记录也不会被客户获得,也就
不存在不一致的情况。(此情况经过测试验证)
c. 用get或load方法按id执行查询:
客观上此时会查询得到已过期的数据。但是又因为系统中执行sql批量删除一般是
针对中间关联数据表,对于
中间关联表的查询一般都是采用条件查询 ,按id来查询某一条关联关系的几率很低,所以此问题也不存在!
如果某个值对象确实需要按id查询一条关联关系,同时又因为数据量大使用了sql执行批量删除。当满足此两个条件时,为了保证按id 的查询得到正确的结果,可以使用手动清楚二级缓存中此对象的数据的方法!!
(此种情况出现的可能性较小)
1.5. 建议
1、建议不要使用sql直接执行数据持久化对象的数据的更新,但是可以执行批量删除。(系统中需要批量更新的地方也较少)
2、如果必须使用sql执行数据的更新,必须清空此对象的缓存数据。调用
SessionFactory.evict(class)
SessionFactory.evict(class,id)
等方法。
3、在批量删除数据量不大的时候可以直接采用hibernate的批量删除,这样就不存在绕开hibernate执行sql产生的缓存数据一致性的问题。
4、不推荐采用hibernate的批量删除方法来删除大批量的记录数据。
原因是hibernate的批量删除会执行1条查询语句外加满足条件的n条删除语句。而不是一次执行一条条件删除语句!!
当待删除的数据很多时会有很大的性能瓶颈!!!如果批量删除数
据量较大,比如超过50条,可以采用JDBC直接删除。这样作的好处是只执行一条sql删除语句,性能会有很大的改善。同时,缓存数据同步的问题,可以采用
hibernate清
除二级缓存中的相关数据的方法。
调用 SessionFactory.evict(class) ;SessionFactory.evict(class,id)等方法。
所以说,对于一般的应用系统开发而言(不涉及到集群,分布式数
据同步问题等),因为只在中间关联表执行批量删除时调用了sql执行,同时中间关联表一般是执行条件查询不太可能执行按id查询。所以,此时可以直接执行sql删除,甚至不需要调用缓存的清除方法。这样做不会导致以后配置
了二级缓存引起数据有效性的问题。
退一步说,即使以后真的调用了按id查询中间表对象的方法,也可以通过调用清除缓存的方法来解决。
4、具体的配置方法
根据我了解的很多hibernate的使用者在调用其相应方法时都迷信的相信“hibernate会自行为我们处理性能的问题”,或
者“hibernate会自动为我们的所有操作调用缓存”,实际的情况是hibernate虽然为我们提供了很好的缓存机制和扩展缓存框架的支持,但是必
须经过正确的调用其才有可能发挥作用!!所以造成很多使用hibernate的系统的性能问题,实际上并不是hibernate不行或者不好,而是因为使
用者没有正确的了解其使用方法造成的。相反,如果配置得当hibernate的性能表现会让你有相当“惊喜的”发现。下面我讲解具体的配置方法.
ibernate提供了二级缓存的接口:
net.sf.hibernate.cache.Provider,
同
时提供了一个默认的 实现net.sf.hibernate.cache.HashtableCacheProvider,
也可以配置
其他的实现 比如ehcache,jbosscache等。
具体的配置位置位于hibernate.cfg.xml文件中
<property
name="hibernate.cache.use_query_cache">true</property>
<property
name="hibernate.cache.provider_class">net.sf.hibernate.cache.HashtableCacheProvider</property>
很多的hibernate使用者在 配置到 这一步 就以为 完事了,
注意:其实光这样配,根本
就没有使用hibernate的二级缓存。同时因为他们在使用hibernate时大多时候是马上关闭session,所以,一级缓存也没有起到任何作
用。结果就是没有使用任何缓存,所有的hibernate操作都是直接操作的数据库!!性能可以想见。
正确的办法是除了以上的配置外还应该配置每一个vo对象的具体缓存策略,在影射文件中配置。例如:
<hibernate-mapping>
<class
name="com.sobey.sbm.model.entitySystem.vo.DataTypeVO"
table="dcm_datatype">
<cache usage="read-write"/>
<id
name="id" column="TYPEID" type="java.lang.Long">
<generator
class="sequence"/>
</id>
<property name="name" column="NAME"
type="java.lang.String"/>
<property name="dbType"
column="DBTYPE" type="java.lang.String"/>
</class>
</hibernate-mapping>
关键就是这个<cache usage="read-write"/>,其有几个选择
read-
only,read-write,transactional,等
然后在执行查询时 注意了
,如果是条件查询,或者返回所有结果的查询,此时session.find()方法
不会获取缓存中的数据。只有调用query.iterate()方法时才会调缓存的数据。
同时 get 和 load方法 是都会查询缓存中的数据 .
对于不同的缓存框架具体的配置方法会有不同,但是大体是以上的配置
(另外,对于支持事务型,以及支持集群的环境的配置我会争取在后续的文章中中 发表出来)
3. 总结
总之是根据不同的业务情况和项目情况
对hibernate进行有效的配置和正确的使用,扬长避短。不存在适合于任何情况的一个“万能”的方案。
以上结论及建议均建立在自己在对 Hibernate 2.1.2中的测试结果以及以前的项目经验的基础上。如有谬处,请打家提
出指正:)!
最后,祝大家
新年快乐!!在新的一年里 取得人生的进步!!!
创建项目:
打开NetBeans 6.5.1,选择文件—》新建项目,选择Java Web,然后在项目列表中选择 Web 应用程序,下一步
选择使用专用文件夹存储库,指定库文件夹的位置,通常是默认的.\lib,即项目文件夹下的lib文件夹,下一步
选择Web应用服务器。这里选择Tomcat 6.0.18 ,Java EE版本选择 Java EE 5,下一步
在框架对话框中什么都不选择,直接点击完成。
配置项目:
1. 配置OperaMask
在WEB-INF文件夹下新建一个faces-config.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2">
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>
在新创建的项目中,右键单击“库”节点,选择添加库
然后选择导入,选择Spring 2.5库,选择导入库,添加库。
再次选择添加库,然后选择创建。库名称为OperaMask,库类型为类库:
在“定制库”中选择“添加JAR/文件夹”,在弹出的对话框中选择OperaMask的基本jar包,并将导入方式指定为:复制到库文件夹。
一路选是。
2. 配置Spring以及与OperaMask的整合
然后再创建一个OperaMask_SpringCompatibility库,将OperaMask中的spring文件夹下的operamasks-spring.jar添加进来
然后在新创建的项目中展开WEB-INF文件夹,打开web.xml:按照OperaMask包中的blank样例程序的配置进行配置。
之后,在web.xml中配置如下内容:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
|
然后,在源包节点下创建Spring配置文件applicationContext.xml,选择如图所示的几个命名空间:
选择完成,然后在Beans节点之间添加如下内容:
<!-- 开启基于注解的配置 -->
<context:annotation-config/>
<!-- 使 AOM 中的 LiteBean 同样能够被 Spring 所感应到 -->
<bean class="org.operamasks.faces.spring.ManagedBeanConfigurer"/>
|
3. 配置JPA持久化支持
在服务选项卡中,选择MySQL数据库驱动,然后右键单击,选择连接设置,配置你要使用的数据库:
数据库配置完毕,然后回到项目选项卡,右键单击刚创建的项目,选择新建-->其它-->持久性-->持久性单元
在新建持久性单元对话框中选择持久性库为Hibernate,即选择Hibernate为JPA持久单元的实现,数据库连接选择我们刚配置好的MySQL连接。
单击完成。此时,Hibernate的JPA库已经被添加到项目的lib目录下了。包含了基本的Hibernate jar包和Hibernate JPA支持jar包。
注:使用JPA的一个好处就是我们不需要在一个统一的配置文件里罗列所有的实体类,而是可以让实体管理器自动扫描所有被@Entity注解了的实体类。要实现这种功能,如果项目的JPA实现迁移到TopLink Essential,需要加入下面的配置:
找到项目中的“配置文件”节点,打开persistence.xml文件,调整到XML视图中,在<provider> </provider>节点后添加:
<exclude-unlisted-classes>false</exclude-unlisted-classes> 一行。如果希望使用Hibernate实现,请一定不要加入这一行,否则,您必须将您创建的所有实体类逐一添加到persistence.xml中。
如果要使用Spring提供的JpaTemplate(即实现JpaDaoSupport方式),则回到spring的applicationContext.xml文件,在<beans>节点里面添加:
<!-- 利用Spring的实体管理器工厂来创建JPA实体管理器,传入的参数为persistence.xml中指定的持久化单元名称 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="DMCSv1PU"/>
</bean>
<!-- 声明一个Spring提供的JPA事务管理器,传入的参数正是Spring中的实体管理器工厂 -->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 开启Spring提供的基于注解的声明式事务管理 -->
<tx:annotation-driven transaction-manager="txManager"/>
|
至此,持久化支持配置完毕。
创建实体类和相应的JPA控制类
右键单击项目,选择新建-->其它-->持久性-->通过数据库生成实体类,选择数据库表:
点击下一步,输入合适的包名
下一步,映射选项如下图所示:
选择新建-->其它-->持久性-->基于实体类的JPA控制器类,下一步:
添加要生成控制器类的实体类,下一步:
选择合适的包,然后完成。
后面的配置无非就是将JPA的控制器类写入Spring的配置文件,然后在AOM的LiteBean中注入这些控制器类,实现数据库操作以及相应的业务逻辑。
注:解决Spring与Hibernate JPA的冲突:
如上图所示:选择工具--> 库 --> 库位置-->选择当前编辑的项目,选中Spring Framework 2.5,然后去掉cglib2.2那个jar包。这个包与Hibernate JPA中的cglib 2.1.3.jar有冲突
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!776.entry
今天发现服务器上的Oracle11g突然登录不上去了,提示ORA-28002错误,说是口令过期。
不当DBA还真不知道Oracle有这神秘功能。
上网上一查,有类似遭遇的朋友在论坛上求助,人家让他找DBA。汗。。。
好在有的DBA乐于分享,终于找到了解决方案:
1. 用DBA账户登录SQL PLUS。我用的是sysman。
2. 系统会提示口令失效,但是会马上让你重置新密码。
3. 重置之后,进入SQL PLUS控制台。
4. 查看口令失效用户的profile文件
SQL>SELECT username,profile FROM dba_users;
EM(Web界面的控制台):服务器>用户,查看口令失效的用户对应的概要文件,这里假设为DEFAULT,下同。
5.
查看对应的概要文件的口令有效期设置
SQL>SELECT * FROM dba_profiles WHERE profile='DEFAULT' AND resource_name='PASSWORD_LIFE_TIME';
EM(Web界面的控制台):服务
器>概要文件>选择刚刚查到的概要文件DEFAULT>查看,查看口令下面的有效期值。
6.将口令有效期默认值180天
修改成“无限制”(此项要慎重!除非你真得不想要这个密码失效的机制!)
SQL>ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;
EM:服务器>概要文件>选择刚刚查到的概要文件DEFAULT>编辑>口令,在有效期输入
或选择你需要的值,保存。
该参数修改实时生效。
出于数据库安全性考虑,不建议将PASSWORD_LIFE_TIME值设置
成UNLIMITED,即建议客户能够定期修改数据库用户口令。
在修改PASSWORD_LIFE_TIME值之前已经失效的用户,还是需
要重新修改一次密码才能使用。
SQL>ALTER USER test INDENTIFIED BYpassword
也可以从SQL Developer 里面来修改用户的密码,用sysman账户登录以后,找到数据库中的其他用户节点,展开,找到你要修改密码的用户。然后编辑用户,对用户密码进行重置,如下图:
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
内部服务参数配置:
JAVA_OPTS="-server -XX:+UseParNewGC -Xms1024m -Xmx2048m -XX:MaxNewSize=128m -XX:NewSize=128m -XX:PermSize=96m -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:CMSInitiatingOccupancyFraction=1 -XX:+CMSIncrementalMode -XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=20000 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:CMSIncrementalDutyCycleMin=10 -XX:CMSIncrementalDutyCycle=30 -XX:CMSMarkStackSize=8M -XX:CMSMarkStackSizeMax=32M"
前端应用参数配置:
JAVA_OPTS="-server -Xmx4096m -Xms4096m -Xmn480m -Xss256k -XX:PermSize=128m -XX:MaxPermSize=256m -XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=8 -XX:CMSFullGCsBeforeCompaction=0
-XX:+UseCMSCompactAtFullCollection -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19
-Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0"
参数说明:
-Xmx1280m:设置JVM最大可用内存为1280m。最大可设为3550m。具体应用可适当调整。
-Xms1280m:设置JVM初始内存为1280m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn480m:设置年轻代大小为480m。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss256k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:PermSize=64m:指定 jvm 中 Perm Generation 的最小值。 这个参数需要看你的实际情况。可以通过jmap 命令看看到底需要多少。
-XX:MaxPermSize=128m:指定 Perm Generation 的最大值
-XX:+UseConcMarkSweepGC:设置并发收集器
-XX:ParallelGCThreads=8:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
-XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。
-XX:SurvivorRatio=8:每个survivor space 和 eden之间的比例。
-XX:MaxTenuringThreshold=7:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。
-XX:GCTimeRatio=19:设置垃圾回收时间占程序运行时间的百分比,公式为1/(1+n)。
-Xnoclassgc:禁用类垃圾回收,性能会有一定提高。
-XX:+DisableExplicitGC:当此参数打开时,在程序中调用System.gc()将会不起作用。默认是off。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。
-XX:-CMSParallelRemarkEnabled:在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间。
-XX:CMSInitiatingOccupancyFraction=70:指示在 old generation 在使用了 70% 的比例后 , 启动 concurrent collector。
-XX:SoftRefLRUPolicyMSPerMB=0:每兆堆空闲空间中SoftReference的存活时间。
@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
首先我要感谢aa和Liu Xing帮我发现了我日志中的错误。之前比较粗心,把3条SQL语句写成一样的了,对于给读者造成的麻烦,我深表抱歉。
今天我把原文做了修订,为了对得起读者对我的关注,我重新深入的研究了这个问题,在后面,我会把来龙去脉写清楚。
问题:
语句1:
Select * from table1 A where A.col1 not in ( select col1 from table2 B )
如果这样,本来应该有一条数据,结果没有。
如果我改写成这样:
语句2:
select * from table1 A where not exists (SELECT * FROM table2 B where B.col1 = A.col1)
结果就正确,有一条数据显示。
经过一番搜索,原以为是子查询结果集太大的原因。
后来有网上强人指点:子查询里面有空集。即子查询的结果集里面有NULL的结果。
把查询语句修改成:
语句3:
Select * from table1 A where A.col1 not in ( select col1 from table2 B where B.col1 is not null )
果然就查出来了。而且一点不差。。。厉害阿~~~
下面是针对本文题的分析:
1。 首先来说说Oracle中的NULL。
Oracle中的NULL代表的是无意义,或者没有值。将NULL和其他的值进行逻辑运算,运算过程中,NULL的表现更象是FALSE。
下面请看真值表:
|
AND NULL
|
OR NULL |
TRUE |
NULL |
TRUE |
FALSE |
FALSE |
NULL |
NULL |
NULL
|
NULL |
另外,NULL和其他的值进行比较或者算术运算(<、>、=、!=、+、-、*、/),结果仍是NULL。
如果想要判定某个值是否为NULL,可以用IS NULL或者IS NOT NULL。
2. 再来说说Oracle中的IN。
in是一个成员条件, 对于给定的一个集合或者子查询,它会比较每一个成员值。
IN功能上相当于 =ANY 的操作,而NOT IN 功能上相当于 !=ALL 的操作。
IN在逻辑上实际上就是对给定的成员集合或者子查询结果集进行逐条的判定,例如:
SELECT * FROM table1 A WHERE A.col1 in (20,50,NULL);
实际上就是执行了
SELECT * FROM table1 A WHERE A.col1=20 OR A.col1=50 OR A.col1=NULL;
这样,根据NULL的运算特点和真值表,我们可以看出,上边这个WHERE 字句可以被简化(如果返回NULL则无结果集返回,这一点和FALSE是一样的)为
WHERE A.col1=20 OR A.col1=50
也就是说,如果你的table1中真的存在含有NULL值的col1列,则执行该语句,无法查询出那些值为null的记录。
再来看看NOT IN。根据逻辑运算关系,我们知道,NOT (X=Y OR N=M) 等价于 X!=Y AND N!=M,那么:
SELECT * FROM table1 A WHERE A.col1 not in (20,50,NULL)
等价于
SELECT * FROM table1 A WHERE A.col1!=20 AND A.col1!=50 AND A.col1!=NULL
根据NULL的运算特性和真值表,该语句无论前两个判定条件是否为真,其结果一定是NULL或者FALSE。故绝对没有任何记录可以返回。
这就是为什么 语句1查不到应有结果的原因。当然,如果你用NOT IN的时候,预先在子查询里把NULL去掉的话,那就没问题了,例如 语句3。
有些童鞋可能要问了:那如果我想把A表里面那些和B表一样col1列的值一样的记录都查出来,即便A、B两表里面的col1列都包括值为NULL的记录的话,用这一条语句就没办法了吗?
我只能很遗憾的告诉你,如果你想在WHERE后面单纯用IN 似乎不太可能了,当然,你可以在外部的查询语句中将NULL条件并列进去,例如:
SELECT * FROM table1 A WHERE A.col1 in (SELECT B.col1 FROM table2 B) OR A.col1 IS NULL;
3. 最后谈谈EXISTS。
有人说EXISTS的性能比IN要好。但这是很片面的。我们来看看EXISTS的执行过程:
select * from t1 where exists ( select * from t2 where t2.col1 = t1.col1 )
相当于:
for x in ( select * from t1 )
loop
if ( exists ( select * from t2 where t2.col1 = x.col1 )
then
OUTPUT THE RECORD in x
end if
end loop
也就是说,EXISTS语句实际上是通过循环外部查询的结果集,来过滤出符合子查询标准的结果集。于是外部查询的结果集数量对该语句执行性能影响最大,故如果外部查询的结果集数量庞大,用EXISTS语句的性能也不一定就会好很多。
当然,有人说NOT IN是对外部查询和子查询都做了全表扫描,如果有索引的话,还用不上索引,但是NOT EXISTS是做连接查询,所以,如果连接查询的两列都做了索引,性能会有一定的提升。
当然至于实际的查询效率,我想还是具体情况具体分析吧。
那么我们不妨来分析一下语句2为什么能够的到正确的结果吧:
语句2是这样的:
select * from table1 A where not exists (SELECT B.col1 FROM table2 B where B.col1 = A.col1)
实际上是这样的执行过程:
for x in ( select * from table1 A )
loop
if (not exists ( select * from table2 B where B.col1 = x.col1 )
then
OUTPUT THE RECORD in x
end if
end loop
由于表A中不包含NULL的记录,所以,遍历完表A,也只能挑出表A中独有的记录。
这就是为什么 语句2能够完成 语句3的任务的原因。
但如果表A中存在NULL记录而表B中不存在呢?
这个问题请大家自己分析吧。哈哈。有答案了可以给我留言哦。
答案:A表中的NULL也会被查出来。因为select * from table2 B where B.col1 = NULL不返回结果,故
not exists ( select * from table2 B where B.col1 = x.col1 )的值为真。
以上SQL运行结果在MySQL和Oracle上都已经通过。
想从备份的dmp文件中导入某些表的时候,可以用如下imp命令,格式:
imp username/password@本地net服务名 file=xxx.dmp fromuser=xx touser=xx tables=(tablename)
username:登陆数据库的用户名
password:登陆数据库的密码
本地net服务名:连接服务器的本地net服务名
file:你的dmp文件的路径
fromuser,touser:从一个用户导入到另外一个用户
tables:从dmp文件中导入的表名
一、emacs编辑器简介
emacs编辑器是由C语言和LISP语言编写的。LISP(链表处理语言)是由约翰·麦卡锡在1960年左右创造的一种基于λ演算的函数式编程语言。 我们可以使用LISP来扩展emacs,从而为emacs添加更多的命令。(补:emacs -nw:以命令行的方式来运行emacs,而不启动GUI界面)
* 自动保存功能
如果你已经修改了一个文件,但是还没来得及存盘你的计算机就罢工了,那么你所做的修改就很可能会丢失。为了避免这样的不幸发生,Emacs 会定期将正在编辑的文件写入一个“自动保存”文件中。自动保存文件的文件名的头尾各有一个“#”字符,比如你正在编辑的文件叫“hello.c”,那么它 的自动保存文件就叫“#hello.c#”。这个文件会在正常存盘之后被 Emacs 删除。
所以,假如不幸真的发生了,你大可以从容地打开原来的文件(注意不是自动保存文件)然后输入 M-x recover file<Return> 来恢复你的自动保存文件。在提示确认的时候,输入 yes<Return>。
* 其他
- 当emacs失去响应时,C-g命令可用来结束纸条命令的执行。其功能相当于Shell中的Ctrl+C
- 有一些 Emacs 命令被“禁用”了,以避免初学者在不了解其确切功能的情况下误用而造成麻烦。如果你用到了一个被禁用的命令,Emacs 会显示一个提示消息,告诉你这个命令到底是干什么的,询问你是否要继续,并在得到你的肯定之后再执行这命令
二、emacs编辑器的界面
1. 编辑区
用来进行文本编辑的区域。
2. 回显区
如果 Emacs 发现你输入多字符命令的节奏很慢,它会在窗格的下方称为“回显区”的地方给你提示。回显区位于屏幕的最下面一行。
3. 状态栏
- 位于回显区正上方的一行被称为“状态栏”。状态栏最开头的星号(*)表示你已经对文字做过改动。刚刚打开的文件肯定没有被改动过,所以状态栏上显示的不是星号而是短线(-)。
- 状态栏中的小括号用来指明当前使用的编辑模式,默认是fundamental(主模式),emacs的主模式包括了文本模式以及编辑程序源码的Lisp模式等。
三、emacs所能提供的工作环境
- emacs可以执行Shell命令
- emacs可以作为Directory Editor(Dired)
- emacs可以编辑、编译以及调试程序
- emacs可以编辑其它主机上的文档
- emacs可以打印文件
- emacs具有年历(Calendar)以及日记功能
- emacs可以用来阅读man page和info文档
- emacs可以收发电子邮件
- emacs可以阅读网络上的电子布告栏(GNUS)
- emacs具有版本控制的功能(CVS)
- emacs可以提供娱乐环境(游戏功能)
emacs所提供的这些功能,都是先唤起代表此功能的模式(mode)。emacs的模式,分成主要模式(major mode)与次要模式(minor mode)。每一次只能使用一个主模式,而且主模式是必须要的。在一个主模式下,俄可以搭配一个以上的次要模式。使用次要模式相当于启用了该次要模式所对应的插件。
四、emacs命令
- emacs中的每一个命令都有一个命令名,命令名就是该命令所对应的LISP函数的函数名。在emacs中,我们可以为这些命令配置快捷键,从而达到快速调用命令的目的。
- .使用emacs来执行命令的方法有两种:(1).使用Ctrl键 (2).使用Meta键。所有emacs命令都可以用Meta键表示出来,键盘上如果没有Meta键,则可以用Alt键或ESC键来代替。常用的emacs命令通常会有一个快捷键与之相连。快捷键通常是以Ctrl来开头(C-x C-c)。如果要使用Meta键来表达与“Ctrl-x Ctrl-c”相同的效果,则使用“M-x save-buffers-kill-emacs”。使用Meta键,可以利用emacs的completion功能。使用emacs的completion功能的方法是将部分字符串键入后,再按下TAB、SPACE或?键即可。
(1).TAB键:尽可能将其的字填满。
(2).SPACE键:将分隔符(-)之前的字填满。
(3).?:将所有可能的completion选择都列出来。
- 由于emacs中所有的命令都有一个命令名(LISP函数的函数名),因此,我们可以使用“M-x 命令名”来调用emacs中的所有的命令。
* 基本光标控制
- C-v:向下翻屏,与PageDown效果相同(v-> vertical)
- M-v:向上翻屏,与PageUp效果相同
- C-l:重绘屏幕,并将光标所在行置于屏幕的中央
- C-b:光标向前移动一格(b->backward)
- C-f:光标向后移动一格(f->forward)
- C-p:光标向前移动一行(p->previous)
- C-n:光标向后移动一行(n->next)
- M-b:光标向前移动一个单词
- M-f:光标向后移动一个单词
- C-a:光标移动到行首
- C-e:光标移动到行尾
- M-<:光标移动到文章的开头(注意:“<”的输入要shift键,实际为Alt+Shift+<)
- M->:光标移动到文章的结尾
- C-u:给命令传递参数。例如:“C-u 2 C-d”表示删除两个字符
- M-x goto-line n RET:调到第n行
* 编辑命令
- C-d:删除光标后的一个字符
- C-k:删除从光标到行尾的字符(k->killl)
- C-x u:Undo(想要redo,随便输入一个字符,在Undo)
- C-SPC、C-@、M-x set-mark-command:设置mark
- C-x h:将整个缓冲区设置为区域
- C-w:将区域的文本删除,并放入yanking ring中。区域指的是从mark到point(光标所处的位置称为point)之间的文本
- M-w:复制区域到yanking ring中
- C-y:将yanking ring中最后一个区域插入当前缓冲区
- M-j:回车并且到下一行产生适当的缩进
- M-m:将光标移动到当前行的第一个非空白字符上
- M-;:产生通用注释
- M-x comment-region:把块注释掉
- M-x kill-comment:消除注释
* 查找与替换
- C-s:向后搜索,光标将停在第一个匹配的字符串处。再按一次C-s将继续搜索下一个匹配的字符串。如果要停止搜索,则使用C-g,此时光标将会回到搜索开始的位置
- C-r:向前搜索
- M-x replace-string:替换
* 文件操作
- C-x c-f:打开文件,如果文件不存在则创建
- C-x C-s:保存文件。第一次存盘时,emacs会将文件重命名来备份。重命令的规则通常是在原文件名后加上一个“~”字符。如果要关闭emacs的自动备份功能,使用 M-x customize-variable <Return> make-backup-files <Return>
- C-x C-w:将文件“另存为”
- C-x C-v:打开一个新文件,并关闭当前缓冲区
- C-x C-r:以只读的方式打开文件
- C-x i:将文件插入光标当前位置
* 缓冲区
- Emacs 把每个编辑中的文件都放在一个称为“缓冲区(buffer)”的地方。每打开一个文件,Emacs 就在其内部开辟一个缓冲区用来保存打开的文件的数据。ESC ESC ESC命令可以用来退出打开的小缓冲区,比如:命令提示窗格等
- C-x C-b 列出当前所有的缓冲区(b->buffer)
- C-x b 缓冲区名:切换到指定的缓冲区(例如:C-x b M<tab>:切换到以M开头的缓冲区)
- C-x s:保存emacs中所有的缓冲区(s->save)
- C-x right:切换到下一个缓冲区
- C-x left:切换到前一个缓冲区
- C-x C-c:退出emacs,并询问用户是否保存
- C-x k:关闭缓冲区
- C-z:将emacs挂起,然后回到Shell中,并不退出emacs。之后,我们可以使用%emacs或fg命令来回到emacs
* 窗口
Emacs 可以有多个窗格,每个窗格显示不同的文字。
- C-x 0:关闭光标所在的窗口
- C-x 1:保留光标所在的窗格,并将其扩大到整个屏幕,同时关掉所有其它的窗格
- C-x 2:水平分割当前窗口
- C-x 3:垂直分割当前窗口
- C-x o:在emacs的窗格中进行切换(o->other)
- C-M-v:滚动下方的窗格。一般在我们使用下方的窗格进行参考,而又不想将光标切换到下一个窗格时使用
* 使用帮助
- C-h c 快捷键:显示快捷键的简要说明
- C-h k 快捷键:显示快捷键所对应的命令名及其详细说明
- C-h a 关键字:显示包含有指定关键字的命令
- C-h i:查看Info文档
* 在emacs中运行shell命令
- M-! cmd RET:打开一个名为“*Shell Command Output*“的窗口,并把该命令的执行结果显示在其中。按下”C-x 1“组合键可以关闭这个窗口。由于Shell命令的输出是在一个编辑缓冲区里,因此我们可以对它进行编辑、保存等操作。
- M-| cmd RET:运行Shell命令,并使用编辑窗口中选定的区域作为该Shell命令的输入,然后可以选择是否用该Shell命令的输出来替换编辑窗口中选中的区域。
- C-u M-! cmd RET:执行一条Shell命令,并将其输出放到编辑区中光标所在的位置处,而不将其输出到”Shell Command Output“窗口。
- M-x shell:运行一个子Shell,该子Shell对应于emacs中的一个名为”*Shell*"的缓冲区,此后,我们就可以交互式的运行Shell命令了。
- M-x term:运行一个子Shell,该子Shell对应于emacs中的一个名为“*Terminal*”的缓冲区。使用该命令获得的子Shell是一个完整的Shell的模拟,与我们直接在Shell中操作没有什么差别。
- M-x eshell:运行emacs shell。该Shell为emacs自己实现的一个shell,而前面运行的shell都为系统中的shell程序(例如:/bin/csh等)。我们可以通过设置变量shell-file-name来设置emacs所使用的默认shell
* Dired功能
- emacs的Dired(Directory Editor)功能使emacs缓冲区能够用来显示目录列表,并可以用来进入目录的子目录。Dired缓冲区是只读的,不能够被修改。
- C-x d:进入Dired
* emacs配置文件
emacs配置文件通常位于计算机的 $HOME 目录,如果是 MS Windows (Windows 2000, Windows XP以上),默认是 c:\Documents and Settings\username\Application Data\,这个适用于 Emacs22 以上的版本。Emacs21 默认的 $HOME 目录在 C:\ 。当然也可以通过环境变量 $HOME 重新设置,总之在比较大众化的操作系统中,你都可以通过C-x C-f ~/.emacs 来编辑您的个性化配置文件。
;;显示时间
(display-time)
;;显示行号
(column-number-mode t)
(show-paren-mode t)
;;设置TAB宽度为4
(setq default-tab-width 4)
;;以下设置缩进
(setq c-indent-level 4)
(setq c-continued-statement-offset 4)
(setq c-brace-offset -4)
(setq c-argdecl-indent 4)
(setq c-label-offset -4)
(setq c-basic-offset 4)
(global-set-key "\C-m" 'reindent-then-newline-and-indent)
(setq indent-tabs-mode nil)
(setq standard-indent 4)
;;开启语法高亮。
(global-font-lock-mode 1)
;;设置默认工作目录
(setq default-directory "/home/test/source/")
;; 去掉滚动条
(set-scroll-bar-mode nil)
;;关闭开启画面
(setq inhibit-startup-message t)
(setq indent-tabs-mode t)
;;不产生备份文件
(setq make-backup-files nil)
;;设置自定义变量
(custom-set-variables
'(column-number-mode t)
'(current-language-environment "UTF-8")
'(display-time-mode t)
'(ecb-options-version "2.32")
'(mouse-1-click-in-non-selected-windows t)
'(mouse-drag-copy-region t)
'(mouse-yank-at-point t)
'(save-place t nil (saveplace))
'(show-paren-mode t)
'(transient-mark-mode t))
(custom-set-faces
;;选择小工具栏图标
(tool-bar-mode -1)
rectangle(列)模式编辑 和emacs shell(摘抄)
不敢独享,与大家分享。也可以在Emacs中用C-x C-h列出全部命令,查找C-x r c,所有列模式命令都是以C-x r开始的
C-x r C-@ point-to-register
C-x r SPC point-to-register
C-x r + increment-register
C-x r b bookmark-jump
C-x r c clear-rectangle
先用C-space或者C-@设一个mark,移动光标到另一点,使用C-x r c可以清楚mark到光标处的矩形区域,该区域留下空白。
C-x r d delete-rectangle
删除矩形区域,不留空白,后面的字符前移
C-x r f frame-configuration-to-register
C-x r g insert-register
C-x r i insert-register
将某个寄存器的内容插入某处
C-x r j jump-to-register
C-x r k kill-rectangle
就是剪切某个选定的矩形区域,用C-x r y可以贴上
C-x r l bookmark-bmenu-list
C-x r m bookmark-set
C-x r n number-to-register
C-x r o open-rectangle
在选定的矩形区域插入空白
C-x r r copy-rectangle-to-register
将选定的矩形区域复制到某个寄存器
C-x r s copy-to-register
C-x r t string-rectangle
在选定区域所有列前插入同样的字符
C-x r w window-configuration-to-register
C-x r x copy-to-register
C-x r y yank-rectangle
类似于矩形区域的粘贴,就是将刚用C-x r k剪切的矩形区域粘贴过来
C-x r C-SPC point-to-register
摘自:http://chandlewei.blogbus.com/logs/15583440.html
在 Emacs 里面同时打开多个 shell 会话:
通过重命名shell所在缓冲区的名字可以开启多个shell
E-x shell
E-x rename-buffer shellA
E-x shell
这时就开启了两个不相干的shell:shellA 和*shell*
也可以通过其他方式来开启shell,例如:
1. M-x eshell 开启Emacs Shell
2. M-x term 开启linux终端,可以指定你要的终端SHELL。但是这个可能会拦截你的EMACS命令。
整理你的屏幕:
通过C-c C-o可以清除上次命令的输出,特别是对于cat或dmesg这种产生大量输出的命令。
详细文章:http://www.ibm.com/developerworks/cn/aix/library/0811_yangbh_emacs2/index.html
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!826.entry
MOZILLA PUBLIC LICENSE
MPL
License,允许免费重发布、免费修改,但要求修改后的代码版权归软件的发起者。这种授权维护了商业软件的利益,,它要求基于这种软件得修改无偿贡献
版权给该软件。这样,围绕该软件得所有代码得版权都集中在发起开发人得手中。但MPL是允许修改,无偿使用得。MPL软件对链接没有要求。
BSD开源协议(original BSD license、FreeBSD license、Original BSD license)
BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以"为所欲为",可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。
但"为所欲为"的前提当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:
- 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
- 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
- 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。
BSD
代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对
商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。
Apache Licence 2.0(Apache License, Version 2.0、Apache License, Version 1.1、Apache License, Version 1.0)
Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件也和BSD类似:
- 需要给代码的用户一份Apache Licence
- 如果你修改了代码,需要再被修改的文件中说明。
- 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。
- 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。
Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。
GPL(GNU General Public License)
我们很熟悉的Linux就是采用了GPL。GPL协议和BSD, Apache
Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代
码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商
业软件公司开发的免费软件了。
GPL协议的主要内容是只要在一个软件中使用("使用"指类库引用,修改后的代码或者衍生代
码)GPL
协议的产品,则该软件产品必须也采用GPL协议,既必须也是开源和免费。这就是所谓的"传染性"。GPL协议的产品作为一个单独的产品使用没有任何问题,
还可以享受免费的优势。
由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。
其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似。
LGPL(GNU Lesser General Public License)
LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生
之GPL类库的的软件必须采用GPL协议不同。LGPL
允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并
发布和销售。
但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生
的代码都必须采用LGPL协议。因此LGPL协议的开源
代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。
GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品
MIT(MIT)
MIT是和BSD一样宽范的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的.
Public Domain
公共域授权。将软件授权为公共域,这些软件包没有授权协议,任何人都可以随意使用它。
Artistic许可使作者保持对进一步开发的控制。
以下为对GPL协议的进一步解释:
=================================================================================
GNU通用公共许可证(GNU General Public License)(英文通常以GNU GPL或是直接簡短的以GPL表示),是一個廣泛被使用的自由軟體許可證,最初由理查德·斯托曼为GNU计划而撰写。此许可证最新版本为“版本3”,2007年6月29日发布。GNU宽通用公共许可证(GNU Lesser General Public License,一般簡稱LGPL)是改自GPL的另一個版本,其目的是為了應用於一些軟體函式庫。
GPL給予了電腦程式自由軟體的定義,並且使用了所謂的"Copyleft"來確保程式的自由被完善的保留。
自由
GPL授予程序接受人以下权利,或称“自由”:
- 以任何目的运行此程序的自由;
- 以学习程序工作机理为目的,对程序进行修改的自由(能得到源代码是前提);
- 再发行复制件的自由;
- 改进此程序,并公开发布改进的自由(能得到源代码是前提)
相反地,随版权所有软件的最终用户许可证几乎从不授予用户任何权利(除了使用的权利),甚至可能限制法律允许的行为,比如逆向工程。
GPL与其他一些更“许可的”自由软件许可证(比如BSD许可证)相比,主要区别就在于GPL寻求确保上述自由能在复制件及演绎作品中得到保障。它通过一种由斯托曼发明的叫Copyleft的法律机制实现,即要求GPL程序的演绎作品也要在GPL之下。相反,BSD式的许可证并不禁止演绎作品变成专有软件。
由于某些原因,GPL成为了自由软件和开源软件的最流行许可证。到2004年4月,GPL已占Freshmeat上所列的自由软件的约75%,SourceForge的约68%。类似的,2001年一项关于Red Hat Linux 7.1的调查显示一般的代码都以GPL发布。著名的GPL自由软件包括Linux核心和GCC。
历史
GPL由斯托曼撰写,用于GNU计划。它以GNU Emacs、GDB、GCC的许可证的早期版本为蓝本。这些许可证都包含有一些GPL的版权思想,但仅只针对特定程序。斯托曼的目标就是创造出一种四海之内皆可使用的许可证,这样就能为许多源代码共享计划带来福音。GPL版本1就这样,在1989年1月诞生。
到1990年时,因为一些共享库而出现了对比GPL更宽松的许可证的需求。所以当GPL版本2在1991年6月发布时,另一许可证——库通用许可证(Library General Public License,简称LGPL)也随之发布,并记作“版本2”以示对GPL的补充。版本号在LGPL版本2.1发布时不再相同,而LGPL也被重命名为GNU宽通用公共许可证(Lesser General Public License)以体现GNU哲学观。
GPLv1
GPL版本1,即最初的版本,发布于1989年一月,其目的是防止那些阻碍自由软件的行为,
而这些阻碍软件开源的行为主要有两种(一种是软件发布者只发布可执行的二进制代码而不发布具有源代码,一种是软件发布者在软件许可加入限制性条款)。因此
按照GPLv1,如果发布了可执行的二进制代码,就必须同时发布可读的源代码,并且在发布任何基于GPL许可的软件时,不能添加任何限制性的条款。
GPL2
理查德·斯托曼在GPLv2中所做的最大的改动就是增加了“自由还是死亡”("Liberty or Death")这章条款,即第七章liberty-or-death Presentation。这章中申明道,如果哪个人在发布源于GPL的软件的时候,同时添加强制的条款,以在一定程度上保障和尊重其它一些人的自由和权益(也就是说在一些国家里,人们只能以二进制代码的形式发布软件,以保护开发软件者的版权),那么他将根本无权发布该软件。
到了1990年,人们普遍认为一个限制性弱的许可证对于自由软件的发展是有战略意义上的好处的;因此,当GPL的第二个版本(GPLv2)在1991年6月发布时,与此同时第二个许可证程式庫GNU通用公共许可证(LGPL,the Library General Public License )也被发布出来并且一开始就将其版本定为第2版本以表示其和GPLv2的互补性。这个版本一直延续到1999年,并分支出一个衍生的LGPL版本号为2.1,并将其重新命名为轻量级通用公共许可证(又称宽通用公共许可证)(Lesser General Public License)以反应其在整个GNU哲学中的位置。
到2005年,GPL版本3正由斯托曼起草,由伊本·莫格林和軟件自由法律中心(Software Freedom Law Center)[1]提供法律咨询。
斯托曼在2006年2月25日自由及开源软件开发者欧洲会议的演讲上说:([2])
- 在所有的改动中,最重要的四个是:
- 解决软件专利问题;
- 与其他许可证的兼容性;
- 源代码分割和组成的定义;
- 解决数字版权管理 (DRM) 问题。
2006年,自由软件基金会针对GPL的可能的修改开始了12个月的公共咨询。
GPLv3草稿[2]于2006年1月16日开始可用。版本2与3的非官方比较对照参见:[3],[4]。
2007年3月28日正式启用。
2007年6月29日,自由软件基金会正式发布了GPL第3版[3]。
条款
以下是对GPL条款的一个通俗易懂的总结。而GPL原文文本才是真正法律上精确的。该文本的链接可从本页底部获得。
授予的权利
此GPL的条款和条件适用于任何收到GPL下的作品的人(即“许可证接受人”)。任何接受这
些条款和条件的许可证接受人都有修改、复制、再发行作品或作品的演绎版本的授权。许可证接受人可以对此项服务收取费用
,反之亦然。这一点是GPL与其他禁止商业用途的自由软件许可证最大的不同。Stallman认为自由软件不应限制其商业用途,同时GPL清楚地说明了这
一点。
但GPL又规定发行者不能限制GPL授予的权利。例如,这禁止对软件在单纯沉默(消极默示)式协议或合同下的发行。GPL下的发行者同时也同意在软件中使用的专利可以在其它GPL软件中使用。
Copyleft
GPL不会授予许可证接受人无限的权利。再发行权的授予需要许可证接受人开放软件的源代码,及所有修改。且复制件、修改版本,都必须以GPL为许可证。
这些要求就是copyleft,它的基础就是作品在法律上版权所有。由于它版权所有,许可证接受人就无权进行修改和再发行(除合理使用),除非它有一个copyleft条款。如果某人想行使通常被法律所禁止的权利,只需同意GPL的条款。相反地,如果某人发行软件违反了GPL(比如不开放源代码),他就有可能被原作者起诉。
copyleft利用版权法来达到与其相反的目的: copyleft给人不可剥夺的权利,而不是版权法所规定的诸多限制。这也是GPL被称作“被黑的版权法”的原因。
许多GPL软件发行者都把源代码与可执行程序捆绑起来。另一方式就是以物理介质(比如CD)为载体提供源代码。在实践中,许多GPL软件都是在互联网上发行的,源代码也有许多可以FTP方式得到。
copyleft只在程序再发行时发生效力。对软件的修改可以不公开或开放源代码,只要不发行。注意copyleft只对软件有效力,而对软件的输出并无效力(除非输出的是软件本身)。不过这在GPL版本3中可能会有改动。
GPL是许可证
GPL设计为一种许可证,而不是合同。在英美法系国家,许可证与合同有法律上的明确区别:合同由合同法保障效力,而GPL作为一种许可证由版权法保障效力。不过在许多采用欧陆法系的国家并无此种区别。
GPL原理简单:在版权法下,你不遵守GPL的条款和条件你就没有相应权利。而作品在没有GPL的情况下,版权法作为默认条款发生效力,而不是作品进入公有领域。
版权所有人
GPL文本是版权所有的,且著作权人是自由软件基金会。但是,自由软件基金会没有在GPL下发行作品的著作权(除非作者制定自由软件基金会是著作权人)。只有著作权人才有权对许可证的违反进行起诉。
自由软件基金会允许人们使用以GPL为基础的其他许可证,但不允许演绎的许可证未经授权地使用GPL的前言。不过像这样的许可证通常与GPL不兼容。[4]
GNU计划创立的其他许可证包括:GNU宽通用公共许可证和GNU自由文档许可证。
争议
一个关于GPL重要的争议是,非GPL软件是否可以动态链接到GPL库。GPL对GPL作品的演绎作品在
GPL下发布规定很明确。但是对于动态链接到GPL库的作品是否是演绎作品就规定得不清楚了。自由和开放源代码社区为此分成两派,自由软件基金会认为这种
作品就是演绎作品,但其他专家并不同意。这个问题根本的并不关乎GPL本身,而是一个版权法如何定义演绎作品。美国联邦上诉法院第九巡回审判庭在
Galoob v. Nintendo案对演绎作品尝试定义,但最终没有明确的结果。
不幸的是,许多开发者觉得这是个技术问题。但实际上这完全是法律问题。不过由于迄今为止没有案例表明有人以动态链接的方式来绕过GPL的条款或者并被起诉,动态链接的限制已经是事实上地(de facto)有效,不论它是否是法律上地(de jure)有效。
2002年,MySQL AB公司起诉Progress NuSphere侵犯版权和商标。NuSphere被指以链接代码的形式侵犯了著作权。最终此案以调解结束。在听证期间,法官“认为没有什么原因”(不管是否是动态链接)会使得GPL失去法律效力。
2003年8月,SCO Group称他们认为GPL没有法律效力,且准备就在Linux核心中使用的SCO Unix代码进行诉讼。参见SCO诉IBM。
2004年4月,在SiteCom拒绝停止发行Netfilter项目的GPL软件后,慕尼黑地区法庭据对GPL条款的侵犯判定对SiteCom进行临时性禁令(诉前停止侵犯专利权行为的措施)。同年7月,法庭确认此勒令为对SiteCom最终判决。此判决明显的印证了自由软件基金会的法律顾问伊本·莫格林的预言:
- “被告侵犯了原告的著作权:提供了软件netfilter/iptables的广告及下载,但没有遵守GPL的条款。可以说,如果被告有许可证许
可,这些行为是完全合法的……原被告就GPL是否达成协议这是一个独立的问题。如果当事人没有同意,被告将没有复制、发行、公开
‘netfilter/iptables’的权利。”
此判决十分重要,因为它是全球首次法庭确认GPL是有法律效力的。
2005年5月,Daniel Wallace于美国联邦印第安纳南区地方法院起诉自由软件基金会,因为二者对GPL是否是非法意见不一。后诉讼于3月结束,因为Wallace没有有效的反托拉斯陈述。法庭注意到“GPL鼓励,而不是反对电脑操作系统的自由竞争和发行,这直接使消费者受益。”[5]Wallace被拒绝改变诉由,并被要求支付诉讼费用。
兼容性
大多数自由软件许可证,比如MIT/X许可证、BSD许可证、LGPL,都是“GPL兼容的”,即它们的代码与GPL代码混用无冲突(但新代码则是GPL下的)。但是有某些开源软件许可证不是GPL兼容的。通常意见是开发者仅只使用GPL兼容的许可证,以免法律问题。
参见软件许可证列表以查证兼容性。
批评
2001年微软的首席执行官史蒂夫·巴爾默称
Linux为“癌症”,因为GPL的影响。微软批评者指出,微软憎恶GPL的真正原因是因为对微软的“包围、扩展、消灭”策略起了反作用。注意微软已以
GPL为许可证发行了SFU(Microsoft Windows Services for UNIX)中所包含的部分组件,例如GCC。
GPL的批评者常常认为GPL是有“传染性”的“病毒”
,因为GPL条款规定演绎作品也必须是GPL的。由于“演绎作品”通常被解释为包含GPL代码或动态链接到GPL库(如上)的软件,“病毒说”来源于
GPL对于许可证的强制继承的要求。这正是GPL与BSD式许可证的哲学思想上的差异。GPL的支持者确信自由软件世界应具有自我保护能力和可持续发展性
——确保自由软件的演绎作品同样“自由”,但其他人认为自由软件应给予“所有人”最大的自由。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!822.entry
今天在项目里碰到了非常纠结的问题——中文乱码问题。
原因是这样的,我打算通过JSF的一个FRAME控件LOAD一个jsp页面,该jsp页面根据GET参数得到文件的路径和文件名,从而可以将文件用二进制流输出给浏览器,以便下载。
但是由于我本地的文件名是中文的,所以出现了乱码问题。
简单点说,主要有以下几点:
1. 用java创建本地文件的中文文件名问题:
为了保证你的文件名不会乱码,在用java.io.File对象创建文件的时候,构造函数里的filename千万不要随意的转换成其他字符集的。就用默认的就好。也就是说
File f= new File(“中文文件.txt”);
足矣。这样,无论Windows用GBK编码文件名还是LINUX用UTF-8编码文件名,都可以在当前的系统中正常的查看。
2.向JSP传递中文参数的时候,如果你没有设定Tomcat的全局URIEncoding,一定要把中文参数进行URLEncoding
URLEncoder.encode(requestUrl,"UTF-8");
在被请求的页面,执行URLDecoder.decode(request.getParameter(“param”),"UTF-8");从而得到正确的中文。如果不能,可以尝试“new String(request.getParameter(“param”).getBytes("iso8859_1"),”UTF-8”)”
3.如果你在POST传参的时候发现出了问题,
可以在web.xml里面配置一个CharacterEncodingFillter。这个东西网上有很多代码,自己找吧。如果你用的是Spring,可以用它自带的org.springframework.web.filter.CharacterEncodingFilter
4.另一种解决参数乱码的办法就是修改TOMCAT的配置文件。
找到server.xml。把里面HTTP端口和HTTPS端口(如果你放开了)的Connector元素后面加上URIEncoding=”UTF-8”字样。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!819.entry
做的项目中要用到日志功能,记录重要数据增删改,以提供后台动态数据恢复功能,在数据库中建立一个表四个字段:
id:标识(long)、action:增删改类别(String 或 int)、olddata与newdata分别记录增删改前后的数据类型为blob、optime记录操作时间
项目持久层用了Hibernate所以数据库中所有条目都是以JavaBean形式出现,JavaBean扩展了Serializable可以实现对象的序列化,现在问题就是怎样保存JavaBean序列化的结果到数据库,并且可以逆向反序列化为实例。
因为刚接触Java对列的概念不是很清楚,所以在序列化上遇到了问题,首先是如何不通过临时文件取得对象序列化的结果,网上的例子大多是对文件流的操作,用来保存图片
综合网上的多个例子以及从JDK中查询的结果,总结出以下过程:
1、还是对流的操作,不过不是文件流而是字节流,利用ByteArrayOutputStream创建
2、通过new出来ByteArrayOutputStream作为参数创建ObjectOutputStream
3、调用ObjectOutputStream的writeObject将任意JavaBean序列化为字节流
//以上是序列化过程,实际上使用不同的XStream就可以把JavaBean序列化到不同的流中
4、通过调用ByteArrayOutputStream的toByteArray可以获得byte数组
//这是取中间值,相当于文件流操作时利用文件名打开一个文件流,文件名也是一个中间值
5、将得到的byte数组作为参数用ByteArrayInputStream打开一个输入流
6、调用静态方法Hibernate.createBlob(),以输入流为参数获取Blob
7、此后可将该Blob设置为接收Bean的属性保存到数据库中
//以上完成将序列化的结果存储到数据库
8、利用Hibernate的API的到数据库中的Blob很容易,然后调用Blob的getBinaryStream可获取输入流,将此流作为ObjectInputStream,调用readObject可得到序列化前的实例
代码:
//序列化
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(new User("cfgxy"));
//保存到数据库,sessionFactory是Hibernate中SessionFactory的一个实例
Session session=sessionFactory.createSession();
Transaction tx =session.openTransaction();
ByteArrayInputStream bis=new ByteArrayInputStream(bos.getByteArray());
session.save(new Logs(null,"INSERT",null,Hibernate.createBlob(bis)));
tx.commit();
session.close();
//从数据库读取,假设传来的参数id为数据库主键的值
Session session=sessionFactory.createSession();
Logs log=(Logs)session.load(Logs.class,id);
ObjectInputStream ois=new ObjectInputStream(log.getNewData().getBinaryStream());
return (User)ois.readObject();
//代码中均未对异常进行捕捉,实际运用中要捕捉异常
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!818.entry
"java深度历险"一书在讲解“类装载”的一章中,举了以下的例子: 引用 Java代码 - public interface Assembly{
- public void start();;
- }
-
- public class Word implements Assembly{
- static{
- System.out.println("Word static initialization!");;
- }
-
- public void start();{
- System.out.prinlnt("Word starts");;
- }
- }
-
- public class Office{
- public static void main(String args[]); throws Exception{
- Office off = new Office();;
- System.out.println("类别准备载入");;
- Class c = Class.forName(args[0],true,off.getClass();.getClassLoader(););;
- System.out.println("类别准备实例化");;
- Object o = c.newInstance();;
- Object o2= c.newInstance();;
- }
- }
执行java Office Word,运行结果如下: “Loaded Office” “类别准备载入” “Loaded Accembly” “Loaded Word”” “Word static initialization” “类别准备实体化”。 但是如果将Office.java中Class.forName(args[0],true,off.getClass().getClassLoader())中的true变为false,再执行java Office Word结果显示为: “Loaded Office” “类别准备载入” “Loaded Accembly” “Loaded Word”” “类别准备实体化” “Word static initialization”。 显然两次红字部分顺序相反,及static块执行的顺序不同。此书作者提出了原因,原文: 引用 “过去很多java书上提到静态初始化(static initializion block)时,都会说静态初始化区块只是在类第一次载入的时候才会被调用仅仅一次。可是上面输出却发现即使类被载入了,其静态初始化区块也没有被调用,而是在第一次调用newInstance方法时,静态初始化块才被真正调用,应该改成-静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。” 其实,该书作者的上述描述有误。通过一个试验,就可以看出谬误所在。 Java代码 - public class TestA{
- static{
- System.out.println("Static block executed!");;
- }
- }
-
- public class Test{
- public static void main(String args[]);{
- Test test = new Test();;
- Class.forName("TestA",true,test.getClass();.getClassLoader(););;
- }
- }
运行一下,相信大家一定可以看到,“Static block executed!”的输出。这与 引用 而是在第一次调用newInstance方法时,静态初始化块才被真正调用 的说法矛盾。 其实我想事实是这样的: 一个类的运行,JVM做会以下几件事情 1、类装载 2、链接 3、初始化 4、实例化;而初始化阶段做的事情是初始化静态变量和执行静态方法等的工作。所以,当Class.forName(args[0],true,off.getClass().getClassLoader());中的true变为false的时候,就是告诉JVM不需再load class之后进行initial的工作。这样,将initial的工作推迟到了newInstance的时候进行。所以,static块的绝对不是什么“只是在类被第一次实体化的时候才会被仅仅调用一次”,而应该是在类被初始化的时候,仅仅调用一次。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!817.entry
一、性能优化的一般概念 人们普遍认为Java程序总是比C程序慢,对于这种意见,大多数人或许已经听得太多了。实际上,情况远比那些陈旧的主张要复杂。许多 Java程序确实很慢,但速度慢不是所有Java程序的固有特征。许多Java程序可以达到C或C++中类似程序的效率,但只有当设计者和程序员在整个开发过程中密切注意性能问题时,这才有可能。 本文的主要目的是讨论如何优化Java IO操作的性能。许多应用把大量的运行时间花在网络或文件IO操作上,设计拙劣的IO代码可能要比经过精心调整的IO代码慢上几倍。 说到Java程序的性能优化,有些概念总是一次又一次地被提起。本文的示例围绕IO应用的优化展开,但基本原则同样适用于其他性能情况。 对于性能优化来说,最重要的原则也许就是:尽早测试,经常测试。不知道性能问题的根源就无法有效地调整性能,许多程序员因为毫无根据地猜测性能问题的所在而徒劳无功。在一个只占程序总运行时间百分之一的模块上花费数天时间,应用性能的改进程度不可能超过百分之一。所以,应当避免猜测,而是采用性能测试工具,比如一些代码分析工具或带有时间信息的日志,找出应用中耗时最多的地方,然后集中精力优化这些程序的热点。性能调整完成后,应当再次进行测试。测试不仅有助于程序员把精力集中在那些最重要的代码上,而且还能够显示出性能调整是否真地取得了成功。 在调整程序性能的过程中,需要测试的数据可能有很多,例如运行总时间、内存占用平均值、内存占用峰值、程序的吞吐能力、请求延迟时间以及对象创建情况等。到底应该关注哪些因素,这与具体的情况和对性能的要求有关。大部分上述数据都可以通过一些优秀的商品化分析工具测试得到,然而,并非一定要有昂贵的代码分析工具才能收集得到有用的性能数据。 本文收集的性能数据只针对运行时间,测试所用的工具类似于下面的Timer类(可以方便地对它进行扩展,使它支持pause()和 restart()之类的操作)。带有时间信息的日志输出语句会影响测试结果,因为这些语句也要创建对象和执行IO操作,Timer允许我们在不用这类语句的情况下收集时间信息。
public class Timer { // 一个简单的“秒表”类,精度为毫秒。 private long startTime, endTime; public void start() { startTime = System.currentTimeMillis(); } public void stop() { endTime = System.currentTimeMillis(); } public long getTime() { return endTime - startTime; } } |
引起Java性能问题的常见原因之一是过多地创建临时对象。虽然新的Java虚拟机在创建许多小型对象时有效地降低了性能影响,但对象创建属于昂贵操作这一事实仍旧没有改变。由于字符串对象不可变的特点,String类常常是性能问题最大的罪魁祸首,因为每次修改一个String对象,就要创建一个或者多个新的对象。由此可以看出,提高性能的第二个原则是:避免过多的对象创建操作。
二、IO性能优化
许多应用要进行大规模的数据处理,而IO操作正属于那种细微的改动会导致巨大性能差异的地方。本文的例子来自对一个文字处理应用的性能优化,这个文字处理应用要对大量的文本进行分析和处理。在文字处理应用中,读取和处理输入文本的时间很关键,优化该应用所采用的措施为上面指出的性能优化原则提供了很好的例子。
影响Java IO性能最主要的原因之一在于大量地使用单字符IO操作,即用InputStream.read()和Reader.read()方法每次读取一个字符。 Java的单字符IO操作继承自C语言。在C语言中,单字符IO操作是一种常见的操作,比如重复地调用getc()读取一个文件。C语言单字符IO操作的效率很高,因为getc()和putc()函数以宏的形式实现,且支持带缓冲的文件访问,因此这两个函数只需要几个时钟周期就可以执行完毕。在Java 中,情况完全不同:对于每一个字符,不仅要有一次或者多次方法调用,而且更重要的是,如果不使用任何类型的缓冲,要获得一个字符就要有一次系统调用。虽然一个依赖read()的Java程序可能在表现、功能上和C程序一样,但两者在性能上不能相提并论。幸而,Java提供了几种简单的办法帮助我们获得更好的IO性能。
缓冲可以用以下两种方式之一实现:使用标准的BufferedReader和BufferedInputStream类,或者使用块读取方法一次读取一大块数据。前者快速简单,能够有效地改进性能,且只需少量地增加代码,出错的机会也较少。后者也即自己编写代码,复杂性略有提高——当然也说不上困难,但它能够获得更好的效果。
为测试不同IO操作方式的效率,本文用到了六个小程序,这六个小程序读取几百个文件并分析每一个字符。表一显示了这六个程序的运行时间,测试用到了五个常见的Linux Java虚拟机:Sun 1.1.7、1.2.2和1.3 Java虚拟机,IBM 1.1.8和1.3 Java虚拟机。
这六个程序是:
- RawBytes:用FileInputStream.read()每次读取一个字节。
- RawChars:用FileReader.read()每次读取一个字符。
- BufferedIS:用BufferedInputStream封装FileInputStream,用read()每次读取一个字节的数据。
- BufferedR:用BufferedReader封装FileReader,用read()每次读取一个字符。
- SelfBufferedIS:用FileInputStream.read(byte[])每次读取1 K数据,从缓冲区访问数据。
- SelfBufferedR:用FileReader.read(char[])每次读取1 K数据,从缓冲区访问数据。
表一 |
|
Sun 1.1.7 |
IBM 1.1.8 |
Sun 1.2.2 |
Sun 1.3 |
IBM 1.3 |
RawBytes |
20.6 |
18.0 |
26.1 |
20.70 |
62.70 |
RawChars |
100.0 |
235.0 |
174.0 |
438.00 |
148.00 |
BufferedIS |
9.2 |
1.8 |
8.6 |
2.28 |
2.65 |
BufferedR |
16.7 |
2.4 |
10.0 |
2.84 |
3.10 |
SelfBufferedIS |
2.1 |
0.4 |
2.0 |
0.61 |
0.53 |
SelfBufferedR |
8.2 |
0.9 |
2.7 |
1.12 |
1.17 |
表一是调整Java VM和程序启动配置之后,处理几百个文件的总计时间。从表一我们可以得到几个显而易见的结论:
- InputStream比Reader高效。一个char用两个字节保存字符,而byte只需要一个,因此用byte保存字符消耗的内存和需要执行的机器指令更少。更重要的是,用byte避免了进行Unicode转换。因此,如果可能的话,应尽量使用byte替代char。例如,如果应用必须支持国际化,则必须使用char;如果从一个ASCII数据源读取(比如HTTP或MIME头),或者能够确定输入文字总是英文,则程序可以使用byte。
- 无缓冲的字符IO实在很慢。字符IO本来就效率不高,如果没有缓冲,情形就更糟了。因此,在编程实践中,至少应该为流加上缓冲,它可以让IO性能提高10倍以上。
- 带有缓冲的块操作IO要比缓冲的流字符IO快。对于字符IO,虽然缓冲流避免了每次读取字符时的系统调用开销,但仍需要一次或多次方法调用。带缓冲的块IO比缓冲流IO快2到4倍,比无缓冲的IO快4到40倍。
从表一不易看出的一点是,字符IO可能抵消速度较快的Java VM带来的优势。在大多数性能测试中,IBM 1.1.8 Linux Java VM大约有Sun 1.1.7 Linux Java VM两倍那么快,然而在RawBytes和RawChars的测试中,结果显示出两者差不多慢,它们花在系统调用上的额外时间开销掩盖了较快Java VM带来的速度优势。
块IO还有另一个不那么明显的优点。缓冲的字符IO有时对组件之间的协调有更多的要求,带来更多的出错机会。很多时候,应用中的IO操作由一个组件完成,应用把一个Reader或InputStream传递给组件,然后,IO组件处理流的内容。一些IO组件可能错误地假设它所操作的流是一个带缓冲的流,但又不在文档中说明这方面的需求,或者虽然IO组件在文档中说明了这方面的要求,但应用的开发者却未能留意到这一点。在这些情况下,IO 操作将不按意料之中地那样带有缓冲,从而带来严重的性能问题。如果改用块IO,这类情形就不可能出现(因此,设计软件组件时,最好能够做到组件不可能被误用,而不要依赖于文档来保证组件的正确使用)。
从上述简单的测试可以看出,用最直接的方法完成一个简单任务,比如读取文本,可能比细心选择的方法慢40到60倍。在这些测试中,程序在提取和分析每一个字符时进行了一些计算。如果程序只是把数据从一个流复制到另一个流,则非缓冲的字符IO和块IO之间的性能差异将更加明显,块IO的性能将达到非缓冲字符IO的300到500倍。
三、再次测试
性能调整必须反复地进行,因为在主要性能问题解决之前,次要性能问题往往不能显露出来。在文字处理应用的例子中,最初的分析显示出程序把绝大部分的时间花费在读取字符上,加上缓冲功能后性能有了戏剧性的提高。只有在程序解决了主要的性能瓶颈(字符IO)之后,剩余的性能热点才显现出来。对程序的第二次分析显示出,程序在创建String对象上花费了大量的时间,而且看起来它为输入文本中的每一个单词创建了一个以上的String对象。
本文例子中的文本分析应用采用了模块化的设计,用户可以结合多个文本处理操作达到预期的目标。例如,用户可以结合运用单词标识器部件(读取输入字符并把它们组织成单词)和小写字母转换器部件(把单词转换成小写字母),以及一个还原器部件(把单词转换成它们的基本形式,例如,把 jumper和jumped转换成jump)。
虽然模块化构造具有很明显的优点,但这种处理方式会对性能产生负面影响。由于部件之间的接口是固定的(每一个部件都以一个String 作为输入,并输出另一个String),部件之间也许存在一些重复的操作。如果有几个部件经常组合在一起使用,对这些情形进行优化是值得的。
在这个文字处理系统中,从实际使用情况可以看出,用户几乎总是在使用单词标识器部件之后,紧接着使用小写字母转换器部件。单词标识器分析每一个字符,寻找单词边界,同时填充一个单词缓冲区。标识出一个完整的单词之后,单词标识器部件将为它创建一个String对象。调用链中的下一个部件是小写字母转换器部件,这个部件将在前面得到的String上调用String.toLowerCase(),从而创建了另一个String对象。对于输入文本中的每一个单词,顺序使用这两个部件将生成两个String对象。由于单词标识器部件和小写字母转换器部件频繁地一起使用,因此可以添加一个经过优化的小写字母单词标识器,这个标识器具有原来两个部件的功能,但只为每一个单词创建一个String对象,从而有利于提高性能。表二显示了测试结果:
表二 |
|
|
Sun 1.1.7 |
IBM 1.1.8 |
Sun 1.2.2 |
Sun 1.3 |
IBM 1.3 |
A |
单词标识 |
23.0 |
3.6 |
10.7 |
2.6 |
2.9 |
B |
单词标识 + 小写字母转换 |
39.6 |
6.7 |
13.9 |
3.9 |
3.9 |
C |
结合单词标识和小写字母转换 |
29.0 |
3.8 |
12.9 |
3.1 |
3.1 |
|
临时字符串创建时间 (B-C) |
10.6 |
2.9 |
1.0 |
0.8 |
0.8 |
从表二我们可以得到几个有用的发现:
- 对于Java VM 1.1,简单的优化引人注目地提高了性能:大约在百分之二十五到百分之四十五之间。最后一行显示出,创建临时String对象占用了程序A和程序B之间百分之六十到九十的性能增加值。另外,正如其他几个测试项目显示出的,IBM Java VM 1.1运行速度要比Sun Java VM 1.1快。
- 对于1.2和1.3的Java VM,两个版本之间的性能差异不再那么大,大约只有百分之十到百分之二十五之间,相当于创建临时String对象所耗时间的百分比。这个结果表明,在创建对象实例方面,版本较高的Java VM确实提高了效率,但过多的对象创建操作对性能的影响仍旧值得注意。
- 对于这类创建大量小型对象的操作,1.3版本的Java VM要比1.1和1.2版本的Java VM快得多。
性能优化是一种需要反复进行的工作。在开发工作的早期阶段开始收集性能数据是值得的,因为这样可以尽早地找出和调整性能热点。通过一些比较简单的改进,比如为IO操作增加缓冲,或在适当的时候用byte替代char,常常可以戏剧性地提高应用的性能。另外,不同的VM之间也有着很大的性能差异,简单地换上一个速度较快的Java VM,可能就让程序的性能向预期的目标跨出了一大步。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!813.entry
Java类如下 public static void downloadFile(String path,String fileName) { try { // 获得JSF上下文环境 FacesContext context = FacesContext.getCurrentInstance(); // 获得ServletContext对象 ServletContext servletContext = (ServletContext) context .getExternalContext().getContext(); // 取得文件的绝对路径 String realName = servletContext.getRealPath(path) + "/" + fileName; HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext .getCurrentInstance().getExternalContext().getResponse(); downloadFile(httpServletResponse,realName,fileName); } catch (IOException e) { e.printStackTrace(); } FacesContext.getCurrentInstance().responseComplete(); } public static void downloadFile(HttpServletResponse response,String realName,String fileName) throws IOException { response.setHeader("Content-disposition", "attachment; filename=" + fileName); response.setContentType("application/x-download"); //File exportFile = new File(realName); //response.setContentLength((int) exportFile.length()); ServletOutputStream servletOutputStream = response.getOutputStream(); byte[] b = new byte[1024]; int i = 0; FileInputStream fis = new java.io.FileInputStream(realName); while ((i = fis.read(b)) > 0) { servletOutputStream.write(b, 0, i); } } 使用方法 1、在backing bean的方法中调用函数1即可。如Abean中download方法调用了该方法,前台可以这样调用: <h:commandButton value="download" action="#{aBean.download}"></h:commandButton> 或者 <h:commandLink value="download" action="#{fileUploadForm.download}"></h:commandLink> 2、jsp页面可以这样调用: <%@ page contentType="text/html; charset=gb2312"%><%@page import="java.io.*"%><% String filename = ""; if (request.getParameter("filename") != null) { filename = request.getParameter("filename"); } try { framework.util.FileUtils.downloadFile(response,getServletContext().getRealPath(filename),filename); } catch(final IOException e) { System.out.println ( "出现IOException." + e ); } catch(final IllegalStateException e) { System.out.println ( "出现IllegalStateException." + e ); } %> 于是jsf页面我们可以借助outputlink来调用该页面 <h:outputLink id="downloadfile" value="#{page/FileDownload.jsp?filename=}"> <t:outputText value="下载文件" /> </h:outputLink> 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!812.entry
最近在单位的电脑上调试程序。由于使用了JPA和OperaMasks这两个在容器启动的时候需要扫描实体Bean和LiteBean的框架,所以,在Tomcat启动初期,系统经常由于大量的对象被创建而不能回收导致PermGen Space Over Flow。在网上经过一番搜索,终于找到了如下的启动参数,经测试多次热部署不会导致VM崩溃。 -Xms128m -Xmx512m -Xmn96m -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=7 -XX:GCTimeRatio=19 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!799.entry
【转自】http://hi.baidu.com/kangqii/blog/item/f8495043f70c3a1572f05d4e.html 最近要做一个CRM,登录时数字证书要有验证的功能,在用户登录时除了效验用户名密码,还需验证其数字证书 相关资源:IBM developerWroks中国中的tomcat4中使用SSL,javaeye中的Acegi X.509双向认证 与tomcat4中使用SSL中的异同:jdk1.4中已经包含JSSE 与AcegiX.509双向认证中的异同:tomcat6配置文件多了SSLEnabled="true"属性 1.生成CA证书目前不使用第三方权威机构的CA来认证,自己充当CA的角色 1.创建私钥 :C:\OpenSSL\apps>openssl genrsa -out root/root-key.pem 1024 2.创建证书请求 :C:\OpenSSL\apps>openssl req -new -out root/root-req.csr -key root/root-key.pem 3.自签署证书 :C:\OpenSSL\apps>openssl x509 -req -in root/root-req.csr -out root/root-cert.pem -signkey root/root-key.pem -days 3650 4.将证书导出成浏览器支持的.p12格式 :C:\OpenSSL\apps>openssl pkcs12 -export -clcerts -in root/root-cert.pem -inkey root/root-key.pem -out root/root.p12 2.生成server证书 1.创建私钥 :C:\OpenSSL\apps>openssl genrsa -out server/server-key.pem 1024 2.创建证书请求 :C:\OpenSSL\apps>openssl req -new -out server/server-req.csr -key server/server-key.pem 3.自签署证书 :C:\OpenSSL\apps>openssl x509 -req -in server/server-req.csr -out server/server-cert.pem -signkey server/server-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 4.将证书导出成浏览器支持的.p12格式 :C:\OpenSSL\apps>openssl pkcs12 -export -clcerts -in server/server-cert.pem -inkey server/server-key.pem -out server/server.p12 3.生成client证书 1.创建私钥 :C:\OpenSSL\apps>openssl genrsa -out client/client-key.pem 1024 2.创建证书请求 :C:\OpenSSL\apps>openssl req -new -out client/client-req.csr -key client/client-key.pem 3.自签署证书 :C:\OpenSSL\apps>openssl x509 -req -in client/client-req.csr -out client/client-cert.pem -signkey client/client-key.pem -CA root/root-cert.pem -CAkey root/root-key.pem -CAcreateserial -days 3650 4.将证书导出成浏览器支持的.p12格式 :C:\OpenSSL\apps>openssl pkcs12 -export -clcerts -in client/client-cert.pem -inkey client/client-key.pem -out client/client.p12 4.根据root证书生成jks文件 C:\OpenSSL\apps\root>keytool -import -v -trustcacerts -storepass password -alias root -file root-cert.pem -keystore root.jks 5.配置tomcat ssl,修改conf/server.xmltomcat6中多了SSLEnabled="true"属性 keystorefile, truststorefile设置为你正确的相关路径 xml 代码 - <connector secure="true" scheme="https" protocol="HTTP/1.1" port="8443"
- sslenabled="true" maxhttpheadersize="8192" maxthreads="150"
- minsparethreads="25" maxsparethreads="75" enablelookups="false"
- disableuploadtimeout="true" acceptcount="100" sslprotocol="TLS"
- clientauth="true" keystorefile="d:/path/bin/x509/server.p12"
- keystoretype="PKCS12" keystorepass="123456" truststorefile="d:/path/bin/x509/root.jks"
- truststoretype="JKS" truststorepass="123456"/>
6.将root.p12,client.p12分别导入到IE中去(打开IE->;Internet选项->内容->证书) root.p12导入至受信任的根证书颁发机构,client.p12导入至个人 7.访问你的应用 http://ip:8443,如果配置正确的话会出现请求你数字证书的对话框 8.在jsp中取得符合x.509格式的证书 java 代码 - <%
- //获得certificate chain
- X509Certificate[] ca=(X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
- if(ca==null)
- {
- out.println("No cert info!");
- } else {
- String serial=ca[0].getSerialNumber().toString();
- String DN=ca[0].getSubjectDN().toString();
- }
- %>
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!792.entry
切换到SSL再切换回来 原文链接:http://hi.baidu.com/jcjxwxb/blog/item/a4a6f301b4416e06738da598.html Switch to SSL and back again ◆ 问题 怎样在应用程序中使用SSL。 ◆ 背景 大多数组织把信息资料当作他们的最宝贵的资产。保护信息的工作不仅仅是象开发者和管理员似的一个专职的工作而已。信息的保护必须从许多不同的角度实施。数据库,用户界面和商业层只是这些需要你注意的关键领域的某些方面。甚至通往服务器机房的门都必须被锁住。更重要的是,一旦这些数据经由因特网离开了这些建筑物,它的安全就会被一些很有才能的在网络上用电子手段刺探的人危及。 在本方法中,我们示范怎样在容器中启动Secure Socket Layer (SSL)。另外,我们为你介绍Struts SSL 的扩展,HTTP/HTTPS切换库——一个允许你声明哪些Action启用了SSL 的程序包。这个很好用的程序包允许你在SSL-激活的Action和禁止的Action之间很容易地转换。 进入本方法之前,让我们涉及一些SSL 基础。SSL原来是一个由Netscape 开发的网络协议,来提供认证和传输保密性。SSL 协议属于TCP/IP之上的网络层,但是低于应用协议,比如HTTP ,LDAP和IMAP。在传送前客户端检验服务器的身份。 类似的,服务器端也可能可选地检验客户端的身份。为了阻断数字偷听者的努力,这两个主机就一个密码算法达成一致协议,通常叫做一个密码,来加密和解密传送的数据。除提供机密性之外,SSL 对传送过程进行审计,来保证传输的数据在途中未被篡改。整个SSL 的工作方式超出了这个方法的范围,但是我们鼓励你利用一些在资源部分提到的资源探索SSL。 大多数容器,包括Tomcat ,提供SSL 服务。在你容器上启动SSL 确保你的通讯是安全的和不被篡改的。容器能通过检查URL 方案识别出SSL 通讯。URL 方案是URL 中冒号前面的那部分。HTTP 非保密协议是用冒号之前的“HTTP”识别的。例如,http://127.0.0.1指出一个非保密http 协议。一个“s”放置在http 后,来表明该请求希望使用SSL 传送。例如,https://127.0.0.1是用于一个SSL http 请求的适当的URL。 许多应用程序兼具SSL 和非SSL连接。这里面的挑战是当你要一个安全通信的时候,怎样格式化你的连接成为SSL 。作为默认,Struts 链接标签使用与你正在浏览的当前页面相同的URI 方案。所以,如何从一个安全的页面到一个不安全的页面建立一个链接,或者反过来怎么做呢?一个选择是建立你自己的链接标签来写SSL 链接,但是这会迅速地导致维护的负担,只要你需要把链接从一个转变到另一个的任何时候。现在如果你能通过在struts-config.xml 文件中声明来做这个,你会成为一个幸福的露营者了。你可以使用标签来写你的链接,但是一旦情况发生变化,你可以在struts-config.xml 文件中声明性地改变它们。很幸运,那刚好是Struts SSL HTTP/HTTPS 切换扩展库为你做的。在这个方法中,我们展示怎样用Tomcat 设立SSL。然后我们展示怎样在你的应用程序中设立和使用ssl-ext 来加密网上传输的数据。完整的SSL 认证的说明超出了本书的范围;实际上,我们集中在Struts 开发的SSL方面。 ◆ 方法 本方法的实现被分成三个部分。在前边部分我们展示怎样在Tomcat 上启动SSL 。接下来,我们描述究竟需要做什么来完成在你的应用程序中安装SSL-ext。最后,我们在代码中应用SSL-ext。现在开始! 步骤1:在Tomcat 上启动SSL 1 从http://java.sun.com/products/jsse/下载并安装JSSE 1.0.2(或者更新的版本)。此网址告诉你安装JSSE必须知道的所有动作。注意,Java 2 SDK Standard Edition v1.4已经预先捆绑了JSSE。如果你正在使用JDK v1.4,你可以跳个过这个步骤。 2 创建一个证书keystore(密钥仓库)。键入以下命令之一: 对Windows: %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA -keystore D:\server.keystore -validity 365 对UNIX: $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA 你会被问到许多问题。作为练习目的,仅仅键入对你来说有意义的无论什么都可以。当要实现到一个受控环境中时,找你的系统管理员。 3 从conf/server.xml 中取消SSL HTTP/1.1 Connector 的注释。该被取消注释的连接器应该看起来像这样(特别地,记下端口号,随后必须知道): <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:/server.keystore" keystorePass="changeit" /> <!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> 4 重新启动Tomcat。用浏览器访问https://127.0.0.1:8443来确定所有动作如设计运行。如果Tomcat 显示该页面,那么就奏效了。 祝贺,你已经在Tomcat 上激活了SSL。 步骤2:为应用程序设置Struts SSL HTTP/HTTPS 切换扩展(SSL-ext) 因为ssl-ext 是一个Struts 扩展库,它的安装与Struts类似就不奇怪了。如果你已经安装了Struts,以下用法说明会似曾相识。 1 从http://sslext.sourceforge.net 下载ssl-ext 包。该包包含一个可工作的样例应用程序。把下载包Unzip 到Tomcat webapps 目录。在以下步骤中,你要从样例应用程序复制许多文件来创建你自己的ssl-ext 应用程序。 2 把sslext.jar 文件从样例应用程序复制到WEB-INF/lib 目录中。 3 把sslext.tld 文件从样例应用程序复制到你的WEB-INF/lib 目录中。 4 把以下片断和其他taglib一起放在web.xml 文件中: <taglib> <taglib-uri>/WEB-INF/sslext.tld</taglib-uri> <taglib-location>/WEB-INF/sslext.tld</taglib-location> </taglib> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 配置ssl的tld --> <jsp-config> <taglib> <taglib-uri>/WEB-INF/sslext.tld</taglib-uri> <taglib-location>/WEB-INF/sslext.tld</taglib-location> </taglib> </jsp-config> 5 把插件标签添加到struts-config.xml 文件中。特别记下httpsPort 属性。它必须与用来配置Tomcat 的相同(步骤1,说明3)。如你可能预期的,该httpPort 属性应该和在conf/server.xml 中找到的Tomcat 配置相配。下列两个定义的值是Tomcat 的值。 <plug-in className="org.apache.struts.action.SecurePlugIn"> <set-property property="httpPort" value="8080"/> <set-property property="httpsPort" value="8443"/> <set-property property="enable" value="true"/> </plug-in> 做完这些,你随时可以开始使用ssl-ext了。 步骤3:使用ssl-ext 建立一个应用程序 我们通过建立一个应用程序来示范ssl-ext 运转。在这个步骤中,我们展示ssl-ext 是如何被用来为指向ssl 安全页面的链接格式化URL 的。相反的,我们示范为指向普通的非SSL页面的链接格式化URL。我们将知道给页面提供安全保障是通过对struts-config.xml 做一个小的改变完成的。清单7.6 展示struts-config.xml 对应一个示例应用程序的动作映射。 清单7.6 Struts-config.xml <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <global-forwards type="org.apache.struts.action.ActionForward"> <forward name="unsecured" path="/unsecured.do"/> <forward name="secured" path="/secured.do"/> <forward name="menu" path="/menu.do"/> </global-forwards> <form-beans> <form-bean name="dummyForm" type="com.strutsrecipes.ssl.forms.Dummy" /> </form-beans> <action-mappings type="org.apache.struts.config.SecureActionConfig"> <action path="/menu" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/pages/menu.jsp"> <set-property property="secure" value="false"/> </action> <action path="/unsecured" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/pages/unsecured.jsp"> <set-property property="secure" value="false"/> </action> <action path="/secured" type="org.apache.struts.actions.ForwardAction" parameter="/WEB-INF/pages/secured.jsp"> <set-property property="secure" value="true"/> </action> <action path="/securesubmit" type="org.apache.struts.actions.ForwardAction" name="dummyForm" parameter="/WEB-INF/pages/securesubmit.jsp"> <set-property property="secure" value="true"/> </action> <action path="/unsecuresubmit" type="org.apache.struts.actions.ForwardAction" name="dummyForm" parameter="/WEB-INF/pages/unsecuresubmit.jsp"> <set-property property="secure" value="false"/> </action> </action-mappings> <plug-in className="org.apache.struts.action.SecurePlugIn"> <set-property property="httpPort" value="8080"/> <set-property property="httpsPort" value="8443"/> <set-property property="enable" value="true"/> </plug-in> </struts-config> 要用SSL 使一个Action 安全,把一个<set-property>标签嵌套在Action 标签内。 该property 属性的值永远是secure。该值属性的true 值表明我们需要URL通过设置URI 方案“https”来发出一个SSL 请求。类似的,false 值表明该页面是普通的不安全的页面;该URI 方案应该是“http”。一个any 值默认表示采用当前页的方案。虽然你可以使 用一个普通的Struts 链接标签达到相同效果,但是该any 值让你可以通过修改Struts-config 文件把它改成true 或者false 。 因为该安全属性不被默认动作映射支持,我们需要在(处)重载它。清单7.7示范ssl-ext 链接标签如何被用来产生一个协议格式的URL 。 清单7.7 menu.jsp: JSP 使用 ssl-ext 链接标签 <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%> <%@ taglib uri="/WEB-INF/sslext.tld" prefix="sslext"%> <html:html> <h1>SSL: Menu</h1> <h2>Links</h2> <br><sslext:link forward="unsecured">unsecured</sslext:link> <br><sslext:link forward="secured">secured</sslext:link> <h2>submit to secured</h2> <sslext:form action="/securesubmit" > <br><html:text property="name" value=""/> <html:submit/> </sslext:form> <h2>submit to unsecured</h2> <sslext:form action="/unsecuresubmit" > <br><html:text property="name" value=""/> <html:submit/> </sslext:form> </html:html> 指向安全页面的链接前缀https ,指向不安全的的页面的链接被格式化成通常的http 。例如,在处的链接显示为“不安全的”http://127.0.0.1:8080/ssl/unsecured.do,而在处的链接显示为“安全的”https://127.0.0.1:8443/ssl/secured.do。 在清单7.7中最显著的结果是我们从未指定页面使用SSL,是否是安全的。该信息是定义在struts-configxml 文件中的。在清单7.7中处的链接标签映射到清单7.6的处。相似的,处映射到处,处映射到处,处映射到处。当写URL 的协议段的时候,该ssl:ext 链接标签查阅struts-config.xml 文件。要使用该ssl-ext taglib,你需要像我们在处已经做的那样声明它。 提交工作以大体上相同的方式进行,只是你必须从ssl-ext 名字空间(处)(处)使用表单标签。 到这里,应用ssl-ext 的过程在上述步骤中得到完全解决。 ◆ 讨论 因为步骤1和步骤2是显而易见的,我们无需回顾那些部分。步骤3更有意思得多,并且值得仔细考察。JSP 的工作很简单,不考虑sslext 名字空间的话,它看起来几乎无异于任何Struts JSP 。代替用Struts 链接标签写链接的是我们使用sslext 名字空间当中相同的标签。在这些场景后面,这些标签是使用动作映射来把URI 方案设置为http 或者https 的。要用https 写URL,把动作映射上的secure 属性设置为true。要把URL 写成“http”,把它设置为false。提交的过程也是一样,只是你使用sslext 名字空间的表单标签而不是Struts 的html 名字空间。 还有一个标签我们尚未讨论过。pageScheme标签使用secure属性 来强制一个到http 或者https 的重定向。例如,一个到<sslext:pageScheme secure="false">的页面的https 请求重定向该请求到http,而不管实际上该请求是用https发起的。 如果没有ssl-ext,用以保证URL 使用正确前缀方案的工作将会更加困难。代替通过在JSP 中修整每个ssl 链接来处理这个问题的是,我们可以从struts-conf. xml 文件中来管理它。如此,维护性方面的报偿是很客观的。例如,如果我们有到相同Struts Action 的20个链接,那么为了改变链接以使用SSL,必须对20个JSP 做改变。同等的改变在ssl-ext 下只需要对struts-config.xml的仅仅一个改动!犯太多罪过的是人,避免错误的是神。 最优方法 审慎地使用SSL——SSL 是一个认证和使应用程序安全的已被证实的、可靠的、灵活的,并且流行的办法,但是你应该仅当需要时才使用它。所有SSL 的传送都加密并解密数据。取决于选择的密码,这些运算会影响应用程序的性能。 ◆ 参考 n [SSLDOC] SSL Overview n [SSLEXT] SSL Extension for Struts HTTP/HTTPS switching n [SSLSPEC] SSL Specification n [SSLTOM] SSL Tomcat Configuration n [URI] W3 URI Specification 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!791.entry
【转自】http://caterpillar.onlyfun.net/Gossip/JSPServlet/JSPServlet.htm 雖 是所謂的編程式安全,不過還是搭配之前所談及的宣告安全環境設定來達成,您可以從HttpServletRequest的 getUserPrincipal()來取得代表登入使用者的Principal,或是使用getRemoteUser()方法來取得登入的使用者名稱。 一個搭配Role設定的方法是isUserInRole(),若您的使用者已經通過驗證,可以藉由這個方法於程式中判定登入的使用者其Role為何,並進 一步決定可使用的資源,像是可以決定使用者是否出現進階使用者選單、試用帳號是否過期之類,這是單純對URL作防護的設定所無法達到的,例如: ... if(request.isUserInRole("manager")) { // 顯示管理者選單 } else { // 顯示一般使用者選單 } ... 若您事先無法決定Role名稱,則可以透過設置<security-role-ref>,將程式中的Role名稱,連結至 <security-role>的Role名稱,例如:
... <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>onlyfun.caterpillar.HelloServlet</servlet-class> <security-role-ref> <role-name>manager</role-name> <role-link>admin</role-link> </security-role-ref> </servlet>
<servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/HelloServlet</url-pattern> </servlet-mapping>
<security-role> <role-name>admin</role-name> <role-name>normal</role-name> </security-role> ... 如此一來,您的程式中的manager,實際上就會對應至admin的Role名稱,如此,就不用擔心在程式中寫死Role名稱。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!789.entry
【转自】http://caterpillar.onlyfun.net/Gossip/JSPServlet/JSPServlet.htm Http Basic Authentication 會讓瀏覽器出現對話方塊,以供使用者輸入名稱與密碼,無法自行設計登入畫面,若要結束目前會話階段,則要關閉瀏覽器。 您可以在web.xml中設定基於表單的驗證方式,以 使用宣告式安全(Http Basic Authentication) 中的例子來說,可以修改為: <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <security-role> <role-name>foo</role-name> </security-role> <security-constraint> <display-name>SecurityConstraint</display-name> <web-resource-collection> <web-resource-name>Secret Information</web-resource-name> <url-pattern>/secure/info.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>foo</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/pages/login.jsp</form-login-page> <form-error-page>/pages/error.jsp</form-error-page> </form-login-config> </login-config> </web-app> 在<login-config>中,修改驗證方式為FORM,並設定了登入表單的頁面所在,您可以設計一個login.jsp:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>請登入</title> </head> <body> <h2>請登入</h2><br> <form action="j_security_check" method="POST"> Username: <input type="text" name="j_username" value="" /> <br /> Password: <input type="password" name="j_password" value="" /> <br /> <input type="submit" value="login" /> </form> </body> </html> 當使用者請求受保護的URL時,會轉至login.jsp,發送表單的動作網址為j_security_check,而使用者 名稱與密碼,則必須使用j_username與j_password請求參數發送,若驗證正確,則會將流程轉至原先請求的位址,若失敗,則流程會轉至所設 定的錯誤網頁。 如果要令此次登入失效,則設計一個令HttpSession執行invaldate()方法的請求即可。 如果您的Web伺服器作好支援SSL的設定,則您可以在web.xml中設定使用SSL,以上例來說,您可以在web.xml中增加設定:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <security-role> <role-name>foo</role-name> </security-role> <security-constraint> <display-name>SecurityConstraint</display-name> <web-resource-collection> <web-resource-name>Secret Information</web-resource-name> <url-pattern>/secure/info.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>foo</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/pages/login.jsp</form-login-page> <form-error-page>/pages/error.jsp</form-error-page> </form-login-config> </login-config> </web-app> 如此,當流程轉發至表單網頁,直至使用者名稱、密碼送出,都會以SSL的方式進行加密傳送。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!788.entry
【转自】http://caterpillar.onlyfun.net/Gossip/JSPServlet/JSPServlet.htm 要 防護應用程式的資源,驗證(Authentication)與授權(Authorization)是基本需求。 驗證基本上就是識別登入系統的使用者,是否為系統所允許的身份,是否如其所宣告的身份。授權則是對資源加以管理保護,針對請求資源的使用者,檢查其是否具 備足夠的權限。 在Java EE中,容器提供驗證、授權服務,透過適當的組態設定或API,您可以讓容器為您管理大部份的驗證與授權,要運用Java EE所提供的驗證、授權服務,基本上您必須了解幾個概念名詞: 中文稱之為「域」 或「範圍」,在Java EE的規範之中,Realm指的是身份驗證資料的來源,Realm可能是記憶體、檔案、憑證、資料庫、網路(如LDAP)等。 例如若為Web容器部份,Tomcat預設是將身份驗證資料儲存於tomcat-users.xml檔案之中,在啟動Tomcat之後,將該檔案載入記憶 體作為Memory Realm,您也可以將之改為JDBC Realm。Glassfish(原 Sun Java System Application Server)的Realm可以是 File Realm(一般使用者)、Admin Realm(管理者)、 Certificate realm等,您可以在Glassfish管理介面中加以設定。 在系統上會有 User,而在多人共用的系統會有不同的權限,為了方便管理使用者的權限,通常會定義Group,將User歸類於某些Group,您可以直接將權限設定 給Group,而Group下的User就擁有該權限。 然而在設計應用程式時,並無法事先得知系統上會有哪些Group,所以您無法直接在應用程式中使用Group名稱 來設定資源的權限,因此在Java EE的規範中,是定義資源可以被哪些Role存取,至於如何將系 統上的Group對應至應用程式的Role不在Java EE的規範之中,而是依伺服器廠商的實作而有所不同。 在設計應用程式 時,因無法事先得知將被部署至的系統上,會有哪些User,因此在Java EE中,登入系統的使用者是定義為Principal,您是依據Proincipal於應用程式中作識別驗證,如何將系統上的User對應至應用程式的 Principal不在Java EE的規範之中,而是依伺服器廠商的實作而有所不同。 例如,Tomcat可以在tomcat-users.xml中設定Role與User的對應,而Glassfish,則可以在sun-web.xml、 sun-ejb-jar.xml、sun-application.xmll等檔案中設定Role與Group的對應。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!787.entry
【转自】http://caterpillar.onlyfun.net/Gossip/JSPServlet/JSPServlet.htm Web容器所提供的宣告安全管理,主要是針對URL來作防護,將您所想要經過驗證、授權才可以存取的Web目錄、網頁或檔案等,在web.xml中作設定,當有使用者想要存取時,就必須輸入名稱與密碼進行驗證,並且必須被授于正確的權限才可存取。 例如若Web應用程式的/serure/info.jsp僅能是foo的Role方可存取,則您可以在web.xml中設定如下: <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <security-role> <role-name>foo</role-name> </security-role> <security-constraint> <display-name>SecurityConstraint</display-name> <web-resource-collection> <web-resource-name>Secret Information</web-resource-name> <url-pattern>/secure/info.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>foo</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>file</realm-name> </login-config> </web-app> <security-role>讓您設定這個Web應用程式中將參與的角色,若有多個角色,則可以用多個<security>加以設定。 在需要防護的URL部份,是在<url-patern>中指定,指定的方式從Web應用程式根目錄開始,若您想防護整個Web應用程式,則可以使用/*來指定,若有多個URL需要防護,則可以使用多個<url-pattern>。 <auth-constraint>中指定哪個Role可以存取指定URL的Role,若有多個Role則使用多個<role-name>來指定。 在<login-config>中,<auth-method>用來指定驗證使用者的方式,指定BASIC的話將授用Http Basic Authentication,也就是丟出「WWW-Authenticate: Basic realm=""」這樣的回應標頭給瀏覽器,瀏覽器將顯示一個輸入對話方塊,要求使用者輸入名稱與密碼,若有輸入名稱與密碼,則瀏覽器會將之以 BASE64方式編碼,以「Authorization: Basic 編碼內容」的請求標頭傳給伺服器。 輸入名稱與密碼在伺服端可能通過驗證,下一步是檢查登入的使用者其對應的Role是否有足夠的權限存取資源,若否則會顯示403 Forbidden。 使用者與Role的對應,在Tomcat上,可以編輯tomcat-users.xml來設定,例如:
... <tomcat-users> <role rolename="foo"/> <user username="caterpillar" password="123456" roles="foo"/> ... </tomcat-users>
若是在Glassfish上,可以在Glassfish的管理介面上新增新的使用者並設定其群組,假設分別為caterpillar與orzGroup,則您可以在WEB-INF下編輯sun-web.xml,設定Role與Group的對應:
... <security-role-mapping> <role-name>foo</role-name> <group-name>orzGrooup</group-name> </security-role-mapping> ...
在設定<web-resource-collection>時,預設是所有的HTTP請求都需要經過驗證與授權,您可以使用<http-method>來設定哪些HTTP請求才需要經過驗證與授權,例如設定GET與POST需要經過驗證與授權:
... <web-resource-collection> <web-resource-name>Secret Information</web-resource-name> <url-pattern>/secure/info.jsp</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> ...
要注意的是,一但設定<http-method>,表示有指定的HTTP請求才需要驗證與授權,但沒有被指定的則不需要,也就是說,若以上面的指定而言,GET、POST是需要驗證與授權的,但HEAD、TRACE等方法則不需要。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!785.entry
StrutsFileUpload 文件上传的简单范例 HTML HTML页面需要做两件事情,首先,表单需要指定enctype="multipart/form-dataand",其次需要一个类型为file的<input>表单控件。 <form name="myForm" method="post" action="/mywebapp/uploadMyFile.do" enctype="multipart/form-data"> Select File: <input type="file" name="myFile"> </br> <input type="submit" value="Upload File"> </form> JSP 上面的HTML标签用Struts标签代替就是以下代码: <html:form action="/uploadMyFile.do" enctype="multipart/form-data"> Select File: <html:file property="myFile"> </br> <html:submit value="Upload File"/> </html:form> ActionForm 这个ActionForm需要一个FormFile类型的字段。 一般的ActionForm import org.apache.struts.upload.FormFile; public class MyActionForm extends ActionForm { private FormFile myFile; public void setMyFile(FormFile myFile) { this.myFile = myfile; } public FormFile getMyFile() { return myFile; } } 动态ActionForms 在struts-config.xml文件中写上: <form-bean name="myForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="myFile" type="org.apache.struts.upload.FormFile"/> </form-bean> 在Action中需要怎么写呢? 其实没什么特殊的,就象和得到其他属性一样,从ActionForm中得到FormFile属性,得到后可以随意进行处理。比如我们可以从FileForm中得到文件名,文件大小,文件内容 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { MyActionForm myForm = (MyActionForm)form; // Process the FormFile FormFile myFile = myForm.getMyFile(); String contentType = myFile.getContentType(); String fileName = myFile.getFileName(); int fileSize = myFile.getFileSize(); byte[] fileData = myFile.getFileData(); ... } 文件上传的配置 在struts-config.xml的<controller>element中可以设置如下参数来配置文件上传: bufferSize - 处理文件上传的缓冲区大小,单位是字节。 默认是4096byte。 maxFileSize - 允许上传文件的大小。可以使用K,M,G为单位。 默认是250M。 multipartClass - muiltpart请求处理器类的全局标识名。默认是org.apache.struts.upload.CommonsMultipartRequestHandler tempDir - 处理文件上传的临时目录。 还有一种可选的文件上传插件的方式可提供使用,那就是实现 org.apache.struts.upload.MultipartRequestHandler接口。 可以在struts-config.xml的<controller>的multipartClass 来指定这个实现给接口的类。 ==================================== StrutsFileDownload Struts 1.2.6中推出了新的DownloadAction,用来简化下载操作。 实现DownloadAction 我们需要扩展org.apache.struts.actions.DownloadAction并实现 getStreamInfo()方法。如果我们要更改默认的缓冲大小,我们也可以覆盖 getBufferSize()方法。 实现getStreamInfo() 方法 getStreamInfo() 方法返回一个StreamInfo对象- 它是DownloadAction类的内 部类,其实是个内部接口。DownloadAction为这个接口提供了两个具体的静态内 部实现类: FileStreamInfo - 简化从磁盘系统下载文件。需要连同content type传入一个java.io.File对象到构造方法中。 ResourceStreamInfo - 简化从web应用资源下载文件。需要传入ServletContext,路径以及content type 到它的构造方法中。 在下面的例子中,我们还提供了一个以Byte array方法实现StreamInfo接口的代 码。 实现getBufferSize() 方法 DownloadAction默认返回4096byte的缓冲区我们可以覆盖这个方法来自定义用 来传输文件的缓冲区大小 范例 下面有三个例子: 使用文件 使用web应用资源 使用byte array FileStreamInfo范例 DownloadAction使用文件的例子。这个范例从struts-config.xml的action mapping的parameter属性来得到文件名。 import java.io.File; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DownloadAction; public class ExampleFileDownload extends DownloadAction{ protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Download a "pdf" file - gets the file name from the // Action Mapping's parameter String contentType = "application/pdf"; File file = new File(mapping.getParameter()); return new FileStreamInfo(contentType, file); } } ResourceStreamInfo范例 DownloadAction使用web应用资源的范例。这个范例从struts-config.xml的 action mapping的parameter属性来得到web应用资源的路径。 import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DownloadAction; public class ExampleResourceDownload extends DownloadAction { protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Download a "jpeg" file - gets the file name from the // Action Mapping's parameter String contentType = "image/jpeg"; String path = mapping.getParameter(); ServletContext application = servlet.getServletContext(); return new ResourceStreamInfo(contentType, application, path); } } Byte Array 范例 DownloadAction使用字节数组(byte array)的范例。 这个例子创建了一个实现了StreamInfo接口的ByteArrayStreamInfo内部类。 import java.io.IOException; import java.io.InputStream; import java.io.ByteArrayInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DownloadAction; public class ExampleByteArrayDownload extends DownloadAction { protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // Download a "pdf" file String contentType = "application/pdf"; byte[] myPdfBytes = null;// Get the bytes from somewhere return new ByteArrayStreamInfo(contentType, myPdfBytes); } protected class ByteArrayStreamInfo implements StreamInfo { protected String contentType; protected byte[] bytes; public ByteArrayStreamInfo(String contentType, byte[] bytes) { this.contentType = contentType; this.bytes = bytes; } public String getContentType() { return contentType; } public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); } } } 在WEB页面上使用DownloadAction 最大的疑惑是我么如何使用这个Action? 需要做两件事情: 和任何Struts的action一样,需要在struts-config.xml中进行配置。 在WEB页面中使用它对文件进行连接 下面是struts-config.xml配置的一个例子: <action path="/downloadMyPdfFile" type="myPackage.ExampleFileDownload" parameter="/foo/bar.pdf"> <action path="/downloadMyImage" type="myPackage.ExampleResourceDownload" parameter="/images/myImage.jpeg"> 那么在我们的JSP页面,可以使用类似下面的例子: <html:img action="downloadMyImage" alt="My Image" height="400" width="400"/> <html:link action="downloadMyPdfFile">Click Here to See the PDF</html:link> 注意:我们可能要将struts配置文件中<controller>属性的nocache值设置为false。如果设置为true,可能在IE上不能成功下载文件,但是在Firefox和Safari上工作正常。 <controller contentType="text/html;charset=UTF-8" locale="true" nocache="false" /> 内容部署(Content Disposition) 设置Content Disposition DownloadAction不能处理content dispositon头部。最简单的方法是在getStreamInfo()方法中设置,比如: public class ExampleFileDownload extends DownloadAction{ protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // File Name String fileName = mapping.getParameter(); // Set the content disposition response.setHeader("Content-disposition", "attachment; filename=" + new String(fileName.getBytes("gbk"), "iso8859_1"));// 设置文件名 // Download a "pdf" file - gets the file name from the // Action Mapping's parameter String contentType = "application/pdf"; File file = new File(fileName); return new FileStreamInfo(contentType, file); } } 如果需要文件名做为参数,可能需要首先把文件前面的任何路径信息先清除。 @@ Content Disposition的值 我们可以设置content disposition来下载一个文件或者在浏览器中打开一个文件。 在浏览器中打开文件的例子写法: "inline; filename=myFile.pdf" 下载的例子写法: "attachment; filename=myFile.pdf" 显示图片的话,可以使用content disposition的"inline"选项。 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!784.entry
天才女程序员离开Google了。新闻上如是说。媒体很关注她的博客内容,她一边称赞在Google的工作,一边转身而去“尝试新鲜事物”。我想到三个词:光荣、风度和潇洒。 看看我们这些人,正如范师傅的名言:同样都是在电脑上写程序滴,差别咋就这么大哩! 你有过样的光荣么?有人称你为天才么?哈,我猜你会说有!小学时常常前三名,中学也得过不少奖状吧!好汉不提当年勇,别提算啦。现在天天绞尽脑汁地在键盘上敲敲打打的,能获得多少认可呢?老板的观点是肯定而且明确的:不管你怎么做,只要实现需求,只看结果,不看过程。说实话,对于大部分需求来说,再笨的人也能找到个办法实现。你真的是天才又怎么样?其实和傻B没什么区别!你要是离职了,没人理你,趁着金融危机正好换个薪水低的。你在博客里写八百遍也白搭,因为本来就没几个人看! 没有光荣的程序员,也谈不上什么风度。别人问你在以前的公司做的怎么样呀?傻一点的就把自己的想法老实说了,什么这公司这也不好,那也不好。这是实话呀,要是感觉好的话你怎么会辞职呢?可你说实话人家就觉得你这人不好!你只好说以前的公司挺好的,然后面试人员很怀疑地看着你,问你离职的理由。这叫啥风度? 更别说什么潇洒了!也许你真的曾有过追求与理想,也许你真的炒过自己的老板,现在你还这样么?我有个非常好的朋友常对我引用某个作家话:我们总是在不知不觉中变得世俗。理想值得追求,但她太遥远。生活是实在的。没有工作就没有钱,没有钱就没法过日子。天天吃饭都得要钱,用不完的还要存下来,等着交给那些贪婪无度的房地产开发商。潇洒?潇什么洒!老老实实写你的程序去吧! 偶不系天才,偶也不系女滴,偶写程序没有光荣,偶跳槽没有风度,偶的生活一点也不潇洒。真是太让人失望了! 这虽然是事实,但偶不想这样想! 偶长大后虽然就不天才了,但偶绝对会写程序;偶不是女滴,正好有体力加班干活;偶写程序无人欣赏,但偶挣钱养家孝敬父母抚养儿女,也很光荣;偶跳槽虽然只为多点工资,但也算好合好散好来好去,虽没风度但也不失体面;你说我天天加班努力表现不潇洒,哼,虽然我不叫阿Q,可我偏说我潇洒,我潇洒,我就潇洒! 我们变不成天才女程序员了,但我们一定要快乐地活着! 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!781.entry
LDAP的英文全称是Lightweight Directory Access Protocol,一般都简称为LDAP。它是基于X.500标准的,但是简单多了并且可以根据需要定制。与X.500不同,LDAP支持TCP/IP,这对访问Internet是必须的。LDAP的核心规范在RFC中都有定义,所有与LDAP相关的RFC都可以在LDAPman RFC网页中找到。现在LDAP技术不仅发展得很快而且也是激动人心的。在企业范围内实现LDAP可以让运行在几乎所有计算机平台上的所有的应用程序从LDAP目录中获取信息。LDAP目录中可以存储各种类型的数据:电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表,等等。通过把LDAP目录作为系统集成中的一个重要环节,可以简化员工在企业内部查询信息的步骤,甚至连主要的数据源都可以放在任何地方。 LDAP目录的优势 如果需要开发一种提供公共信息查询的系统一般的设计方法可能是采用基于WEB的数据库设计方式,即前端使用浏览器而后端使用WEB服务器加上关系数据库。后端在Windows的典型实现可能是Windows NT + IIS + Acess数据库或者是SQL服务器,IIS和数据库之间通过ASP技术使用ODBC进行连接,达到通过填写表单查询数据的功能; 后端在Linux系统的典型实现可能是Linux+ Apache + postgresql,Apache和数据库之间通过PHP3提供的函数进行连接。使用上述方法的缺点是后端关系数据库的引入导致系统整体的性能降低和系统的管理比较繁琐,因为需要不断的进行数据类型的验证和事务的完整性的确认;并且前端用户对数据的控制不够灵活,用户权限的设置一般只能是设置在表一级而不是设置在记录一级。 目录服务的推出主要是解决上述数据库中存在的问题。目录与关系数据库相似,是指具有描述性的基于属性的记录集合,但它的数据类型主要是字符型,为了检索的需要添加了BIN(二进制数据)、CIS(忽略大小写)、CES(大小写敏感)、TEL(电话型)等语法(Syntax),而不是关系数据库提供的整数、浮点数、日期、货币等类型,同样也不提供象关系数据库中普遍包含的大量的函数,它主要面向数据的查询服务(查询和修改操作比一般是大于10:1),不提供事务的回滚(rollback)机制,它的数据修改使用简单的锁定机制实现All-or-Nothing,它的目标是快速响应和大容量查询并且提供多目录服务器的信息复制功能。 现在该说说LDAP目录到底有些什么优势了。现在LDAP的流行是很多因数共同作用的结果。可能LDAP最大的优势是:可以在任何计算机平台上,用很容易获得的而且数目不断增加的LDAP的客户端程序访问LDAP目录。而且也很容易定制应用程序为它加上LDAP的支持。 LDAP协议是跨平台的和标准的协议,因此应用程序就不用为LDAP目录放在什么样的服务器上操心了。实际上,LDAP得到了业界的广泛认可,因为它是Internet的标准。产商都很愿意在产品中加入对LDAP的支持,因为他们根本不用考虑另一端(客户端或服务端)是怎么样的。LDAP服务器可以是任何一个开发源代码或商用的LDAP目录服务器(或者还可能是具有LDAP界面的关系型数据库),因为可以用同样的协议、客户端连接软件包和查询命令与LDAP服务器进行交互。与LDAP不同的是,如果软件产商想在软件产品中集成对DBMS的支持,那么通常都要对每一个数据库服务器单独定制。不象很多商用的关系型数据库,你不必为LDAP的每一个客户端连接或许可协议付费 大多数的LDAP服务器安装起来很简单,也容易维护和优化。 LDAP服务器可以用“推”或“拉”的方法复制部分或全部数据,例如:可以把数据“推”到远程的办公室,以增加数据的安全性。复制技术是内置在LDAP服务器中的而且很容易配置。如果要在DBMS中使用相同的复制功能,数据库产商就会要你支付额外的费用,而且也很难管理。 LDAP允许你根据需要使用ACI(一般都称为ACL或者访问控制列表)控制对数据读和写的权限。例如,设备管理员可以有权改变员工的工作地点和办公室号码,但是不允许改变记录中其它的域。ACI可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由LDAP目录服务器完成的,所以不用担心在客户端的应用程序上是否要进行安全检查。 LDAP(Lightweight Directory Acess Protocol)是目录服务在TCP/IP上的实现(RFC 1777 V2版和RFC 2251 V3版)。它是对X500的目录协议的移植,但是简化了实现方法,所以称为轻量级的目录服务。在LDAP中目录是按照树型结构组织,目录由条目(Entry)组成,条目相当于关系数据库中表的记录;条目是具有区别名DN(Distinguished Name)的属性(Attribute)集合,DN相当于关系数据库表中的关键字(Primary Key);属性由类型(Type)和多个值(Values)组成,相当于关系数据库中的域(Field)由域名和数据类型组成, 只是为了方便检索的需要,LDAP中的Type可以有多个Value,而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织,非常的直观。LDAP把数据存放在文件中,为提高效率可以使用基于索引的文件数据库,而不是关系数据库。LDAP协议集还规定了DN的命名方法、存取控制方法、搜索格式、复制方法、URL格式、开发接口等 LDAP对于这样存储这样的信息最为有用,也就是数据需要从不同的地点读取,但是不需要经常更新。 例如,这些信息存储在LDAP目录中是十分有效的: l 公司员工的电话号码簿和组织结构图 l 客户的联系信息 l 计算机管理需要的信息,包括NIS映射、email假名,等等 l 软件包的配置信息 l 公用证书和安全密匙 什么时候该用LDAP存储数据 大多数的LDAP服务器都为读密集型的操作进行专门的优化。因此,当从LDAP服务器中读取数据的时候会比从专门为OLTP优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化,大多数的LDAP目录服务器并不适合存储需要需要经常改变的数据。例如,用LDAP服务器来存储电话号码是一个很好的选择,但是它不能作为电子商务站点的数据库服务器。 如果下面每一个问题的答案都是“是”,那么把数据存在LDAP中就是一个好主意。 l 需要在任何平台上都能读取数据吗? l 每一个单独的记录项是不是每一天都只有很少的改变? l 可以把数据存在平面数据库(flat database)而不是关系型数据库中吗?换句话来说,也就是不管什么范式不式的,把所有东西都存在一个记录中(差不多只要满足第一范式)。 最后一个问题可能会唬住一些人,其实用平面数据库去存储一些关系型的数据也是很一般的。例如,一条公司员工的记录就可以包含经理的登录名。用LDAP来存储这类信息是很方便的。一个简单的判断方法:如果可以把保数据存在一张张的卡片里,就可以很容易地把它存在LDAP目录里。 安全和访问控制 LDAP提供很复杂的不同层次的访问控制或者ACI。因这些访问可以在服务器端控制,这比用客户端的软件保证数据的安全可安全多了。 用LDAP的ACI,可以完成: l 给予用户改变他们自己的电话号码和家庭地址的权限,但是限制他们对其它数据(如,职务名称,经理的登录名, 等等)只有“只读”权限。 l 给予“HR-admins"组中的所有人权限以改变下面这些用户的信息:经理、工作名称、员工号、部门名称和部门号。 但是对其它域没有写权限。 l 禁止任何人查询LDAP服务器上的用户口令,但是可以允许用户改变他或她自己的口令。 l 给予经理访问他们上级的家庭电话的只读权限,但是禁止其他人有这个权限。 l 给予“host-admins"组中的任何人创建、删除和编辑所有保存在LDAP服务器中的与计算机主机有关的信息 l 通过Web,允许“foobar-sales"组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理(PDA)上。(如果销售人员的软件都支持LDAP,这将非常有用) l 通过Web,允许组的所有者删除或添加他们拥有的组的成员。例如:可以允许销售经理给予或禁止销售人员改变Web页的权限。也可以允许邮件假名(mail aliase)的所有者不经过IT技术人员就直接从邮件假名中删除或添加用户。“公用”的邮件列表应该允许用户从邮件假名中添加或删除自己(但是只能是自己)。也可以对IP地址或主机名加以限制。例如,某些域只允许用户IP地址以192.168.200.*开头的有读的权限,或者用户反向查找DNS得到的主机名必须为*.foobar.com。 LDAP目录树的结构
LDAP目录以树状的层次结构来存储数据。如果你对自顶向下的DNS树或UNIX文件的目录树比较熟悉,也就很容易掌握LDAP目录树这个概念了。就象DNS的主机名那样,LDAP目录记录的标识名(Distinguished Name,简称DN)是用来读取单个记录,以及回溯到树的顶部。后面会做详细地介绍。 为什么要用层次结构来组织数据呢?原因是多方面的。下面是可能遇到的一些情况: l 如果你想把所有的美国客户的联系信息都“推”到位于到西雅图办公室(负责营销)的LDAP服务器上,但是你不想把公司的资产管理信息“推”到那里。 l 你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里,资产管理组对“asset-mgmt"部分有完全的访问权限,但是不能访问其它地方。 l 把LDAP存储和复制功能结合起来,可以定制目录树的结构以降低对WAN带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息,但是欧洲的销售情况就只要每小时更新一次就行了。 刨根问底:基准DN LDAP目录树的最顶部就是根,也就是所谓的“基准DN"。基准DN通常使用下面列出的三种格式之一。假定我在名为FooBar的电子商务公司工作,这家公司在Internet上的名字是foobar.com。 o="FooBar, Inc.", c=US (以X.500格式表示的基准DN) 在这个例子中o=FooBar, Inc. 表示组织名,在这里就是公司名的同义词。c=US 表示公司的总部在美国。以前,一般都用这种方式来表示基准DN。但是事物总是在不断变化的,现在所有的公司都已经(或计划)上Internet上。随着 Internet的全球化,在基准DN中使用国家代码很容易让人产生混淆。现在,X.500格式发展成下面列出的两种格式。 o=foobar.com (用公司的Internet地址表示的基准DN) 这种格式很直观,用公司的域名作为基准DN。这也是现在最常用的格式。 dc=foobar, dc=com (用DNS域名的不同部分组成的基准DN) 就象上面那一种格式,这种格式也是以DNS域名为基础的,但是上面那种格式不改变域名(也就更易读),而这种格式把域名:foobar.com分成两部分 dc=foobar, dc=com。在理论上,这种格式可能会更灵活一点,但是对于最终用户来说也更难记忆一点。考虑一下foobar.com这个例子。当foobar.com和gizmo.com合并之后,可以简单的把“dc=com"当作基准DN。把新的记录放到已经存在的dc=gizmo, dc=com目录下,这样就简化了很多工作(当然,如果foobar.com和wocket.edu合并,这个方法就不能用了)。如果LDAP服务器是新安装的,我建议你使用这种格式。再请注意一下,如果你打算使用活动目录(Actrive Directory),Microsoft已经限制你必须使用这种格式。 更上一层楼:在目录树中怎么组织数据 在UNIX文件系统中,最顶层是根目录(root)。在根目录的下面有很多的文件和目录。象上面介绍的那样,LDAP目录也是用同样的方法组织起来的。 在根目录下,要把数据从逻辑上区分开。因为历史上(X.500)的原因,大多数LDAP目录用OU从逻辑上把数据分开来。OU 表示“Organization Unit",在X.500协议中是用来表示公司内部的机构:销售部、财务部,等等。现在LDAP还保留ou=这样的命名规则,但是扩展了分类的范围,可以分类为:ou=people, ou=groups, ou=devices,等等。更低一级的OU有时用来做更细的归类。例如:LDAP目录树(不包括单独的记录)可能会是这样的: dc=foobar, dc=com ou=customers ou=asia ou=europe ou=usa ou=employees ou=rooms ou=groups ou=assets-mgmt ou=nisgroups ou=recipes 单独的LDAP记录 DN是LDAP记录项的名字 在LDAP目录中的所有记录项都有一个唯一的“Distinguished Name",也就是DN。每一个LDAP记录项的DN是由两个部分组成的:相对DN(RDN)和记录在LDAP目录中的位置。 RDN是DN中与目录树的结构无关的部分。在LDAP目录中存储的记录项都要有一个名字,这个名字通常存在cn(Common Name)这个属性里。因为几乎所有的东西都有一个名字,在LDAP中存储的对象都用它们的cn值作为RDN的基础。如果我把最喜欢的吃燕麦粥食谱存为一个记录,我就会用cn=Oatmeal Deluxe作为记录项的RDN。 l 我的LDAP目录的基准DN是dc=foobar,dc=com l 我把自己的食谱作为LDAP的记录项存在ou=recipes l 我的LDAP记录项的RDN设为cn=Oatmeal Deluxe 上面这些构成了燕麦粥食谱的LDAP记录的完整DN。记住,DN的读法和DNS主机名类似。下面就是完整的DN: cn=Oatmeal Deluxe,ou=recipes,dc=foobar,dc=com 举一个实际的例子来说明DN 现在为公司的员工设置一个DN。可以用基于cn或uid(User ID),作为典型的用户帐号。例如,FooBar的员工Fran Smith (登录名:fsmith)的DN可以为下面两种格式: uid=fsmith,ou=employees,dc=foobar,dc=com (基于登录名) LDAP(以及X.500)用uid表示“User ID",不要把它和UNIX的uid号混淆了。大多数公司都会给每一个员工唯一的登录名,因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫Fran Smith的加入公司,如果Fran改变了她的名字(结婚?离婚?或宗教原因?),也用不着改变LDAP记录项的DN。 cn=Fran Smith,ou=employees,dc=foobar,dc=com (基于姓名) 可以看到这种格式使用了Common Name(CN)。可以把Common Name当成一个人的全名。这种格式有一个很明显的缺点就是:如果名字改变了,LDAP的记录就要从一个DN转移到另一个DN。但是,我们应该尽可能地避免改变一个记录项的DN。 定制目录的对象类型 你可以用LDAP存储各种类型的数据对象,只要这些对象可以用属性来表示,下面这些是可以在LDAP中存储的一些信息: l 员工信息:员工的姓名、登录名、口令、员工号、他的经理的登录名,邮件服务器,等等。 l 物品跟踪信息:计算机名、IP地址、标签、型号、所在位置,等等。 l 客户联系列表:客户的公司名、主要联系人的电话、传真和电子邮件,等等。 l 会议厅信息:会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。 l 食谱信息:菜的名字、配料、烹调方法以及准备方法。 因为LDAP目录可以定制成存储任何文本或二进制数据,到底存什么要由你自己决定。LDAP目录用对象类型(object classes)的概念来定义运行哪一类的对象使用什么属性。在几乎所有的LDAP服务器中,你都要根据自己的需要扩展基本的LDAP目录的功能,创建新的对象类型或者扩展现存的对象类型。 LDAP目录以一系列“属性对”的形式来存储记录项,每一个记录项包括属性类型和属性值(这与关系型数据库用行和列来存取数据有根本的不同)。下面是我存在LDAP目录中的一部分食谱记录: dn: cn=Oatmeal Deluxe, ou=recipes, dc=foobar, dc=com cn: Instant Oatmeal Deluxe recipeCuisine: breakfast recipeIngredient: 1 packet instant oatmeal recipeIngredient: 1 cup water recipeIngredient: 1 pinch salt recipeIngredient: 1 tsp brown sugar recipeIngredient: 1/4 apple, any type 请注意上面每一种配料都作为属性recipeIngredient值。LDAP目录被设计成象上面那样为一个属性保存多个值的,而不是在每一个属性的后面用逗号把一系列值分开。 因为用这样的方式存储数据,所以数据库就有很大的灵活性,不必为加入一些新的数据就重新创建表和索引。更重要的是,LDAP目录不必花费内存或硬盘空间处理“空”域,也就是说,实际上不使用可选择的域也不会花费你任何资源。 作为例子的一个单独的数据项
让我们看看下面这个例子。我们用Foobar, Inc.的员工Fran Smith的LDAP记录。这个记录项的格式是LDIF,用来导入和导出LDAP目录的记录项。 dn: uid=fsmith, ou=employees, dc=foobar, dc=com objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson objectclass: foobarPerson uid: fsmith givenname: Fran sn: Smith cn: Fran Smith cn: Frances Smith telephonenumber: 510-555-1234 roomnumber: 122G o: Foobar, Inc. mailRoutingAddress: fsmith@foobar.com mailhost: mail.foobar.com userpassword: {crypt}3x1231v76T89N uidnumber: 1234 gidnumber: 1200 homedirectory: /home/fsmith loginshell: /usr/local/bin/bash 属性的值在保存的时候是保留大小写的,但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性(例如,password)在搜索的时候需要区分大小写。 让我们一点一点地分析上面的记录项。 dn: uid=fsmith, ou=employees, dc=foobar, dc=com 这是Fran的LDAP记录项的完整DN,包括在目录树中的完整路径。LDAP(和X.500)使用uid(User ID),不要把它和UNIX的uid号混淆了。 objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson objectclass: foobarPerson 可以为任何一个对象根据需要分配多个对象类型。person对象类型要求cn(common name)和sn(surname)这两个域不能为空。persion对象类型允许有其它的可选域,包括givenname、telephonenumber,等等。organizational Person给person加入更多的可选域,inetOrgPerson又加入更多的可选域(包括电子邮件信息)。最后,foobarPerson是为Foobar定制的对象类型,加入了很多定制的属性。 uid: fsmith givenname: Fran sn: Smith cn: Fran Smith cn: Frances Smith telephonenumber: 510-555-1234 roomnumber: 122G o: Foobar, Inc. 以前说过了,uid表示User ID。当看到uid的时候,就在脑袋里想一想“login"。 请注意CN有多个值。就象上面介绍的,LDAP允许某些属性有多个值。为什么允许有多个值呢?假定你在用公司的LDAP服务器查找Fran的电话号码。你可能只知道她的名字叫Fran,但是对人力资源处的人来说她的正式名字叫做Frances。因为保存了她的两个名字,所以用任何一个名字检索都可以找到Fran的电话号码、电子邮件和办公房间号,等等。 mailRoutingAddress: fsmith@foobar.com mailhost: mail.foobar.com 就象现在大多数的公司都上网了,Foobar用Sendmail发送邮件和处理外部邮件路由信息。Foobar把所有用户的邮件信息都存在LDAP中。最新版本的Sendmail支持这项功能。 Userpassword: {crypt}3x1231v76T89N uidnumber: 1234 gidnumber: 1200 gecos: Frances Smith homedirectory: /home/fsmith loginshell: /usr/local/bin/bash 注意,Foobar的系统管理员把所有用户的口令映射信息也都存在LDAP中。FoobarPerson类型的对象具有这种能力。再注意一下,用户口令是用UNIX的口令加密格式存储的。UNIX的uid在这里为uidnumber。提醒你一下,关于如何在LDAP中保存NIS信息,有完整的一份RFC。在以后的文章中我会谈一谈NIS的集成。 LDAP复制 LDAP服务器可以使用基于“推”或者“拉”的技术,用简单或基于安全证书的安全验证,复制一部分或者所有的数据。 例如,Foobar有一个“公用的”LDAP服务器,地址为ldap.foobar.com,端口为389。Netscape Communicator的电子邮件查询功能、UNIX的“ph"命令要用到这个服务器,用户也可以在任何地方查询这个服务器上的员工和客户联系信息。公司的主LDAP服务器运行在相同的计算机上,不过端口号是1389。 你可能即不想让员工查询资产管理或食谱的信息,又不想让信息技术人员看到整个公司的LDAP目录。为了解决这个问题,Foobar有选择地把子目录树从主LDAP服务器复制到“公用”LDAP服务器上,不复制需要隐藏的信息。为了保持数据始终是最新的,主目录服务器被设置成即时“推”同步。这些种方法主要是为了方便,而不是安全,因为如果有权限的用户想查询所有的数据,可以用另一个LDAP端口。 假定Foobar通过从奥克兰到欧洲的低带宽数据的连接用LDAP管理客户联系信息。可以建立从ldap.foobar.com:1389到munich-ldap.foobar.com:389的数据复制,象下面这样: periodic pull: ou=asia,ou=customers,o=sendmail.com periodic pull: ou=us,ou=customers,o=sendmail.com immediate push: ou=europe,ou=customers,o=sendmail.com “拉”连接每15分钟同步一次,在上面假定的情况下足够了。“推”连接保证任何欧洲的联系信息发生了变化就 立即被“推”到Munich。 用上面的复制模式,用户为了访问数据需要连接到哪一台服务器呢?在Munich的用户可以简单地连接到本地服务 器。如果他们改变了数据,本地的LDAP服务器就会把这些变化传到主LDAP服务器。然后,主LDAP服务器把这些变化 “推”回本地的“公用”LDAP服务器保持数据的同步。这对本地的用户有很大的好处,因为所有的查询(大多数是读)都在本地的服务器上进行,速度非常快。当需要改变信息的时候,最终用户不需要重新配置客户端的软件,因为LDAP目录服务器为他们完成了所有的数据交换工作。 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!779.entry
1. 通过EL表达式直接在页面中使用Spring Bean。(这一点称之为:SpringBean被当作LiteBean直接使用) 由于AOM是和Spring紧密集成的,因此,当我们要结合Spring使用的时候,只需要向工程中添加Spring 2.5的一些基本jar包,然后把operamasks-spring.jar这个jar添加到工程中就可以实现在xhtml或者jsp中通过EL表达式来访问Spring Bean。在该方式中,SpringBean可以被注入到LiteBean中。 package com.vv.aom.numbertest; import java.util.Random; public class NumberBean { private int num; public int getNum() { num = new Random().nextInt(10); return num; } public void setNum(int num) { this.num = num; } } | <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd" > <beans> <bean id="numberBean" class="com.vv.aom.numbertest.NumberBean"/> </beans> | <f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:w="http://www.apusic.com/jsf/widget" xmlns:h="http://java.sun.com/jsf/html" xmlns:ajax="http://www.apusic.com/jsf/ajax" renderKitId="AJAX"> <h:form> <p>Generate a number between 0 to 10</p> <h:inputText value="#{numberBean.num}"/><br/> <h:outputText value="Generate" style="text-decoration:underline;cursor:pointer;"> <ajax:action event="onclick"/> </h:outputText> </h:form> </f:view> | 需要注意:如果你需要在页面中通过EL表达式直接使用LiteBean的时候,需要注意你的所有路径下不能出现两个及以上的同名LiteBean,否则系统会自动为你选择路径最短的一个LiteBean。所以仍然推荐使用OperaMask的IoVC来使用LiteBean。 2. 在AOM中定义的LiteBean都可以当作是Spring Bean被使用。 如果希望从Spring上下文中获得一个LiteBean,则必须在Spring配置文件中配置下面的Bean: <bean class="org.operamasks.faces.spring.ManagedBeanConfigurer"/> |
上面的 org.operamasks.faces.spring.ManagedBeanConfigurer是 AOM 默认提供的一个 Spring Bean 定义,必须在Spring中配置此Bean,它的作用是:让 AOM 中的 LiteBean 同样能够被 Spring 所感应到。
如果你使用 Apusic 应用服务器,我们还建议你正确配置 Apusic 应用服务器的 TransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName"> <value>java:/TransactionManager</value> </property> </bean> |
这样,我们在AOM中定义的LiteBean既可以通过applicationContext.getBean(“”)方法来得到,也可以在Spring配置文件中使用LiteBean。
(1)通过spring的applicationContext的getBean方法得到LiteBean:
WebApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
NumberService mySpringBean = (NumberService) appContext.getBean("numberService");
out.print("The bean defined in Spring: " + mySpringBean);
out.print("<br/>");
Object myLiteBean = (Object) appContext.getBean("GenerateNumberBean");
out.print("The bean defined in AOM: " + myLiteBean); |
(2)通过spring-faces-config.xml配置文件使SpringBean和LiteBean协同工作,当然如果要指定该配置文件的个性化位置和名称,可以在web.xml中添加:
<context-param> <param-name>spring.faces.ConfiguarionFiles</param-name> <param-value>/WEB-INF/user-module.xml, /WEB-INF/role-module.xml</param-value> </context-param> |
之后在里面可以这样定义:(完全与Spring IOC的配置无异)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="compositeBeanAdvice" class="demo.spring.bean.CompositeBeanAdvice"/> <bean id="compositeBeanTarget" class="demo.spring.bean.CompositeBeanImpl" scope="session"> <property name="service" ref="numberService"/> <property name="numberBean" ref="GenerateNumberBean"/> </bean> <bean id="compositeBean" class="org.springframework.aop.framework.ProxyFactoryBean" scope="session"> <property name="proxyInterfaces" value="demo.spring.bean.CompositeBean"/> <property name="interceptorNames"> <list> <value>compositeBeanAdvice</value> </list> </property> <property name="target" ref="compositeBeanTarget"/> </bean> </beans> |
3. SpringBean在被当作LiteBean使用的时候,依然可以获得IoVC支持。这一点是通过在SpringBean中添加@Bind和@Action注解来实现的。例如:
SayHelloBean.java:
package com.vv.aom.numbertest;
import org.operamasks.faces.annotation.Action;
import org.operamasks.faces.annotation.Bind;
import org.operamasks.faces.annotation.ManagedBean;
//@ManagedBean
public class SayHelloBean {
@Bind
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Action
public String sayHello() {
System.out.println("hello " + name);
return null;
}
@Override
public String toString() {
return "MySpringBean";
}
} |
sayHello.xhtml
<f:view xmlns=http://www.w3.org/1999/xhtml xmlns:f="http://java.sun.com/jsf/core"
xmlns:w=http://www.apusic.com/jsf/widget
xmlns:layout="http://www.apusic.com/jsf/layout" renderKitId="AJAX"> <w:page title="spring"> <w:form> <layout:panelGrid columns="2"> <w:textField id="name"/> <w:button id="sayHello"/> </layout:panelGrid> </w:form> </w:page>
</f:view> |
Spring-bean.xml:
<bean id="SayHelloBean" class="com.vv.aom.numbertest.SayHelloBean">
<property name="name" value="Kevin"/>
</bean> | 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!736.entry
Oracle常用的数据库字段类型如下: 字段类型 | 中文说明 | 限制条件 | 其它说明 | CHAR | 固定长度字符串 | 最大长度2000 bytes | | VARCHAR2 | 可变长度的字符串 | 最大长度4000 bytes | 可做索引的最大长度749 | NCHAR | 根据字符集而定的固定长度字符串 | 最大长度2000 bytes | | NVARCHAR2 | 根据字符集而定的可变长度字符串 | 最大长度4000 bytes | | DATE | 日期(日-月-年) | DD-MM-YY(HH-MI-SS) | 经过严格测试,无千虫问题 | LONG | 超长字符串 | 最大长度2G(231-1) | 足够存储大部头著作 | RAW | 固定长度的二进制数据 | 最大长度2000 bytes | 可存放多媒体图象声音等 | LONG RAW | 可变长度的二进制数据 | 最大长度2G | 同上 | BLOB | 二进制数据 | 最大长度4G | | CLOB | 字符数据 | 最大长度4G | | NCLOB | 根据字符集而定的字符数据 | 最大长度4G | | BFILE | 存放在数据库外的二进制数据 | 最大长度4G | | ROWID | 数据表中记录的唯一行号 | 10 bytes ********.****.****格式,*为0或1 | NROWID | 二进制数据表中记录的唯一行号 | 最大长度4000 bytes | NUMBER(P,S) | 数字类型 | P为整数位,S为小数位 | DECIMAL(P,S) | 数字类型 | P为整数位,S为小数位 | INTEGER | 整数类型 | 小的整数 | FLOAT | 浮点数类型 | NUMBER(38),双精度 | REAL | 实数类型 | NUMBER(63),精度更高 | 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!734.entry
http://zangweiren.javaeye.com/category/34977 2008-08-25 关键字: java 面试题 自增 自减 位运算符 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 有些运算符在JAVA语言中存在着,但是在实际开发中我们或许很少用到它们,在面试题中却时常出现它们的身影,对于这些运算符的含义和用法,你是否还记得呢? 自增(++)和自减(--)运算符 我们先来回答几个问题吧: int i = 0; int j = i++; int k = --i; 这段代码运行后,i等于多少?j等于多少?k等于多少?太简单了?好,继续: int i = 0; int j = i++ + ... 2008-08-08 关键字: java 面试题 多线程 thread 线程池 synchronized 死锁 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 线程或者说多线程,是我们处理多任务的强大工具。线程和进程是不同的,每个进程都是一个独立运行的程序,拥有自己的变量,且不同进程间的变量不能共享;而线程是运行在进程内部的,每个正在运行的进程至少有一个线程,而且不同的线程之间可以在进程范围内共享数据。也就是说进程有自己独立的存储空间,而线程是和它所属的进程内的其他线程共享一个存储空间。线程的使用可以使我们能够并行地处理一些事情。线程通过并行的处理给用户带来更好的使用体验,比如你使 ... 2008-07-31 关键字: java 面试题 继承 多态 重载 重写 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承、多态、重载和重写。 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型。继承是面向对象的三个基本特征--封装、继承、多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,ja ... 2008-07-25 关键字: java 面试题 基本类型 int long boolean float double char 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 基本类型,或者叫做内置类型,是JAVA中不同于类的特殊类型。它们是我们编程中使用最频繁的类型,因此面试题中也总少不了它们的身影,在这篇文章中我们将从面试中常考的几个方面来回顾一下与基本类型相关的知识。 基本类型共有九种,它们分别都有相对应的包装类。关于它们的详细信息请看下表: [img]http://zangweiren.javaeye.com/upload/picture/pic/18450/8071c6c2-7cfb ... 2008-07-22 关键字: java 面试题 日期 时间 转换 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 日期和时间的处理不仅在面试题中会考到,在实际项目开发中也是我们经常需要处理的问题,似乎没有哪个项目可以避开它们,我们常常在处理用户的出生年月日、注册日期,订单的创建时间等属性时用到,由此可见其重要性。 java.util.Date类 提到日期和时间,我想大家最先想到应该是java.util.Date类吧。Date类可以精确到毫秒数,这个毫秒数是相对于格林威治标准时间“1970-01-01 00:00:00.000 GMT ... 2008-07-18 关键字: java 面试题 字符串 string 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 上一次我们已经一起回顾了面试题中常考的到底创建了几个String对象的相关知识,这一次我们以几个常见面试题为引子,来回顾一下String对象相关的其它一些方面。 String的length()方法和数组的length属性 String类有length()方法吗?数组有length()方法吗? String类当然有length()方法了,看看String类的源码就知道了,这是这个方法的定义: public int l ... 2008-07-13 关键字: java 面试题 值传递 引用传递 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< JAVA中的传递都是值传递吗?有没有引用传递呢? 在回答这两个问题前,让我们首先来看一段代码: public class ParamTest { // 初始值为0 protected int num = 0; // 为方法参数重新赋值 public void change(int i) { i = 5; } // 为方法参数重新赋值 public void change(Par ... 2008-07-08 关键字: java 面试题 final finally finalize 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< final、finally和finalize的区别是什么? 这是一道再经典不过的面试题了,我们在各个公司的面试题中几乎都能看到它的身影。final、finally和finalize虽然长得像孪生三兄弟一样,但是它们的含义和用法却是大相径庭。这一次我们就一起来回顾一下这方面的知识。 final关键字 我们首先来说说final。它可以用于以下四个地方: 定义变量,包括静态的和非静态的。定义方法的参数。定义方法。定义 ... 2008-07-03 关键字: java 面试题 继承 变量的覆盖 属性 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 我们来看看这么一道题: class ParentClass { public int i = 10; } public class SubClass extends ParentClass { public int i = 30; public static void main(String[] args) { ParentClass parentClass = new SubClass() ... 2008-06-30 关键字: java 面试题 string 创建几个对象 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 我们首先来看一段代码: String str=new String("abc"); 紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。 我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。Strin ... 2008-06-26 关键字: java 面试题 初始化 作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com >>>转载请注明出处!<<< 大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。 我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初 ... 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!725.entry
作用域:抛开request,session,globalsession不谈,先说说singleton和prototype。 singleton是默认的作用域,作用域为singleton的Bean在Spring初始化上下文期间就已经初始化,并且全局唯一 作用域为prototype的Bean在Spring初始化上下文期间不进行初始化,只有在getBean()方法被调用时才进行初始化,每调用一次getBean()方法,就生成一个新的Bean实例。 生命周期:一般来讲,如果作用域为默认,除非设置了属性lazy-init="true",一个Bean的实例化是在Spring上下文初始化时进行。 init-mothed属性用于指定Bean初始化时执行的初始化方法,destroy-method用于指定Spring上下文关闭时,单一实例的Bean执行销毁方法。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!723.entry
实例化bean的三种方式: 1。默认构造方法: <bean id=''bean1" class="com.vv.Bean"/> 前提是已经有了Bean.class 2。静态工厂方法: public class BeanFactory { public static Bean createBean(){ return new Bean(); } } <bean id=''bean1" class="com.vv. BeanFactory" factory-mothod=" createBean"/> 3。实例工厂方法: public class BeanFactory {
public Bean createBean(){
return new Bean();
}
} <bean id='' beanFactory" class="com.vv. BeanFactory"/> <bean id="bean1" factory-bean=" beanFactory" factory-mothod=" createBean"/> 备注:默认情况下,如果一个bean的class、factory-bean、factory-method属性都指定后,工厂实例化方法优先 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!722.entry
由于项目需要,写了一个hibernate+datasource的CRUD程序。。。但是部署到测试环境后,修改了context.xml和hibernate.cfg.xml发现程序运行不正常。通过跟踪日志发现Hibernate链接正常,但是datasource连接数据库异常。。。 通过跟踪日志发现是由于配置的JNDI数据源不正常导致的。但是修改了META-INF/context.xml之后仍不能正常工作。在尝试了更换测试环境JDK、更换tomcat、更换jdbc驱动等一系列方法后,终于找到根源,是由于tomcat不能自动同步META-INF/context.xml到%CATALINA_HOME%/conf/Catalina/localhost/%appname%.xml导致的。。。。 经过修改%CATALINA_HOME%/conf/Catalina/localhost/%appname%.xml内的数据源连接,该应用现在正常工作。 结论:对于tomcat,看来以后要手工同步context.xml文件到%CATALINA_HOME%/conf/Catalina/localhost/%appname%.xml了。。。 文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!581.entry
问题: tb为一个table var row = tb.rows[0]; var cell=row.insertCell(row.cells.length); cell.innerHTML = "aaaa"; cell.style.display = ""; cell.style.cursor = "pointer"; cell.onmouseover = "mOver(this)"; cell.onmouseout = "mOut(this)"; cell.onclick = "mClick(this)"; 以上是在一个table中加一个单元格 但是 cell.onmouseover = "mOver(this)"; cell.onmouseout = "mOut(this)"; cell.onclick = "mClick(this)"; 却无效 也就是说在动态添加的单元格上的事件 无效 解决方法:用eval()函数 cell.setAttribute("onmouseover", eval(function(){mOver(this)})); cell.setAttribute("onmouseout", eval(function(){mOut(this)})); cell.setAttribute("onclick", eval(function(){mClick(this)})); 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!575.entry
Oracle9i之前,中文是按照二进制编码进行排序的。在oracle9i中新增了按照拼音、部首、笔画排序功能。 1、设置NLS_SORT参数值 SCHINESE_RADICAL_M 按照部首(第一顺序)、笔划(第二顺序)排序 SCHINESE_STROKE_M 按照笔划(第一顺序)、部首(第二顺序)排序 SCHINESE_PINYIN_M 按照拼音排序 2、Session级别的设置,修改ORACLE字段的默认排序方式: 按拼音:alter session set nls_sort = SCHINESE_PINYIN_M; 按笔画:alter session set nls_sort = SCHINESE_STROKE_M; 按偏旁:alter session set nls_sort = NLS_SORT=SCHINESE_RADICAL_M; 3、语句级别设置排序方式: 按照笔划排序 select * from dept order by nlssort(name,'NLS_SORT=SCHINESE_STROKE_M'); 按照部首排序 select * from dept order by nlssort(name,'NLS_SORT=SCHINESE_RADICAL_M'); 按照拼音排序,此为系统的默认排序方式 select * from dept order by nlssort(name,'NLS_SORT=SCHINESE_PINYIN_M'); 4、修改系统参数(数据库所在操作系统): set NLS_SORT=SCHINESE_RADICAL_M ;export NLS_SORT (sh) setenv NLS_SORT SCHINESE_RADICAL_M (csh) HKLC\SOFTWARE\ORACLE\home0\NLS_SORT (win注册表) Oracle 官方说明 NLS_SORT NLS_SORT specifies the collating sequence for ORDER BY queries. NLS_COMP NLS_COMP specifies the collation behavior of the database session. Property | Description | Parameter type | String | Syntax | NLS_SORT = { BINARY | linguistic_definition } | Default value | Derived from NLS_LANGUAGE | Modifiable | ALTER SESSION | Range of values | BINARY or any valid linguistic definition name | - If the value is BINARY, then the collating sequence for ORDER BY queries is based on the numeric value of characters (a binary sort that requires less system overhead).
-
If the value is a named linguistic sort, sorting is based on the order of the defined linguistic sort. Most (but not all) languages supported by the NLS_LANGUAGE parameter also support a linguistic sort with the same name. Note: Setting NLS_SORT to anything other than BINARY causes a sort to use a full table scan, regardless of the path chosen by the optimizer. BINARY is the exception because indexes are built according to a binary order of keys. Thus the optimizer can use an index to satisfy the ORDER BY clause when NLS_SORT is set to BINARY. If NLS_SORT is set to any linguistic sort, the optimizer must include a full table scan and a full sort in the execution plan. You must use the NLS_SORT operator with comparison operations if you want the linguistic sort behavior. Property | Description | Parameter type | String | Syntax | NLS_COMP = { BINARY | LINGUISTIC | ANSI } | Default value | BINARY | Modifiable | ALTER SESSION | Basic | No | Values: Normally, comparisons in the WHERE clause and in PL/SQL blocks is binary unless you specify the NLSSORT function. Comparisons for all SQL operations in the WHERE clause and in PL/SQL blocks should use the linguistic sort specified in the NLS_SORT parameter. To improve the performance, you can also define a linguistic index on the column for which you want linguistic comparisons. A setting of ANSI is for backwards compatibility; in general, you should set NLS_COMP to LINGUISTIC 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!569.entry
<bean:message key="unite.error.format" arg0="<%=Utility.getMessage("backend.livemanager.itemindex")%>"/>
最近发现tomcat 5.5.27和6.0.18都不能正确的解析上面的这段JSP代码了。说是Utility.getMessage("backend.livemanager.itemindex")这段要escape一下~~~~无语
Then I found that the JSP 2.0 standard states that quotes in JSP espressions used in tag attributes must be escaped with a backslash. This is silly, unnecessary and counter-intuitive but it is the standard...
F*cking the silly tomcat~~~~
解决的办法是在${tomcat}/conf下面的catalina.properties中加入以下内容:
org.apache.jasper.compiler. Parser.STRICT_QUOTE_ESCAPING =false
这样就关闭了这个令人讨厌的引号escape验证。
当然对于最新的jsp/servlet标准而言,上面提到的写法确实已经不合法了。所以建议以后可以用<bean:message key="unite.error.format" arg0="<%=Utility.getMessage(\”backend.livemanager.itemindex\”)%>"/>
代替。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!562.entry
方法1:
function getAbsPoint(e)
{
var x = e.offsetLeft, y = e.offsetTop;
while(e=e.offsetParent)
{
x += e.offsetLeft;
y += e.offsetTop;
}
alert("x:"+x+","+"y:"+y);
}
方法2:
function getAbsPoint(obj)
{
var x,y;
oRect = obj.getBoundingClientRect();
x=oRect.left
y=oRect.top
alert("("+x+","+y+")")
}
JS中获得窗口属性的方法
1。获得屏幕的分辨率:
screen.width
screen.height
2。获得窗口大小:
document.body.clientWidth
document.body.clientHeight
3。获得窗口大小(包含Border、Scroll等元素)
document.body.offsetWidth
document.body.offsetHeight
为找Td坐标苦恼了很久,终于找到,方法不错,哈哈
文章来源:http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!559.entry
好,说了这么多,最后让我们来看看如何在Web应用中使用Quartz。 由于Scheduler的配置相当的个性化,所以,在Web应用中,我们可以通过一个quartz.properties文件来配置QuartzServlet。不过之前让我们先来看看web.xml中如何配置 web.xml | <servlet> <servlet-name> QuartzInitializer </servlet-name> <display-name> Quartz Initializer Servlet </display-name> <servlet-class> org.quartz.ee.servlet.QuartzInitializerServlet </servlet-class> <load-on-startup> -1 </load-on-startup> <init-param> <param-name>config-file</param-name> <param-value>/quartz.properties</param-value> </init-param> <init-param> <param-name>shutdown-on-unload</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>start-scheduler-on-load</param-name> <param-value>true</param-value> </init-param> </servlet> | 这里,load-on-startup是指定QuartzServlet是否随应用启动,-1表示否,正数表示随应用启动,数值越小,则优先权越高。 初始化参数中,config-file里面可以指定QuartzServlet的配置文件,这里我们用的是quartz.properties。 shutdown-on-unload,表示是否在卸载应用时同时停止调度,该参数推荐true,否则你的tomcat进程可能停不下来。 start-scheduler-on-load,表示应用加载时就启动调度器,如果为false,则quartz.properties中指定的调度器在用户访问这个Servlet之后才会加载,在此之前,如果你通过ServletContext查找SchedulerFactory是可以找到的,但是要得到具体的Scheduler,那么你一定会发现Jvm抛出了一个NullPointerExcetion。 下面就来看看quartz.properties的真面目。 quartz.properties | org.quartz.scheduler.instanceName = PushDBScheduler org.quartz.scheduler.instanceId = one org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 4 org.quartz.threadPool.threadPriority = 4 org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin org.quartz.plugin.jobInitializer.fileName = quartz_job.xml | 我想不用多说,大家都看出来了,首先配置了基本的Scheduler实例名,并分配了ID,然后为这个调度器设定了线程池,后面是初始化插件。初始化插件是Quartz非常实用的功能,你可以用这个功能来实现Quartz的扩展性。这里配置的插件是读取job XML文件,让调度器自动载入Job。这个插件现在支持读取多个job XML文件,但是我现在还没有试过,感兴趣的读者可以自己尝试。另外就是有一个scanInterval属性,表示每隔几秒自动扫描一次job XML文件,我现在也没有试过,感兴趣的读者可以自己试验一下。注意,该参数设定为0表示不扫描。 最后,我们来看看job XML文件,这里以quartz_job.xml为例 quartz_job.xml | <quartz> <job> <job-detail> <name>ScanItemsInDB</name> <group>Scanning</group> <job-class>com.testquartz.ScanDB</job-class> <job-data-map allows-transient-data="true"> <entry> <key>testmode</key> <value>true</value> </entry> </job-data-map> </job-detail> <trigger> <cron> <name>t1</name> <group> Scanning </group> <job-name> ScanItemsInDB </job-name> <job-group> Scanning </job-group> <cron-expression>0 0/5 * * * ?</cron-expression> </cron> </trigger> </job> </quartz> | 这个文件真是非常显而易见了,我就不多说了,大家自己研究吧。 然后你只要自己写一下ScanDB这个类就可以了。 ScanDB.java | public class ScanDB implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { //你的代码 } } | 注意JobExecutionContext这个类。这个类是用来存取任务执行时的相关信息的,从中我们可以获取当前作业的Trigger、Scheduler、JobDataMap等等。 当然,Scheduler也有对应的SchedulerContext,具体的用途很像ServletContext。有兴趣的读者自己研究吧。 另外就是可以提供一个提示:在一个作业执行的时候,你就可以设定另外一个调度器,去执行另一个Job,这样你可以每个一段时间扫描一下数据库,然后看一看数据库里有没有下一个时间段待发的邮件,然后调用一个新的调度器实例,以便在指定的发送时间将其发送出去。 好了,Quartz的相关知识就总结到这里。谢谢大家。 上一篇 Quartz调度框架应用总结(续1) 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!554.entry
三.触发器 Trigger是一个抽象类,它有三个子类:SimpleTrigger,CronTrigger和NthIncludedDayTrigger。前两个比较常用。 1。SimpleTrigger:这是一个非常简单的类,我们可以定义作业的触发时间,并选择性的设定重复间隔和重复次数。 2。CronTrigger:这个触发器的功能比较强大,而且非常灵活,但是你需要掌握有关Cron表达式的知识。如果你是一个Unix系统爱好者,你很可能已经具备这种知识,但是如果你不了解Cron表达式,请看下面的Cron详解: Cron表达式由6或7个由空格分隔的时间字段组成,如表1所示: 表1 Cron表达式时间字段 位置 | 时间域名 | 允许值 | 允许的特殊字符 | 1 | 秒 | 0-59 | , - * / | 2 | 分钟 | 0-59 | , - * / | 3 | 小时 | 0-23 | , - * / | 4 | 日期 | 1-31 | , - * ? / L W C | 5 | 月份 | 1-12 | , - * / | 6 | 星期 | 1-7 | , - * ? / L C # | 7 | 年(可选) | 空值1970-2099 | , - * / | Cron表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下: ●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”; ●问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符; ●减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12; ●逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五; ●斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y; ●L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五; ●W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围; ●LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日; ●井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发; ● C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。表2下面给出一些完整的Cron表示式的实例: 表2 Cron表示式示例 表示式 | 说明 | "0 0 12 * * ? " | 每天12点运行 | "0 15 10 ? * *" | 每天10:15运行 | "0 15 10 * * ?" | 每天10:15运行 | "0 15 10 * * ? *" | 每天10:15运行 | "0 15 10 * * ? 2008" | 在2008年的每天10:15运行 | "0 * 14 * * ?" | 每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。 | "0 0/5 14 * * ?" | 每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。 | "0 0/5 14,18 * * ?" | 每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。 | "0 0-5 14 * * ?" | 每天14:00点到14:05,每分钟运行一次。 | "0 10,44 14 ? 3 WED" | 3月每周三的14:10分到14:44,每分钟运行一次。 | "0 15 10 ? * MON-FRI" | 每周一,二,三,四,五的10:15分运行。 | "0 15 10 15 * ?" | 每月15日10:15分运行。 | "0 15 10 L * ?" | 每月最后一天10:15分运行。 | "0 15 10 ? * 6L" | 每月最后一个星期五10:15分运行。 | "0 15 10 ? * 6L 2007-2009" | 在2007,2008,2009年每个月的最后一个星期五的10:15分运行。 | "0 15 10 ? * 6#3" | 每月第三个星期五的10:15分运行。 | 上一篇 Quartz调度框架应用总结 下一篇 Quartz调度框架应用总结(续2) 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!553.entry
前一段时间项目需要做一个定时发送消息的功能,该功能依附于Web应用上,即当Web应用启动时,该应用就开始作用。起先决定使用java.util.Timer和java.util.TimerTask来实现,但是研究了一下以后发现Java Timer的功能比较弱,而且其线程的范围不受Web应用的约束。后来发现了Quartz这个开源的调度框架,非常有趣。 首先我们要得到Quartz的最新发布版。目前其最新的版本是1.6。我们可以从以下地址获得它的完整下载包,包中可谓汤料十足,不仅有我们要的quartz.jar,更包含多个例程和详细的文档,从API到配置文件的XSD一应俱全。感兴趣的朋友也可以在src目录下找到该项目的源码一看究竟。 废话少说,下面就来看一看这个东东是怎么在Java Web Application中得以使用的。 首先不得不提出的是Quartz的三个核心概念:调度器、触发器、作业。让我们来看看他们是如何工作的吧。 一.作业总指挥——调度器 1. Scheduler接口 该接口或许是整个Quartz中最最上层的东西了,它提携了所有触发器和作业,使它们协调工作。每个Scheduler都存有JobDetail和Trigger的注册,一个Scheduler中可以注册多个JobDetail和多个Trigger,这些JobDetail和Trigger都可以通过group name和他们自身的name加以区分,以保持这些JobDetail和Trigger的实例在同一个Scheduler内不会冲突。所以,每个Scheduler中的JobDetail的组名是唯一的,本身的名字也是唯一的(就好像是一个JobDetail的ID)。Trigger也是如此。 Scheduler实例由SchedulerFactory产生,一旦Scheduler实例生成后,我们就可以通过生成它的工厂来找到该实例,获取它相关的属性。下面的代码为我们展示了如何从一个Servlet中找到SchedulerFactory并获得相应的Scheduler实例,通过该实例,我们可以获取当前作业中的testmode属性,来判断该作业是否工作于测试模式。 //从当前Servlet上下文中查找StdSchedulerFactory ServletContext ctx=request.getSession().getServletContext(); StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute("org.quartz.impl.StdSchedulerFactory.KEY"); Scheduler sch = null; try { //获取调度器 sch = factory.getScheduler("SchedulerName"); //通过调度器实例获得JobDetail,注意领会JobDetailName和GroupName的用法 JobDetail jd=sch.getJobDetail("JobDetailName", "GroupName"); Map jobmap1=jd.getJobDataMap(); istest=jobmap1.get("testmode")+""; } catch (Exception se) { //如果得不到当前作业,则从配置文件中读取testmode ReadXML("job.xml").get(“job.testmode”); } | Scheduler实例生成后,它处于"stand-by"模式,需要调用其start方法来使之投入运作。 public class SendMailShedule{ //设置标准SchedulerFactory static SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); static Scheduler sched; public static void run()throws Exception{ //生成Scheduler实例 sched = schedFact.getScheduler(); //创建一个JobDetail实例,对应的Job实现类是SendMailJob JobDetail jobDetail = new JobDetail("myJob",sched.DEFAULT_GROUP,SendMailJob.class); //设置CronTrigger,利用Cron表达式设定触发时间 CronTrigger trigger = new CronTrigger("myTrigger","test","0 0 8 1 * ?"); sched.scheduleJob(jobDetail, trigger); sched.start(); } public static void stop()throws Exception{ sched.shutdown(); } } | 另外,我们也可以通过监听器来跟踪作业和触发器的工作状态。 二.作业及其相关 1. Job 作业实际上是一个接口,任何一个作业都可以写成一个实现该接口的类,并实现其中的execute()方法,来完成具体的作业任务。 2. JobDetail JobDetail可以指定我们作业的详细信息,比如可以通过反射机制动态的加载某个作业的实例,可以指定某个作业在单个调度器内的作业组名称和具体的作业名称,可以指定具体的触发器。 一个作业实例可以对应多个触发器(也就是说学校每天10点放一次眼保健操录音,下午3点半可以再放一次),但是一个触发器只能对应一个作业实例(10点钟的时候学校不可能同时播放眼保健操和广播体操的录音)。 3. JobDataMap 这是一个给作业提供数据支持的数据结构,使用方法和java.util.Map一样,非常方便。当一个作业被分配给调度器时,JobDataMap实例就随之生成。 Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。 正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job。 如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。 JobDataMap实例也可以与一个触发器相关联。这种情况下,对于同一作业的不同触发器,我们可以在JobDataMap中添加不同的数据,以便作业在不同时间执行时能够提供更为灵活的数据支持(学校上午放眼保健操录音第一版,下午放第二版)。 不管是有状态还是无状态的任务,在任务执行期间对Trigger的JobDataMap所做的更改都不会进行持久,也即不会对下次的执行产生影响。 下一篇 Quartz调度框架应用总结(续1) 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!550.entry
1 框架编程概述 一个Html 页面可以有一个或多个子框架,这些子框架以<iframe>来标记,用来显示一 个独立的Html 页面。这里所讲的框架编程包括框架的自我控制以及框架之间的互相访问, 例如从一个框架中引用另一个框架中的JavaScript变量、调用其他框架内的函数、控制另一 个框架中表单的行为等。 2 框架间的互相引用 一个页面中的所有框架以集合的形式作为window 对象的属性提供,例如: window.frames 就表示该页面内所有框架的集合,这和表单对象、链接对象、图片对象等是 类似的,不同的是,这些集合是document 的属性。因此,要引用一个子框架,可以使用如 下语法: window.frames[“frameName”]; window.frames.frameName window.frames[index] 其中,window字样也可以用self代替或省略,假设frameName 为页面中第一个框架, 则以下的写法是等价的: self.frames[“frameName”] self.frames[0] frames[0] frameName 了解了如何引用一个框架,那么引用的这个框架到底是什么呢?其实,每个框架都对应 一个HTML 页面,所以这个框架也是一个独立的浏览器窗口,它具有窗口的所有性质,所 谓对框架的引用也就是对window 对象的引用。有了这个window 对象,就可以很方便地对 其中的页面进行操作,例如使用window.document对象向页面写入数据、使用window.location 属性来改变框架内的页面等。 下面分别介绍不同层次框架间的互相引用: 2.1.父框架到子框架的引用 知道了上述原理,从父框架引用子框架变的非常容易,即: window.frames[“frameName”]; 这样就引用了页面内名为frameName 的子框架。如果要引用子框架内的子框架,根据 引用的框架实际就是window对象的性质,可以这样实现: window.frames[“frameName”].frames[“frameName2”]; 这样就很引用到了二级子框架,以此类推,可以实现多层框架的引用。 2.2.子框架到父框架的引用 每个window对象都有一个parent属性,表示它的父框架。如果该框架已经是顶层框架, 则window.parent 还表示该框架本身。 2.3.兄弟框架间的引用 如果两个框架同为一个框架的子框架,它们称为兄弟框架,可以通过父框架来实现互相 引用,例如一个页面包括2 个子框架: <frameset rows="50%,50%"> <frame src="1.html" name="frame1" /> <frame src="2.html" name="frame2" /> </frameset> 在frame1 中可以使用如下语句来引用frame2: self.parent.frames[“frame2”]; 2.4.不同层次框架间的互相引用 框架的层次是针对顶层框架而言的。当层次不同时,只要知道自己所在的层次以及另一 个框架所在的层次和名字,利用框架引用的window对象性质,可以很容易地实现互相访问, 例如: self.parent.frames[“childName”].frames[“targetFrameName”]; 2.5.对顶层框架的引用 和parent 属性类似,window 对象还有一个top 属性。它表示对顶层框架的引用,这可 以用来判断一个框架自身是否为顶层框架,例如: //判断本框架是否为顶层框架 if(self==top){ //dosomething } 3 改变框架的载入页面 前面已经讲到,对框架的引用就是对window对象的引用,利用window对象的location 属性,可以改变框架的导航,例如: window.frames[0].location=”1.html”; 这就将页面中第一个框架的页面重定向到1.html,利用这个性质,甚至可以使用一条链 接来更新多个框架。 <frameset rows="50%,50%"> <frame src="1.html" name="frame1" /> <frame src="2.html" name="frame2" /> </frameset> <!--somecode--> <a href=”frame1.location=’3.html;frame2.location=’4.html’” onclick=””>link</a> <!--somecode--> 4 引用其他框架内的JavaScript变量和函数 在介绍引用其他框架内JavaScript变量和函数的技术之前,先来看以下代码: <script language="JavaScript" type="text/javascript"> <!-- function hello(){ alert("hello,ajax!"); } window.hello(); //--> </script> 如果运行了这段代码,会弹出“hello,ajax!”的窗口,这正是执行hello()函数的结果。 那为什么hello()变成了window对象的方法呢?事实上,在一个页面内定义的所有全局变量 和全局函数都是作为window对象的成员。例如: var a=1; alert(window.a); 就会弹出对话框显示为1。同样的原理,在不同框架之间共享变量和函数,就是要通过 window对象来调用。 为了方便大家的理解,下面举例说明。一个商品浏览页面由两个子框架组成,左侧表示 商品分类的链接;当用户单击分类链接时,右侧显示相应的商品列表;用户可以单击商品旁 的【购买】链接将商品加入购物车。 在这个例子中,可以利用左侧导航页面来存储用户希望购买的商品,因为当用户单击导 航链接时,变化的是另外一个页面,即商品展示页面,而导航页面本身是不变的,因此其中 的JavaScript变量不会丢失,可以用来存储全局数据。其实现原理如下: 假设左侧页面为links.html,右侧页面为show.html,页面结构如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title> New Document </title> </head> <frameset cols="20%,80%"> <frame src="link.html" name="link" /> <frame src="show.html" name="show" /> </frameset> </html> 在show.html 中展示的商品旁边可以加入这样一条语句: <a href=”void(0)” onclick=”self.parent.link.addToOrders(32068)”>加入购物车</a> 其中link表示导航框架,在link.html 页面中定义了arrOrders数组来存储商品的id,函 数addToOrders()用来响应商品旁边【购买】链接的单击事件,它接收的参数id 表示商品的 id,例子中是一个id为32068 的商品: <script language="JavaScript" type="text/javascript"> <!-- var arrOrders=new Array(); function addToOrders(id){ arrOrders.push(id); } //--> </script> 这样,在结帐页面或是购物车浏览页面就可以用arrOrders来获取所有准备购买的商品。 框架可以使一个页面划分为功能独立的多个模块,每个模块之间彼此独立,但又可以通 过window 对象的引用来建立联系,是web 开发中的一个重要机制。在Ajax 开发中,还可 以利用隐藏框架实现各种技巧,在后面介绍Ajax 实例编程时可以发现,无刷新上传文件以 及解决Ajax的前进后退问题都会用到这种技术。 以上内容来自《征服AJAX》 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!423.entry
[以下是转文] Ajax从数据库里读取数据不能及时更新,这是因为浏览器的缓存机制。本文提出了四种解决办法。 以下是引用片段: 在电信做的小灵通短信订餐系统中,有几个页面要用到三级联动下拉框,为了使用户体验更好,使服务器缓解一定压力,我决定使用AJAX来完成这个功能,可是我却粗心大意,漏掉了一个环节,使得ajax从数据库里读取数据不能及时更新.这是因为浏览器的缓存机制. 有4种方法可以解决这个问题: 1.在请求的URL后面加一个时间参数,如: time=new date() 当然也可以添加其他性质参数,只要是随机参数就可以, open("GET",url+"?t="+Math.random(),false) 或者 url+"?timeStamp="+new Date().getTime(); 2. js 代码 function ajaxRead(file){ var xmlObj = null; if(window.XMLHttpRequest){ xmlObj = new XMLHttpRequest(); } else if(window.ActiveXObject){ xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } else { return; } xmlObj.onreadystatechange = function(){ if(xmlObj.readyState == 4){ processXML(xmlObj.responseXML); } else{ document.getElementById ('playernews').innerHTML='采用AJAX来实现数据的读取,正在加载...'; } } xmlObj.open ('GET', file, true); xmlObj.send (''); } function show() { ajaxRead('*.jsp'); setInterval("ajaxRead('new.php')",30000); //自动更新 } 3.加上 xmlhttp.setRequestHeader("Cache-Control","no-cache"); 4.在XmlHttpRequest发送请求之前加上 XmlHttpRequest.setRequestHeader("If-Modified-Since","0"), 如:在 XXXXX.send(YYYYYY). [以下是本人注释] 在使用prototype.js的时候,如果想使用方法3和4,需要这样写: var myAjax = new Ajax.Request( request_url, { method:'get', requestHeaders: ['Cache-Control','no-cache','If-Modified-Since','0'], parameters:request_pars, asynchronous:true, //true---异步;false---同步.默认为true onComplete:processRequest } ); 注意红色的代码部分,你可能注意到这是把3、4两种方法结合起来使用的。。。 好了,如果你是使用json的话,这个方法一样好用。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!421.entry
在www.json.org上公布了很多Java下的json解析工具,其中org.json和json-lib比较简单,两者使用上差不多。下面两段源代码是分别使用这两个工具解析和构造JSON的演示程序。 http://nchc.dl.sourceforge.net/sourceforge/json-lib/json-lib-2.2.1-jdk15.jar (Needs libs below:) [ - jakarta commons-lang 2.3
- jakarta commons-beanutils 1.7.0
- jakarta commons-collections 3.2
- jakarta commons-logging 1.1
- ezmorph 1.0.4
] 这是使用json-lib的程序: import java.util.HashMap; import java.util.Map; import net.sf.json.JSONObject; public class Test { public static void main(String[] args) { String json = "{\"name\":\"reiz\"}"; JSONObject jsonObj = JSONObject.fromObject(json); String name = jsonObj.getString("name"); jsonObj.put("initial", name.substring(0, 1).toUpperCase()); String[] likes = new String[] { "JavaScript", "Skiing", "Apple Pie" }; jsonObj.put("likes", likes); Map <String, String> ingredients = new HashMap <String, String>(); ingredients.put("apples", "3kg"); ingredients.put("sugar", "1kg"); ingredients.put("pastry", "2.4kg"); ingredients.put("bestEaten", "outdoors"); jsonObj.put("ingredients",ingredients); System.out.println(jsonObj); } } http://www.json.org/java/json.zip 这是使用org.json的程序: import java.util.HashMap; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; public class Test { public static void main(String[] args) throws JSONException { String json = "{\"name\":\"reiz\"}"; JSONObject jsonObj = new JSONObject(json); String name = jsonObj.getString("name"); jsonObj.put("initial", name.substring(0, 1).toUpperCase()); String[] likes = new String[] { "JavaScript", "Skiing", "Apple Pie" }; jsonObj.put("likes", likes); Map <String, String> ingredients = new HashMap <String, String>(); ingredients.put("apples", "3kg"); ingredients.put("sugar", "1kg"); ingredients.put("pastry", "2.4kg"); ingredients.put("bestEaten", "outdoors"); jsonObj.put("ingredients", ingredients); System.out.println(jsonObj); System.out.println(jsonObj); } } 两者的使用几乎是相同的,但org.json比json-lib要轻量得多,前者没有任何依赖,而后者要依赖ezmorph和commons的lang、logging、beanutils、collections等组件。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!419.entry
JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧。 JSON 是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON 数据不需要任何特殊的 API 或工具包。 JSON的规则很简单: 对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。具体细节参考http://www.json.org/json-zh.html 举个简单的例子: js 代码 - function showJSON() {
- var user =
- {
- "username":"andy",
- "age":20,
- "info": { "tel": "123456", "cellphone": "98765"},
- "address":
- [
- {"city":"beijing","postcode":"222333"},
- {"city":"newyork","postcode":"555666"}
- ]
- }
-
- alert(user.username);
- alert(user.age);
- alert(user.info.cellphone);
- alert(user.address[0].city);
- alert(user.address[0].postcode);
- }
这表示一个user对象,拥有username, age, info, address 等属性。 同样也可以用JSON来简单的修改数据,修改上面的例子 js 代码 - function showJSON() {
- var user =
- {
- "username":"andy",
- "age":20,
- "info": { "tel": "123456", "cellphone": "98765"},
- "address":
- [
- {"city":"beijing","postcode":"222333"},
- {"city":"newyork","postcode":"555666"}
- ]
- }
-
- alert(user.username);
- alert(user.age);
- alert(user.info.cellphone);
- alert(user.address[0].city);
- alert(user.address[0].postcode);
-
- user.username = "Tom";
- alert(user.username);
- }
JSON提供了json.js包,下载http://www.json.org/json.js 后,将其引入然后就可以简单的使用object.toJSONString()转换成JSON数据。 js 代码 - function showCar() {
- var carr = new Car("Dodge", "Coronet R/T", 1968, "yellow");
- alert(carr.toJSONString());
- }
-
- function Car(make, model, year, color) {
- this.make = make;
- this.model = model;
- this.year = year;
- this.color = color;
- }
可以使用eval来转换JSON字符到Object js 代码 - function myEval() {
- var str = '{ "name": "Violet", "occupation": "character" }';
- var obj = eval('(' + str + ')');
- alert(obj.toJSONString());
- }
或者使用parseJSON()方法 js 代码 - function myEval() {
- var str = '{ "name": "Violet", "occupation": "character" }';
- var obj = str.parseJSON();
- alert(obj.toJSONString());
- }
下面使用prototype写一个JSON的ajax例子。 先写一个servlet (我的是servlet.ajax.JSONTest1.java)就写一句话 java 代码 - response.getWriter().print("{ \"name\": \"Violet\", \"occupation\": \"character\" }");
再在页面中写一个ajax的请求 js 代码 - function sendRequest() {
- var url = "/MyWebApp/JSONTest1";
- var mailAjax = new Ajax.Request(
- url,
- {
- method: 'get',
- onComplete: jsonResponse
- }
- );
- }
-
- function jsonResponse(originalRequest) {
- alert(originalRequest.responseText);
- var myobj = originalRequest.responseText.parseJSON();
- alert(myobj.name);
- }
prototype-1.5.1.js中提供了JSON的方法,String.evalJSON(), 可以不使用json.js, 修改上面的方法 js 代码 - function jsonResponse(originalRequest) {
- alert(originalRequest.responseText);
- var myobj = originalRequest.responseText.evalJSON(true);
- alert(myobj.name);
- }
JSON还提供了java的jar包 http://www.json.org/java/index.html API也很简单,下面举个例子 在javascript中填加请求参数 js 代码 - function sendRequest() {
- var carr = new Car("Dodge", "Coronet R/T", 1968, "yellow");
- var pars = "car=" + carr.toJSONString();
-
- var url = "/MyWebApp/JSONTest1";
- var mailAjax = new Ajax.Request(
- url,
- {
- method: 'get',
- parameters: pars,
- onComplete: jsonResponse
- }
- );
- }
使用JSON请求字符串就可以简单的生成JSONObject并进行解析,修改servlet添加JSON的处理(要使用json.jar) java 代码 - private void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
- String s3 = request.getParameter("car");
- try {
- JSONObject jsonObj = new JSONObject(s3);
- System.out.println(jsonObj.getString("model"));
- System.out.println(jsonObj.getInt("year"));
- } catch (JSONException e) {
- e.printStackTrace();
- }
- response.getWriter().print("{ \"name\": \"Violet\", \"occupation\": \"character\" }");
- }
同样可以使用JSONObject生成JSON字符串,修改servlet java 代码 - private void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
- String s3 = request.getParameter("car");
- try {
- JSONObject jsonObj = new JSONObject(s3);
- System.out.println(jsonObj.getString("model"));
- System.out.println(jsonObj.getInt("year"));
- } catch (JSONException e) {
- e.printStackTrace();
- }
-
- JSONObject resultJSON = new JSONObject();
- try {
- resultJSON.append("name", "Violet")
- .append("occupation", "developer")
- .append("age", new Integer(22));
- System.out.println(resultJSON.toString());
- } catch (JSONException e) {
- e.printStackTrace();
- }
- response.getWriter().print(resultJSON.toString());
- }
js 代码 - function jsonResponse(originalRequest) {
- alert(originalRequest.responseText);
- var myobj = originalRequest.responseText.evalJSON(true);
- alert(myobj.name);
- alert(myobj.age);
- }
参考 http://www.json.org/js.html http://www.blogjava.net/Jkallen/archive/2006/03/28/37905.html http://www.json.org/ http://www.prototypejs.org/learn/json http://www.json.org/java/index.html http://www.ibm.com/developerworks/cn/web/wa-ajaxintro10/index.html 使用JSON JSON也就是JavaScript Object Notation,是一个描述数据的轻量级语法。JSON的优雅是因为它是JavaScript语言的一个子集。接下来你将看到它为什么如此重要。首先,来比较一下JSON和XML语法。 JSON和XML都使用结构化方法描述数据。例如一个地址簿应用程序可以提供用来产生XML格式的地址卡的web服务: <?xml version='1.0' encoding='UTF-8'?> <card> <fullname>Sean Kelly</fullname> <org>SK Consulting</org> <emailaddrs> <address type='work'>kelly@seankelly.biz</address> <address type='home' pref='1'>kelly@seankelly.tv</address> </emailaddrs> <telephones> <tel type='work' pref='1'>+1 214 555 1212</tel> <tel type='fax'>+1 214 555 1213</tel> <tel type='mobile'>+1 214 555 1214</tel> </telephones> <addresses> <address type='work' format='us'>1234 Main St Springfield, TX 78080-1216</address> <address type='home' format='us'>5678 Main St Springfield, TX 78080-1316</address> </addresses> <urls> <address type='work'>http://seankelly.biz/</address> <address type='home'>http://seankelly.tv/</address> </urls> </card> 使用JSON, 形式如下: { "fullname": "Sean Kelly", "org": "SK Consulting", "emailaddrs": [ {"type": "work", "value": "kelly@seankelly.biz"}, {"type": "home", "pref": 1, "value": "kelly@seankelly.tv"} ], "telephones": [ {"type": "work", "pref": 1, "value": "+1 214 555 1212"}, {"type": "fax", "value": "+1 214 555 1213"}, {"type": "mobile", "value": "+1 214 555 1214"} ], "addresses": [ {"type": "work", "format": "us", "value": "1234 Main StnSpringfield, TX 78080-1216"}, {"type": "home", "format": "us", "value": "5678 Main StnSpringfield, TX 78080-1316"} ], "urls": [ {"type": "work", "value": "http://seankelly.biz/"}, {"type": "home", "value": "http://seankelly.tv/"} ] } 如你所看到的,JSON有结构化的嵌套数据元素,这一点和XML相似。JSON也是基于文本的,XML也是如此。两者都使用Unicode。JSON和XML都很容易阅读。主观上,JSON更清晰,冗余更少。JSON WEB站点严格地描述了JSON语法,目前就是这样的。它确实是一个简单的小语言! XML确实适合标记文档,但是JSON是数据交互的理想格式。每个JSON文档描述了一个这样一个对象,该对象包含有:嵌套对象、数组、字符串、数字、布尔值或空值。 在这些地址卡例子代码中,JSON版本是更轻量级的,只占用了682字节的空间,而XML版本需要744字节空间。尽管这不是一个可观的节省。而实际的好处则来自解析过程。 XML对比JSON:地位丧失 通过使用XMLHttpRequest对象,可以从你的基于AJAX的应用程序取得XML和JSON文件。典型的,交互代码如下: var req = new XMLHttpRequest(); req.open("GET", "http://localhost/addr?cardID=32", /*async*/true); req.onreadystatechange = myHandler; req.send(/*no params*/null); 作为WEB服务器响应,你提供的处理器函数(myHandler函数)被多次调用,为你提供提前终止事务,更新进度条等机会。通常的,只有在web请求完成以后才起作用:那时,你就可以使用返回的数据了。 为了处理XML版本的地址卡数据,myHandler的代码如下: function myHandler() { if (req.readyState == 4 /*complete*/) { // Update address field in a form with first street address var addrField = document.getElementById('addr'); var root = req.responseXML; var addrsElem = root.getElementsByTagName('addresses')[0]; var firstAddr = addrsElem.getElementsByTagName('address')[0]; var addrText = fistAddr.firstChild; var addrValue = addrText.nodeValue; addrField.value = addrValue; } } 值得注意的是你不必解析XML文档:XMLHttpRequest对象自动地解析了,并使responseXML中的DOM树可用。通过使用responseXML属性,可以调用getElementsByTagName方法查找文档的地址部分,你还可以使用第一个去找到它。然后,可以再次调用getElementsByTagName在地址部分查找第一个地址元素。这就取得了文档的第一个DOM子节点,就是一个文本节点,并取得节点的值,这就是你想要的街道地址。最后,可以在表单域中显示结果。 确实不是一个简单的工作,现在,使用JSON再试一下: function myHandler() { if (req.readyState == 4 /*complete*/) { var addrField = document.getElementById('addr'); var card = eval('(' + req.responseText + ')'); addrField.value = card.addresses[0].value; } } 你所做的第一件事情就是解析JSON响应。但是,因为JSON是JavaScript的一个子集,你可以使用JavaScript自己的编译器来解析它,通过调用eval函数。解析JSON仅需要一行!此外,操纵JSON中的对象就像操纵其他JavaScript对象一样。这显然要比通过DOM树来操纵简单,例如: card.addresses[0].value 是第一个街道地址, "1234 Main Stb &" card.addresses[0].type 是地址类型, "work" card.addresses[1] 是家庭地址对象 card.fullname 是card的名称, "Sean Kelly" 如果更仔细观察,你可能会发现XML格式中文档至少有一个跟元素,card。这在JSON里是不存在的,为什么? 大概就是,如果你正在开发JavaScript来访问Web服务,你已经知道你想要得到的。然而,你可以在JSON中这么使用: {"card": {"fullname": ...}} 使用这个技术,你的JSON文件总是以一个带有单一命名属性的对象开始,该属性标识了对象的种类。 JSON是快速可靠的吗? JSON提供轻量的小文档,并且JSON在JavaScript更容易使用。XMLHttpRequest自动为你解析了XML文档,而你还要手工解析JSON文件,但是解析JSON比解析XML更慢么?作者通过几千次的反复测试,使用XMLHttpRequest解析XML和解析JSON,结果是解析JSON比XML要快10倍!当把AJAX当作桌面应用看待时,速度是最重要的因素,很明显,JSON更优秀。 当然,你不能总是控制服务器端来为AJAX程序产生数据。你还可以使用第三方服务器代替服务器提供XML格式的输出。并且,如果服务器恰好提供JSON,你可以确定你真的想使用它吗? 代码中值得注意的是,你将响应文本直接传入到eval中。如果你控制着服务器,就可以这么做。如果不是,一个恶意服务器可以使你的浏览器执行危险操作。在这样的情况下,你最好使用写在JavaScript中的代码来解析JSON。幸运地,这已经有了。 说到解析,Python爱好者可能注意到JSON不只是JavaScript的子集,它还是Python的一个子集。你可以在Python中直接执行JSON,或者使用安全JSON解析代替。JSON.org网站列举了许多常用JSON解析器。 服务器端的JSON 到现在为止,你或许将焦点注意在运行在客户浏览器中的基于AJAX的web应用程序使用JSON。自然地,首先,JSON格式的数据必须在服务器端产生。幸运地是,创建JSON或将其他存在的数据转换成JSON是相当简单的。一些WEB应用程序框架,例如TurboGears,自动包括对JSON输出的支持。 此外商业WEB服务提供商也注意到了JSON。Yahoo最近创建了许多基于JSON的web服务。Yahoo的多种搜索服务,履行计划,del.icio.us,还有高速公路交通服务也都支持JSON输出。毫无疑问,其他主要WEB服务提供商也将加入到对JSON的支持中。 总结 JSON的聪明在于它是JavaScript和Python的子集,使得它更易用,为AJAX提供高效的数据交互。它解析更快,比XML更易使用。JSON正成为现在“Web 2.0”的最强音。每个开发者,无论是标准桌面应用程序或Web应用程序,越来越注意到了它的简单和便捷。我希望你能体会到在buzzword-compliant, Web-2.0-based, AJAX-enabled, 敏捷开发中应用到JSON的乐趣。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!418.entry
感谢原作者的努力,struts1.x用的熟练的朋友可以很快上手Struts2 下面只粘贴六篇的链接地址。 第一篇|第二篇|第三篇|第四篇|第五篇|第六篇 另外,听说JDK1.6要出Update10了,通常这种Update Release是不会对核心的公共API做出修改的。这次Update虽然也没有修改公共API,但是新增了许多细节,并且更改了JRE的分发方式,支持按需获取不同大小的JRE,从而使得Java程序更轻快的运行在各种系统中。 有关此次更新的官网链接: Introducing Java SE 6 update 10 Beta 看来JavaFX有望成为Java技术带来的下一个经济增长点。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!415.entry
Mozilla Public License MPL License,允许免费重发布、免费修改,但要求修改后的代码版权归软件的发起者。这种授权维护了商业软件的利益,,它要求基于这种软件得修改无偿贡献版权给该软件。这样,围绕该软件得所有代码得版权都集中在发起开发人得手中。但MPL是允许修改,无偿使用得。MPL软件对链接没有要求。 BSD开源协议 BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。 当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件: 1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。 2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。 3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。 BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。 Apache Licence 2.0 Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件: 1. 需要给代码的用户一份Apache Licence 2. 如果你修改了代码,需要再被修改的文件中说明。 3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。 4. 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。 Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。 GPL GPL许可证是自由软件的应用最广泛的软件许可证,人们可以修改程式的一个或几个副本或程式的任何部分,以此形成基於这些程式的衍生作品。必须在修改过的档案中附有明显的说明:您修改了此一档案及任何修改的日期。您必须让您发布或出版的作品,包括本程式的全部或一部分,或内含本程式的全部或部分所衍生的作品,允许第三方在此许可证条款下使用,并且不得因为此项授权行为而收费。 LGPL Linux就是采用了GPL。GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux,包括商业公司的linux和linux上各种各样的由个人,组织,以及商业软件公司开发的免费软件了。 GPL协议的主要内容是只要在一个软件中使用(“使用”指类库引用,修改后的代码或者衍生代码)GPL协议的产品,则该软件产品必须也采用 GPL协议,既必须也是开源和免费。这就是所谓的”传染性”。GPL协议的产品作为一个单独的产品使用没有任何问题,还可以享受免费的优势。 由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议,对于使用GPL协议的开源代码,商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。 其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似 Public Domain 公共域授权。将软件授权为公共域,这些软件包没有授权协议,任何人都可以随意使用它。 Artistic许可 使作者保持对进一步开发的控制。
文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!414.entry
前一段时间一直被正则表达式搞得晕头转向,现在好了,微软仅仅用几行就把正则表达式说完了。。。 虽然不是很全面,但是足够用了。 正则表达式运算符 “表达式”属性支持以下正则表达式运算符: 表达式 | 说明 | . | 指示任何字符。 | \ | 指示后面的字符应按原义而不是作为特殊字符进行解释。例如,\. 指示“.”。 | () | 将括号内的运算符分组。 | {n} | 生成前面项的 n 个实例。例如,a{2} 生成“aa”。 | {n,m} | 生成前面项的至少 n 个实例但不超过 m 个实例。例如,a{2,4} 生成“aa”、“aaa”或“aaaa”。 | {n,} | 生成前面项的 n 个或更多实例。例如,a{2,} 生成“aa”、“aaa”、“aaaa”、“aaaaa”等。 | * | 生成前面项的 0 个或多个实例。 | + | 生成前面项的 1 个或多个实例。 | ? | 生成前面项的 0 个或 1 个实例。 | | | 在 | 字符任一侧生成项。 | [aeiou] | 生成括号内的任何字符。 | [a-z] | 生成字符指定范围内的任何字符。 | [^aeiou] | 生成除括号内字符以外的任何字符。 | 原文地址:http://msdn2.microsoft.com/zh-cn/library/aa833197(VS.80).aspx 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!409.entry
更改IE浏览器默认的源文件编辑器 用户在浏览网页时,如果在网页中单击鼠标右键并选择菜单中的“查看源文件”选项后,系统就会调用记事本打开该网页的HTML源文件,用户可以通过修改注册表来更改默认的打开程序,然后如下: 一:打开注册表。 “开始”菜单->“运行”->输入 regedit 然后点确定。 二:打开HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\INTERNET EXPLORER,在其下新建一个主键"View Source Editor",在其下再新一个主键"Editor Name",又击右侧窗口中的"默认"将数值设为更换程序的路径及文件名. 三:重新启动INTERNET EXPLORER,查看某个网页的源文件,用户就会发现打开的程序已经更改了. **************************************************对于有需要查看网页源文件的朋友来说,用系统自带的文本编辑器,远远达不到我们的要求。 用UltraEdit就非常好了,它是一个非常出名,而且好用的文本编辑器。功能非常的多。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!406.entry
前段时间写的 深入浅出Java中文问题系列描述了很多Java各种应用中出现的中文问题,唯独没有说到文件的读写。最近用Java处理文件的时候,同样遇到了中文问题,觉得还是有必要总结一下,也使该系列的文章更加完整。 熟悉Java 的人都知道,在Java中,IO是分成两大部分的,分别对应字节和字符的操作,也就是Stream和Character,它们之间可以相互转换,桥梁就是StreamInputReader/StreamOutputWriter。为了更加清楚的了解它们之间的关系,我们可以看看它们所在的类结构。 java.lang.Object - java.io.InputStream (implements java.io.Closeable)
- java.io.OutputStream (implements java.io.Closeable, java.io.Flushable)
- java.io.RandomAccessFile (implements java.io.Closeable, java.io.DataInput, java.io.DataOutput)
- java.io.Reader (implements java.io.Closeable, java.lang.Readable)
- java.io.BufferedReader
- java.io.InputStreamReader
- java.io.Writer (implements java.lang.Appendable, java.io.Closeable, java.io.Flushable)
- java.io.BufferedWriter
- java.io.OutputStreamWriter
上面列出来的并不是Java.io中全部的类,但是对于文件读写来说已经足够了。通常,我们使用以下代码来进行文件的读写: public void naiveWrite() throws IOException{ FileWriter fw = new FileWriter("test.txt"); fw.write("中文你好"); fw.close(); }
public String naiveRead() throws IOException{ FileReader fr = new FileReader("test.txt"); BufferedReader br = new BufferedReader(fr); String str = br.readLine(); br.close(); fr.close(); return str; } 如果我们的是中文平台,上面代码是可以正常运行的。但是如果我们把这些代码放到一个ISO8859-1的系统上,中文问题就出来了(当然,前提你在javac的时指定了编码方式,如javac -encoding gb2312 ***.java,参看该系列前面的文章)。为什么呢?这是因为FileWriter和FileReader是辅助类,为了方便大家使用 OutputSteamWriterer 和 InputStreamReader 而屏蔽了字符集的设定操作,而采用系统默认的编码方式,而这在很多情况下也能满足用户的需求。在中文系统中,系统的默认编码方式一般是GBK,因此文件中中文的读写是没有问题的。但是,当程序运行在ISO8859-1的系统中时,JVM使用ISO8859-1对中文进行编码,当然就认不到了,于是那一个个的问号就来了。 那怎么办呢?既然捷径走不通,我们就只好使用OutputSteamWriter 和 InputStreamReader了。 public void write() throws IOException{ OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("test.txt"), "utf-8"); osw.write("中国万岁"); osw.close(); } public String read() throws IOException{ InputStreamReader isr = new InputStreamReader( new FileInputStream("test.txt"),"utf-8"); BufferedReader br = new BufferedReader(isr); String str = br.readLine(); br.close(); isr.close(); return str; } 在这里,我们指定文件读写的编码方式为utf-8,当然对于中文来说GBK和GB2312也是可以的,但是推荐使用UTF-8,这样对于软件的国际化很有好处。其实,这里指定编码方式进行文件的写入跟我们使用记事本等编辑器的另存为,并且指定格式为“UTF-8”在本质上是一样的。通过上述处理后,程序就可以跨平台运行了。 在处理文件的过程中,我们还会用到RandomAccessFile这个类来随机访问文件。这里,如果我们写入字符串的时候调用writeChars,那么,如果写入的是中文,中文问题就又会出现了。因为此时RandomAccessFile并没有使用系统的默认编码来写入文件,而是直接将内存中的二进制数据直接写到文件中去。如何解决这个问题呢?只要读写对称就行了。 public void randWrite() throws IOException{ RandomAccessFile raf = new RandomAccessFile("test1.txt","rw"); raf.writeChars("中国你好"); raf.close(); } public String randRead() throws IOException{ RandomAccessFile raf = new RandomAccessFile("test1.txt","r"); StringBuffer sb = new StringBuffer(); while( raf.getFilePointer() < raf.length()){ sb.append( raf.readChar() ); } raf.close(); return sb.toString(); } 但是这样处理起来不是很方便,我们可以这样写: public void randWrite() throws IOException{ RandomAccessFile raf = new RandomAccessFile("test1.txt","rw"); raf.writeUTF("中国你好"); raf.close(); } public String randRead() throws Exception{ RandomAccessFile raf = new RandomAccessFile("test1.txt","r"); String str = raf.readUTF(); raf.close(); return str; } 好了,文件读写的中文问题就解决了。 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!405.entry
1. Abstract: Java将I/O分为高阶I/O与低阶I/O,高阶I/O在使用上提供更多的读写方法,如读写 int、double、String的资料型态,而低阶的I/O大部份只提供write、read的byte[]存取,因为程式大部份的资料都是以字串或 其它主要型态资料来运算,因此低阶的I/O在使用上不利於程式设计,所以Java将许多好用的方法全部集合成高阶I/O; 换言之,低阶I/O的主要工作是负责与媒体资料作存取,高阶I/O类别主要作资料型态的转换及提供一些特殊的功能。在使用Java I/O时要谨记的一个重要原则是,在建立一个I/O之前必需先用低阶I/O类别来存取媒体资料(如档案或pipe),之後再使用高阶I/O来控制低阶 I/O类别的动作,这种一层又一层的架构称I/O Chain。底下为Java的I/O架构图,第一个为以byte为单位的I/O,第二个则是以char为单位。 2. File I/O: A. FileInputStream & FileOutputStream FileInputStream是读取档案用的类别,其建构式有叁个: public FileInputStream(String strFilename) throws FileNotFoundException public FileInputStream(File fIn) throws FileNotFoundException public FileInputStream(FileDescriptor fdObj) 在这里我只讲第一个,这是最直觉的方式,如下的范例1,会一次从e:\test.txt读10个bytes,将读入的结果输出到标准输出设备,直到档案结 束。在这个范例中要注意的是,available会传回输入串流中还有多少个bytes,read则会根据buffer的大小来决定一次读几个 bytes,并将实际读到的byte数传回。 ===== 范例 1 ===== import java.io.*; public class FIn { public FIn() { try { FileInputStream fis = new FileInputStream("e:/in.txt"); while (fis.available() > 0) { byte[] b = new byte[10]; int nResult = fis.read(b); if (nResult == -1) break; System.out.println(new String(b)); } fis.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { FIn fIn = new FIn(); } } FileOutputStream是写入档案用的类别,其建构式有四个: Public FileOutputStream(String strFilename) throws FileNotFoundException Public FileOutputStream(File fOut) throws FileNotFound Exception Public FileOutputStream(FileDescriptor fdObj) public FileOutputStream(String name, boolean append) throws FileNotFoundException 第四个和第一个的差别只在於当档案存在时,第一个会将原来的档案内容覆盖,第四个则可以选择覆盖或将新内容接在原内容後面。范例2以建构式一讲解如何写入 一个档案…在这个范例中要注意的是,fIn每个读10个bytes,但是最後一次不一定会读10个bytes,因此,fOut在write时,要指明要写 几个bytes到档案中,否则最後一次仍会写入10个bytes,因Java在new byte时会先将内容先填0,所以後面的几个bytes会是0。 ===== 范例2 ===== import java.io.*; public class FOut { public FOut() { try { FileInputStream fIn = new FileInputStream("e:/in.txt"); FileOutputStream fOut = new FileOutputStream("e:/out.txt"); while (fIn.available() > 0) { byte[] b = new byte[10]; int nResult = fIn.read(b); if (nResult == -1) break; fOut.write(b, 0, nResult); } fIn.close(); fOut.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { FOut FOut1 = new FOut(); } } B. FileReader & FileWriter FileReader 和FileInputStream最大不同在於,FileInputStream读取的单位是byte,FileReader读取的单位是char。另外 要注意的是,在FileInputStream中以available来判断是否还有资料可读取,在FileReader中是以ready来判断, 但是,available是传回还有多少个bytes可以读取,ready则传回true或false,当传回true时表示,下次read时保证不会停顿,当传回false时,表示下次read时”可能”停顿,所谓可能是指不保证不会停顿。 Ps. 测试时,in.txt里放些中文字就可以看出以byte和以char为单位有什麽不同。 ===== 范例 3 ===== import java.io.*; public class chFIn { public chFIn() { try { FileReader rdFile = new FileReader("e:/in.txt"); while (rdFile.ready()) { char[] chIn = new char[10]; int nResult = rdFile.read(chIn); if (nResult == -1) break; System.out.println(chIn); } rdFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { chFIn chFIn1 = new chFIn(); } } FileWriter和FileOutputStream的最大不同也在於写入单位的不同,FileOutputStream为byte,FileWriter为char。 ===== 范例 4 ===== import java.io.*; public class chFOut { public chFOut() { try { FileReader rdFile = new FileReader("e:/in.txt"); FileWriter wrFile = new FileWriter("e:/out.txt"); while (rdFile.ready()) { char[] chIn = new char[10]; int nResult = rdFile.read(chIn); if (nResult == -1) break; wrFile.write(chIn, 0, nResult); } rdFile.close(); wrFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { chFOut chFOut1 = new chFOut(); } } C. BufferedReader & BufferedWriter File I/O是相当耗时的作业,通常电脑在做处理时,者会建立一个缓冲区,一次读取或写入一个区块,借由减少I/O次数,来节省时间 ,在Java中的BufferedReader和BufferedWriter就是提供这样的缓冲功能。 在 范例5中,我们将FileReader导向BufferedReader,将FileWriter导向BufferedWriter,来达到区块读取、写 入的目的。BufferedReader提供的readLine一次可以读取一行,当遇到档尾时,会传回null。BufferedWriter提供的 newLine会产生列尾符号,这个列尾符号随作业系统的不同而不同,在Windows上为\r\n,在Unix上为\n,在Mac上为\r,这个符号是 依据line.separator系统性质而来的。需注意的是,如果将BufferedWriter应用到网路程式时,绝对不要使用newLine,因为 绝大多数的网路协定都是以\r\n为列尾,不会因作业系统不同而异。 ===== 范例 5 ===== import java.io.*; public class bufIn { public bufIn() { try { FileReader rdFile = new FileReader("e:/in.txt"); BufferedReader brdFile = new BufferedReader(rdFile); FileWriter wrFile = new FileWriter("e:/out.txt"); BufferedWriter bwrFile = new BufferedWriter(wrFile); String strLine; while ((strLine = brdFile.readLine()) != null) { bwrFile.write(strLine); bwrFile.newLine(); } brdFile.close(); bwrFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { bufIn bufIn1 = new bufIn(); } } D. File 在档案处理方面,程式不只是要对档案做读、写,有时也需要得知档案的属性,或删除、移动、更名,有时则是要找出或列出某目录下的某些档案,针对这些运作,Java提供了File这个类别。底下的范例,说明如何使用File类别。 a. 如何得知档案属性: 在 范例6中需注意的是lastModified传回的最後更改时间是自1970/1/1 00:00:00算起的时间,单位为毫秒,所以要用Date将它转换成日期、时间; 另外getCanonicalPath和getAbsolutePath得到的值在Windows上会是一样的,在Unix可能就会不一样。 ===== 范例 6 ===== import java.io.*; import java.util.*; public class FileSpy { public FileSpy(String strFilename) { File fFile = new File(strFilename); if (fFile.exists()) { System.out.println("Name: " + fFile.getName()); System.out.println("Absolute path: " + fFile.getAbsolutePath()); try { System.out.println("Canonical path: " + fFile.getCanonicalPath()); } catch (IOException e) { e.printStackTrace(); } if (fFile.canWrite()) System.out.println(fFile.getName() + " is writable"); if (fFile.canRead()) System.out.println(fFile.getName() + " is readable"); if (fFile.isFile()) { System.out.println(fFile.getName() + " is a file"); } else if (fFile.isDirectory()) { System.out.println(fFile.getName() + " is a directory"); } else { System.out.println("What is this?"); } long lngMilliseconds = fFile.lastModified(); if (lngMilliseconds !=0) System.out.println("last modified at " + new Date(lngMilliseconds)); long lngLen = fFile.length(); if (lngLen !=0) System.out.println("size: " + lngLen); } else System.out.println("file not found"); } public static void main(String[] args) { if (args.length == 1) { FileSpy fileSpy1 = new FileSpy(args[0]); } else System.out.println("Usage: java FileSpy Filename"); } } b. 建立、删除、移动、更名: File 类别提供了createNewFile、renameTo、delete作为建立(createNewFile)、删除(delete)、移动、更名 (renameTo)之用,使用方式如下: (移动和更名都用renameTo,就如在Unix上档案搬移和更名都用mv一样) ===== 范例 7 ===== import java.io.*; public class OperateFile { public OperateFile() { //create new file File fNewFile = new File("C:/newfile.txt"); try { if (fNewFile.exists() == false) { if (fNewFile.createNewFile() == true) { System.out.println("create c:/newfile.txt success"); } else { System.out.println("create c:/newfile.txt fail"); } } else { System.out.println("file already exists"); } } catch (IOException e) { e.printStackTrace(); } //rename file File fRenameFile = new File("c:/renamefile.txt"); fNewFile.renameTo(fRenameFile); //remove file File fRemoveFile = new File("d:/" + fRenameFile.getName()); fRenameFile.renameTo(fRemoveFile); //delete file try { File fDelFile = new File(fRemoveFile.getCanonicalPath()); fDelFile.delete(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { OperateFile operateFile1 = new OperateFile(); } } c. 找出某特定目录里的所有档案: File类别提供的list和listFiles都可以列出某特定目录里的所有档案,其中list传回的是String[],listFiles传回的是File[],这两个函式都会传回所有的档案和目录。 ===== 范例 8 ===== import java.io.*; public class ListAllFiles { public ListAllFiles(String strDir) { File fDir = new File(strDir); File[] fAllFiles = fDir.listFiles(); for(int i=0; i if (fAllFiles.isFile()) System.out.println("File: " + fAllFiles.getName()); else System.out.println("Dir: " + fAllFiles.getName()); } } public static void main(String[] args) { ListAllFiles listAllFiles1 = new ListAllFiles(args[0]); } } 3. Network I/O: Java对网路的支援只有TCP/IP和UDP/IP,提供的类别有URL、URLConnection、Socket、ServerSocket,在这里我只打算用ServerSocket、Socket为例,来说明Network I/O。 基本上,Java的I/O不管在任何的媒体上都是将它们视为stream,所以,网路I/O和档案I/O原理也是一致的,底下的两个程式分别为server socket及client socket。在看范例之前,可以再复习一下前面的abstract… ===== 范例 9 ===== import java.net.*; import java.io.*; public class myServer { public myServer(String strPort) { int nPort = new Integer(strPort).intValue(); try { ServerSocket ss = new ServerSocket(nPort); Socket s = ss.accept(); OutputStream out = s.getOutputStream(); PrintStream psOut = new PrintStream(out); String strResponse = "Hello " + s.getInetAddress() + " on port " + s.getPort() + "\r\n"; strResponse += "This is " + s.getLocalAddress() + " on port " + s.getLocalPort() + "\r\n"; psOut.print(strResponse); s.close(); ss.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { myServer myServer1 = new myServer(args[0]); } } ===== 范例 10 ===== import java.net.*; import java.io.*; public class myClient { public myClient(String strIP, String strPort) { int nPort = new Integer(strPort).intValue(); try { Socket s = new Socket(strIP, nPort); InputStream in = s.getInputStream(); BufferedInputStream bisIn = new BufferedInputStream(in); while (bisIn.available() > 0) { byte[] b = new byte[30]; int nLen = bisIn.read(b); System.out.println(new String(b, 0, nLen)); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { myClient myClient1 = new myClient(args[0], args[1]); } } 4. Object Serialization: A. 所谓Object Serialization就是把物件的”状态”储存成一系列的位元组,而这些位元组在稍候可用来恢复物件。更简单的说,Object Serialization是让物件可以以物件为储存单位。在Java中,任何物件要能Serialization,必须implements Serializable这个Interface,以下是一个简单的程式范例,可以将物件储存到e:\point.ser,或从e:\point.ser 将物件恢复原值。 ===== 范例 11 ===== import java.io.*; public class ThreeDPoint implements Serializable { private double m_dblX, m_dblY, m_dblZ; public ThreeDPoint(double x, double y, double z) { m_dblX = x; m_dblY = y; m_dblZ = z; } public void PrintXYZ() { System.out.println("X: " + m_dblX); System.out.println("Y: " + m_dblY); System.out.println("Z: " + m_dblZ); } public static void main(String[] args) { if (args[0].equalsIgnoreCase("w")) { ThreeDPoint threeDPoint1 = new ThreeDPoint(10 ,20, 30); try { FileOutputStream fout = new FileOutputStream("e:\\point.ser"); ObjectOutputStream oout = new ObjectOutputStream(fout); oout.writeObject(threeDPoint1); oout.close(); System.out.println("write:"); threeDPoint1.PrintXYZ(); } catch (Exception e) { e.printStackTrace(); } } else if (args[0].equalsIgnoreCase("r")) { try { FileInputStream fin = new FileInputStream("e:\\point.ser"); ObjectInputStream oin = new ObjectInputStream(fin); Object o = oin.readObject(); ThreeDPoint threeDPoint1 = (ThreeDPoint) o; oin.close(); System.out.println("read:"); threeDPoint1.PrintXYZ(); } catch (Exception e) { } } } //end of main } B. 在Java中,一个实作某特定介面的类别,其子类别也因继承的原故而被视为实作了该介面,因此,许多没有明确宣告实作Serializable介面的类别,事实上也是可以被Serialization的。 C. 并非每个实作了Serializable介面的物件都可以被Serialization,如果这个物件继承图上的祖先,有其中一个是不可以被Serialization,那麽这个物件就不可以被Serialization。 5. Formated I/O: 在Java 的I/O里,并没有所谓的型别,不管是int、long、double…最後都是以String输出,所以如果要让数字以特定格式输出,需透过Java提 供的两个类别java.text.NumberFormat和java.text.DecimalFormat将数字格式化後再输出。 范例12简 要说明NumberFormat如何使用,在开始使用NumberFormat时,应先用getInstance取得NumberFormat的实体,范 例12中的setMaximumIntegerDigits和setMinimumFractionDigits是用来设定整数和小数的位数,另外还有 setMinimumIntegerDigits和setMaximumFractionDigits也是同样功能。这些设定如有冲突,Java以最後设 定的为准。 ===== 范例 12 ===== import java.text.*; public class myFormat { public myFormat() { NumberFormat nf = NumberFormat.getInstance(); double dblNum = Math.PI; System.out.println(dblNum); nf.setMaximumIntegerDigits(5); nf.setMinimumFractionDigits(4); System.out.println("PI: " + nf.format(dblNum)); } public static void main(String[] args) { myFormat myFormat1 = new myFormat(); } } 另 一个类别DecimalFormat是继承NumberFormat的子类别,它提供了更强的格式化功能,透过设定pattern,可以使我们的输出更多 样化,至於Java提供的pattern有那些? 在API Document中有详细说明! 范例13仅举其一,说明DecimalFormat如何使用。 ===== 范例 13 ===== import java.text.*; public class myDecimalFormat { public myDecimalFormat() { DecimalFormat df = new DecimalFormat("0000.000"); double dblNum = 123.45; System.out.println("dblNum: " + dblNum); System.out.println("dblNum: " + df.format(dblNum)); } public static void main(String[] args) { myDecimalFormat myDecimalFormat1 = new myDecimalFormat(); } } 文章来源: http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!399.entry
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
常用链接
留言簿(6)
随笔分类(28)
随笔档案(90)
文章分类(1)
文章档案(1)
收藏夹(4)
牛人牛博
酷站
最新随笔
搜索
最新评论
阅读排行榜
评论排行榜
|
|