#
http://www.zhuicha.com/zhuicha/onlineHB/linuxcmd/ 此网站列举了常用命令
文件传输
备份压缩
文件管理
磁盘管理
磁盘维护
系统设置
系统管理
文档编辑
网络通讯
电子邮件与新闻组
X WINDOWS SYSTEM
对于这个系列里的问题,每个学Java的人都应该搞懂。当然,如果只是学Java玩玩就无所谓了。如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列。内容均来自于CSDN的经典老贴。
问题一:我声明了什么!
String s = "Hello world!";
许多人都做过这样的事情,但是,我们到底声明了什么?回答通常是:一个String,内容是“Hello world!”。这样模糊的回答通常是概念不清的根源。如果要准确的回答,一半的人大概会回答错误。
这个语句声明的是一个指向对象的引用,名为“s”,可以指向类型为String的任何对象,目前指向"Hello world!"这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量。所以,如果在刚才那句语句后面,如果再运行一句:
String string = s;
我们是声明了另外一个只能指向String对象的引用,名为string,并没有第二个对象产生,string还是指向原来那个对象,也就是,和s指向同一个对象。
问题二:"=="和equals方法究竟有什么区别?
==操作符专门用来比较变量的值是否相等。比较好理解的一点是:
int a=10;
int b=10;
则a==b将是true。
但不好理解的地方是:
String a=new String("foo");
String b=new String("foo");
则a==b将返回false。
根据前一帖说过,对象变量其实是一个引用,它们的值是指向对象所在的内存地址,而不是对象本身。a和b都使用了new操作符,意味着将在内存中产生两个内容为"foo"的字符串,既然是“两个”,它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值,所以使用"=="操作符,结果会是false。诚然,a和b所指的对象,它们的内容都是"foo",应该是“相等”,但是==操作符并不涉及到对象内容的比较。
对象内容的比较,正是equals方法做的事。
看一下Object对象的equals方法是如何实现的:
boolean equals(Object o){
return this==o;
}
Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法,那你的类使用equals和使用==会得到同样的结果。同样也可以看出,Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的创建者决定,所以Object把这个任务留给了类的创建者。
看一下一个极端的类:
Class Monster{
private String content;
...
boolean equals(Object another){ return true;}
}
我覆盖了equals方法。这个实现会导致无论Monster实例内容如何,它们之间的比较永远返回true。
所以当你是用equals方法判断对象的内容是否相等,请不要想当然。因为可能你认为相等,而这个类的作者不这样认为,而类的equals方法的实现是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列码的集合(HashSet,HashMap,HashTable),请察看一下java doc以确认这个类的equals逻辑是如何实现的。
继续深入讨论一下“==”和equals,看如下例子
public class TestStringIntern {
public static void main(String[] args) {
testIt();
}
private static void testIt() {
String s1 = "sean_gao";
String s2 = "sean"+"_"+"gao";
String s3 = new String("sean_gao");
String s4 = new String("sean_gao").intern();
System.out.println("s1==s2? "+(s1==s2));
System.out.println("s1==s3? "+(s1==s3));
System.out.println("s1==s4? "+(s1==s4));
System.out.println("s1.equals(s2)? "+(s1.equals(s2)));
System.out.println("s1.equals(s3)? "+(s1.equals(s3)));
System.out.println("s1.equals(s4)? "+(s1.equals(s4)));
}
}
以下是结果:
s1==s2? true // 引用的是同一个对象,因为内容一致
s1==s3? false // 引用的是不同的对象,因为用了new关键字
s1==s4? true // 引用的是同一个对象,因为用了intern方法
s1.equals(s2)? true // 内容一致
s1.equals(s3)? true // 内容一致
s1.equals(s4)? true // 内容一致
再次解释:对于String对象,如果是按照String s = "some string";这样的形式声明的,如果同一个JVM中恰好有相同内容的String对象,那么这个s指向的就是那个已有的对象。但如果使用String s = new String("some string");这样的语法,那么不管JVM中有没有可以重用的String对象,都将新建一个对象。
==操作符判断的是对象引用是否指向同一个对象,而equals方法在String类中的实现是判断String对象的内容是否一致。
问题三:String到底变了没有?
没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。请看下列代码:
String s = "Hello";
s = s + " world!";
s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论。我们来看看发生了什么事情。在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。
问题四:final关键字到底修饰了什么?
final使得被修饰的变量"不变",但是由于对象型变量的本质是“引用”,使得“不变”也有了两种含义:引用本身的不变,和引用指向的对象不变。
引用本身的不变:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//编译期错误
引用指向的对象不变:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //编译通过
可见,final只对引用的“值”(也即它所指向的那个对象的内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。这很类似==操作符:==操作符只负责引用的“值”相等,至于这个地址所指向的对象内容是否相等,==操作符是不管的。
理解final问题有很重要的含义。许多程序漏洞都基于此----final只能保证引用永远指向固定对象,不能保证那个对象的状态不变。在多线程的操作中,一个对象会被多个线程共享或修改,一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃。一个错误的解决方法就是在此对象新建的时候把它声明为final,意图使得它“永远不变”。其实那是徒劳的。
问题五:到底要怎么样初始化!
本问题讨论变量的初始化,所以先来看一下Java中有哪些种类的变量。
1. 类的属性,或者叫值域
2. 方法里的局部变量
3. 方法的参数
对于第一种变量,Java虚拟机会自动进行初始化。如果给出了初始值,则初始化为该初始值。如果没有给出,则把它初始化为该类型变量的默认初始值。
int类型变量默认初始值为0
float类型变量默认初始值为0.0f
double类型变量默认初始值为0.0
boolean类型变量默认初始值为false
char类型变量默认初始值为0(ASCII码)
long类型变量默认初始值为0
所有对象引用类型变量默认初始值为null,即不指向任何对象。注意数组本身也是对象,所以没有初始化的数组引用在自动初始化后其值也是null。
对于两种不同的类属性,static属性与instance属性,初始化的时机是不同的。instance属性在创建实例的时候初始化,static属性在类加载,也就是第一次用到这个类的时候初始化,对于后来的实例的创建,不再次进行初始化。这个问题会在以后的系列中进行详细讨论。
对于第二种变量,必须明确地进行初始化。如果再没有初始化之前就试图使用它,编译器会抗议。如果初始化的语句在try块中或if块中,也必须要让它在第一次使用前一定能够得到赋值。也就是说,把初始化语句放在只有if块的条件判断语句中编译器也会抗议,因为执行的时候可能不符合if后面的判断条件,如此一来初始化语句就不会被执行了,这就违反了局部变量使用前必须初始化的规定。但如果在else块中也有初始化语句,就可以通过编译,因为无论如何,总有至少一条初始化语句会被执行,不会发生使用前未被初始化的事情。对于try-catch也是一样,如果只有在try块里才有初始化语句,编译部通过。如果在catch或finally里也有,则可以通过编译。总之,要保证局部变量在使用之前一定被初始化了。所以,一个好的做法是在声明他们的时候就初始化他们,如果不知道要出事化成什么值好,就用上面的默认值吧!
其实第三种变量和第二种本质上是一样的,都是方法中的局部变量。只不过作为参数,肯定是被初始化过的,传入的值就是初始值,所以不需要初始化。
问题六:instanceof是什么东东?
instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。举个例子:
String s = "I AM an Object!";
boolean isObject = s instanceof Object;
我们声明了一个String对象引用,指向一个String对象,然后用instancof来测试它所指向的对象是否是Object类的一个实例,显然,这是真的,所以返回true,也就是isObject的值为True。
instanceof有一些用处。比如我们写了一个处理账单的系统,其中有这样三个类:
public class Bill {//省略细节}
public class PhoneBill extends Bill {//省略细节}
public class GasBill extends Bill {//省略细节}
在处理程序里有一个方法,接受一个Bill类型的对象,计算金额。假设两种账单计算方法不同,而传入的Bill对象可能是两种中的任何一种,所以要用instanceof来判断:
public double calculate(Bill bill) {
if (bill instanceof PhoneBill) {
//计算电话账单
}
if (bill instanceof GasBill) {
//计算燃气账单
}
...
}
这样就可以用一个方法处理两种子类。
然而,这种做法通常被认为是没有好好利用面向对象中的多态性。其实上面的功能要求用方法重载完全可以实现,这是面向对象变成应有的做法,避免回到结构化编程模式。只要提供两个名字和返回值都相同,接受参数类型不同的方法就可以了:
public double calculate(PhoneBill bill) {
//计算电话账单
}
public double calculate(GasBill bill) {
//计算燃气账单
}
所以,使用instanceof在绝大多数情况下并不是推荐的做法,应当好好利用多态。
主要就我所了解的J2EE开发的框架或开源项目做个介绍,可以根据需求选用适当的开源组件进行开发.主要还是以Spring为核心,也总结了一些以前web开发常用的开源工具和开源类库
1持久层:
1)Hibernate
这个不用介绍了,用的很频繁,用的比较多的是映射,包括继承映射和父子表映射
对于DAO在这里介绍个在它基础上开发的包bba96,目前最新版本是bba96 2.0它对Hibernate进行了封装, 查询功能包括执行hsql或者sql查询/更新的方法,如果你要多层次逻辑的条件查询可以自己组装QueryObject.可以参考它做HibernateDAO.也可以直接利用它
2) iBATIS
另一个ORM工具,Apache的,没有Hibernate那么集成,自由度比较大
2:SpringMVC
原理说明和快速入门:
配置文件为:
Spring的配置文件默认为WEB-INF/xxxx-servelet.xm其中xxx为web.xml中org.springframework.web.servlet.DispatcherServlet的servlet-name。
Action分发:
Spring将按照配置文件定义的URL,Mapping到具体Controller类,再根据URL里的action= xxx或其他参数,利用反射调用Controller里对应的Action方法。
输入数据绑定:
Spring提供Binder 通过名字的一一对应反射绑定Pojo,也可以直接从request.getParameter()取数据。
输入数据验证
Sping 提供了Validator接口当然还可以使用开源的Commons-Validaor支持最好
Interceptor(拦截器)
Spring的拦截器提供接口需要自己编写,在这点不如WebWork做的好.全面
(这里提一下WebWork和Struts的区别最主要的区别在于WebWork在建立一个Action时是新New一个对象而Struts是SingleMoule所有的都继承它的一个Action,所以根据项目需要合适的选择.)
3:View层
1) 标签库:JSP2.0/JSTL
由于Webwork或Spring的标签确实很有限,一般view层用JSTL标签,而且据说JSTL设计很好速度是所有标签中最快的使用起来也很简单
2) 富客户端:DOJO Widgets, YUI(YahooUI),FCKEditor, Coolest日历控件
Dojo主要提供Tree, Tab等富客户端控件,可以用其进行辅助客户端开发
YahooUI和DOJO一样它有自己的一套javascript调试控制台,主要支持ajax开发也有很多Tree,Table,Menu等富客户端控件
FCKEditor 最流行的文本编辑器
Coolest日历控件 目前很多日历控件可用,集成在项目中也比较简单,这个只是其中的一个,界面不错的说..
3) JavaScript:Prototype.js
Prototype.js作为javascript的成功的开源框架,封装了很多好用的功能,通过它很容易编写AJAX应用,现在AJAX技术逐渐成熟,框架资源比较丰富,比如YUI,DWR等等,也是因为JavaScript没有合适的调试工具,所以没有必要从零开始编写AJAX应用,个人认为多用一些成熟的Ajax框架实现无刷新更新页面是不错的选择.
4)表格控件:Display Tag ,Extreme Table
这两个的功能差不多,都是View层表格的生成,界面也比较相向,可以导出Excel,Pdf,对Spring支持很容易.
相比较而言比较推荐ExtremeTable,它的设计很好功能上比DisplayTag多一些,支持Ajax,封装了一些拦截器,而且最方面的是在主页wiki中有详细的中文使用文档.
5):OSCache
OSCache是OpenSymphony组织提供的一个J2EE架构中Web应用层的缓存技术实现组件,Cache是一种用于提高系统响应速度、改善系统运行性能的技术。尤其是在Web应用中,通过缓存页面的输出结果,可以很显著的改善系统的稳定性和运行性能。
它主要用在处理短时间或一定时间内一些数据或页面不会发生变化,或将一些不变的统计报表,缓冲在内存,可以充分的减轻服务器的压力,防治负载平衡,快速重启服务器(通过硬盘缓存).
6)SiteMesh
sitemesh应用Decorator模式主要用于提高页面的可维护性和复用性,其原理是用Filter截取request和response,把页面组件head,content,banner结合为一个完整的视图。通常我们都是用include标签在每个jsp页面中来不断的包含各种header, stylesheet, scripts and footer,现在,在sitemesh的帮助下,我们删掉他们轻松达到复合视图模式.
Sitemesh也是 OpenSymphony的一个项目现在最近的版本是2.2,目前OpenSymphony自从04年就没有更新的版本了..感觉它还是比较有创新的一种页面组装方式, OpenSymphony开源组织的代码一般写的比较漂亮,可以改其源代码对自己的项目进行适配.
测试发现Sitemesh还存在一些问题,比如中文问题,它的默认编码是iso-8859-1在使用时候需要做一些改动.
7)CSS,XHTML
这个不用说了,遵循W3C标准的web页面开发.
8)分页标签: pager-taglib组件
Pager-taglib 是一套分页标签库,可以灵活地实现多种不同风格的分页导航页面,并且可以很好的与服务器分页逻辑分离.使用起来也比较简单.
9)Form: Jodd Form taglib
Jodd Form taglib使用比较简单,只要把<form>的头尾以<jodd:form bean= "mybean">包住
就会自动绑定mybean, 自动绑定mybean的所有同名属性到普通html标记input, selectbox, checkbox,radiobox.....在这些input框里不用再写任何代码…
10)Ajax:DWR
J2EE应用最常用的ajax框架
11)报表 图表
Eclipse BIRT功能比较强大,也很庞大..好几十M,一般没有特别需求或别的图表设计软件可以解决的不用它
JasperReports+ iReport是一个基于Java的开源报表工具,它可以在Java环境下像其它IDE报表工具一样来制作报表。JasperReports支持PDF、HTML、XLS、CSV和XML文件输出格式。JasperReports是当前Java开发者最常用的报表工具。
JFreeChart主要是用来制作各种各样的图表,这些图表包括:饼图、柱状图(普通柱状图以及堆栈柱状图)、线图、区域图、分布图、混合图、甘特图以及一些仪表盘等等。
琴棋报表,国产的..重点推荐,适合中国的情况,开放源代码,使用完全免费。纯JAVA开发,适用多种系统平台。特别适合B/S结构的系统。官方网站有其优点介绍,看来用它还是不错的选择,最重要的是支持国产呵呵
4:权限控制: Acegi
Acegi是Spring Framework 下最成熟的安全系统,它提供了强大灵活的企业级安全服务,如完善的认证和授权机制,Http资源访问控制,Method 调用访问控制等等,支持CAS
(耶鲁大学的单点登陆技术,这个单点登陆方案比较出名.我也进行过配置使用,可以根据项目需要,如果用户分布在不同的地方不同的系统通用一套登陆口令可以用它进行解决,一般注册机登陆机就是这样解决的)
Acegi只是于Spring结合最好的安全框架,功能比较强大,当然还有一些其他的安全框架,这里列举一些比较流行的是我从网上找到的,使用方法看其官方文档把…
JAAS, Seraph, jSai - Servlet Security, Gabriel, JOSSO, Kasai, jPAM, OpenSAML都是些安全控制的框架..真够多的呵呵
5:全文检索
1) Lucene
Lucene是一套全文索引接口,可以通过它将数据进行倒排文件处理加入索引文件,它的索引速度和查询速度是相当快的,查询百万级数据毫秒级出结果,现在最火的Apache开源项目,版本更新速度很快现在已经到了2.0,每个版本更新的都比较大,目前用的最多的版本应该是1.4.3,但它有个不太方面的地方单个索引文件有2G文件限制,现在2.0版本没有这个限制,我研究的比较多,它的扩展性比较好,可以很方面的扩充其分词接口和查询接口.
基于它的开发的系统很多,比如最常用的Eclipse的搜索功能,还有一些开源的软件比如Compass,Nutch,Lius,还有我最近做的InSearch(企业级FTP文件网页搜索)
6:公共Util类
主要是Jakarta-Commons类库,其中最常用得是以下几个类库
1) Jakarta-Commons-Language
最常用得类是StringUtils类,提供了使用的字符串处理的常用方法效率比较高
2) Jakarta-Commons-Beantuils
主要用Beantuils能够获得反射函数封装及对嵌套属性,map,array型属性的读取。
3) Jakarta-Commons-Collections
里面有很多Utils方法
7 日志管理
Log4J
任务是日志记录,分为Info,Warn,error几个层次可以更好的调试程序
8 开源的J2EE框架
1) Appfuse
Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架, 它对如何集成流行的Spring、Hibernate、iBatis、Struts、Xdcolet、JUnit等基础框架给出了示范. 在持久层,AppFuse采用了Hibernate O/R映射工具;在容器方面,它采用了Spring,用户可以自由选择Struts、Spring/MVC,Webwork,JSF这几个Web框架。
2) SpringSide
.SpringSide较完整的演示了企业应用的各个方面,是一个电子商务网站的应用 SpringSide也大量参考了Appfuse中的优秀经验。最重要的是它是国内的一个开源项目,可以了解到国内现在的一些实际技术动态和方向很有指导意义…
9:模版 Template
主要有Veloctiy和Freemarker
模板用Servlet提供的数据动态地生成 HTML。编译器速度快,输出接近静态HTML 页面的速度。
10:工作流
我所知道比较出名的主要有JBpm Shark Osworkflow,由于对它没有过多的研究所以还不是很清楚之间有什么区别.
项目管理软件
dotProject:是一个基于LAMP的开源项目管理软件。最出名的项目管理软件
JIRA: 项目计划,任务安排,错误管理
Bugzilla:提交和管理bug,和eclipse集成,可以通过安装MyEclipse配置一下即可使用
BugFree借鉴微软公司软件研发理念、免费开放源代码、基于Web的精简版Bug管理
CVS:这个就不介绍了都在用.
SVN: SubVersion已逐渐超越CVS,更适应于JavaEE的项目。Apache用了它很久后,Sourceforge刚刚推出SVN的支持。
测试用例:主要JUnit单元测试,编写TestCase,Spring也对Junit做了很好的支持
后记:
以Spring为主的应用开发可选用的组件中间件真是眼花缭乱,所以针对不同的项目需求可以利用不同的开源产品解决,比如用Spring+Hibernate/ iBATIS或Spring+WebWork+Hibernate/ iBATIS或Spring+Struts+Hibernate/ iBATIS,合理的框架设计和代码复用设计对项目开发效率和程序性能有很大的提高,也有利于后期的维护.
现在主流的Java集成开发工具(IDE)Eclipse越来越流行,自从3.0版本以后Eclipse也逐渐稳定,现在Eclipse开发社区的人员越来越多版本更新速度也越来越快,目前最近的版本是3.2,Eclipse相比较一些其他的IDE如NetBeans/SunOne Studio,Jbuilder,IntelliJ IDEA主要的优点在于它是免费的、开放源代码的、质量很好,而且非常容易定制。Eclipse的最大的优势在于它的插件机制,除了很小的运行时内核外,Eclipse的所有的东西都是插件.现在插件超多眼花缭乱…正确有效的使用一些插件对开发速度很有提高.Eclipse的插件开发机制也比较简单运用Eclipse的基础库SWT,JFace,和插件开发环境PDE可以定制开发一些符合自己框架标准的代码生成框架,避免重复性代码的编写,把更多的精力放在如何构造合理的架构提高项目开发的速度方面.
首先介绍插件的配置方法一般来讲对于非安装文件,将插件的文件夹features, plugins放在Eclipse目录下的features,plugins下即可.如果不能使用可能是因为插件版本和当前Eclipse版本不匹配所致,下载合适的插件版本即可.目前3.1版本比较稳定在此平台应用的插件也比较多
下面主要介绍开发J2EE常用的插件的功能和简单快速的使用方法:
1:MyEclipse
很强大的Eclipse增强插件集合,集成了很多的J2EE的功能,比如Spring,Hibernate,Struts,EJB,JSTL等插件,也支持Tomcat,Jboss,Weblogic,WebSphere等主流容器的配置,还有DatabaseExplorer数据库管理插件,也集成了主流的bug管理系统bugzilla的错误提交,也提供了UML图的绘制插件,是一个比较全面的插件集合,官方更新速度很快,现在最新的版本是MyEclipse 5.0 GA支持Eclipse3.2不过是收费的,可以在网上找到破解码:-)
对于Tomcat开发为主流的常用的功能它的Reload机制,在window->Preferences->MyEclipse下Application Servers里Tomcat设置TomcatHome,然后通过快捷工具栏中的Run/Stop/Restart MyEclipse Application Servers启动服务,这样你的项目修改Java类不需要在重启Tomcat就可以实现改过的功能.
如果初时工程设为Web Projects可以通过Myeclipse为其添加Spring,Struts,Jsf,Jstl,Hibernate的库,设置方法为右键你的工程然后在菜单中选择Myeclipse在弹出菜单中Add相应的Capabilities.也可以选择为工程添加J2EE容器,如果上一步配置了Myeclipse Application Servers可以Add Tomcat Server,然后它会自动部署到Tomcat的webapps下.
DatabaseExplorer配置比较简单也比较好用,配置方法为:New一个Driver选择相应SqlServer2000,或Oracle的驱动jar,按照提示配置好数据库连接字符串就可以操作数据库了,也可以执行sql查询.
Myeclipse为开发便利为Eclipse开发了它的几个视图,可以通过菜单window->Open Perspective选择适当的视图,如MyEclipse Database Explorer,MyEclipse J2EE Development,MyEclipseHibernate和MyEclipse Web2.0
MyEclipse的缺点就在于对系统要求太高,开文件过多会死掉有时,所以一般1G内存跑起来比较爽,可以通过-Xmx属性对Eclipse使用的内存进行扩充.
对于UML方面说一下一般MyEclipse这个功能是个花瓶中看不中用,小的功能比较简单的UML图还可以够用,对于UML的正向或者逆向工程不支持,所以一般不用它.建议使用”Eclipse UML”插件
2. Lomboz
Lomboz也是一个功能强大的基于J2EE的集成插件其功能主要是JSP高亮显示,语法检查和编码助手,支持部署J2EE Web应用程序,利用Wizard创建Web应用和常用的代码生成,支持JSP的调试.
Lomboz的配置很简单将其放在Eclipse相应的文件夹即可.
Lomboz的优势在于可以调试Jsp, Lomboz的调试原理在于对要调试的jsp页面所产生的java代码进行调试,和java工程的调试过程一样,调试方法是打开Lomboz J2EE View 选择服务器,单击右键debug server,打开jsp所生成的java文件设置断点,在IE打开jsp就可以激活调试进行jsp调试,其实我感觉最好的调试方法是System.out.println,比较快捷.
3.SWT-Designer
看名字就知道是开发Java图形用户界面的插件,可以用于开发PDE插件或基于SWT的应用程序,非常强大的开发工具收费的不过比VE稳定很多,可以画界面,使用方法比较简单
在官方下载找个注册码激活就可以.
4.JSEclipse
这个对于WEB开发很有用可以对javascript进行高亮显示,语法检查和编码助手,特别是在Myeclipse或Lomboz下js开发时有时候没有编码助手,错误也没有提示,很不方面,JSEclipse可以进行编码提示和错误提示很实用!对于以后的ajax编码和富客户端开发调试效率会有很大的提高!
5.Properties Editor
编辑java的属性文件,并可以自动存盘为Unicode格式
6.XMLBuddy
XMLBuddy 主要用于编辑XML文件
7.Log4E
Log4E Log4j插件,提供各种和Log4j相关的任务,如为方法、类添加一个logger等,主要优点在于代码生成免去了每个类都要logger一下的麻烦
.使用方法比较简单..选中某个.java文件右键选择Log4J.
8.FreeMarker Eclipse Plugin / FreeMarker IDE
FreeMarker没有语法高亮看起来确实很不爽…调试起来比较痛苦这个插件用来在Eclipse中支持FreeMarker模板语言.它包括语法高亮显示,语法错误提示、视图等.
9.Veloedit
Velocity模版开发插件与FreeMarker类似
以上几个都是最常用的J2EE的插件,我都测试过很方便,在网上都有新版本下载,如果你的内存比较大可以用MyEclipse作为主要开发工具,辅助其他几个实用的插件,如果你机子配置不是很高.采用Lomboz加上其他几个插件也可.当然还有很多实用的插件这里没有介绍比如Profiler(性能跟踪、测量工具,能跟踪、测量BS程序) VSS Plugin for Eclipse (Microsoft Visual SourceSafe (VSS)),大家可以发掘介绍…
常用Eclipse快捷键介绍
主要总结了最最常用的Eclipse快捷键的功能
F3: 打开申明(Open declaration)。
Control-Shift-G: 在workspace中搜索引用(reference)。这个热键的作用和F3恰好相反.
Control-Shift-F: 根据代码风格设定重新格式化代码.
Control-Shift-O: 快速引入要import的类说明.
Control-O: 快速概要。通过这个快捷键,你可以迅速的跳到一个方法或者属性.
Control-/: 对一行注释或取消注释。对于多行也同样适用。
Spirng的InitializingBean为bean提供了定义初始化方法的方式。InitializingBean是一个接口,它仅仅包含一个方法:afterPropertiesSet()。
Bean实现这个接口,在afterPropertiesSet()中编写初始化代码:
package research.spring.beanfactory.ch4;
import org.springframework.beans.factory.InitializingBean;
public class LifeCycleBean implements InitializingBean{
public void afterPropertiesSet() throws Exception {
System.out.println("LifeCycleBean initializing...");
}
}
在xml配置文件中并不需要对bean进行特殊的配置:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean">
bean>
beans>
编写测试程序进行测试:
package research.spring.beanfactory.ch4;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LifeCycleTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch4/context.xml"));
factory.getBean("lifeBean");
}
}
运行上面的程序我们会看到:“LifeCycleBean initializing...”,这说明bean的afterPropertiesSet已经被Spring调用了。
Spring在设置完一个bean所有的合作者后,会检查bean是否实现了InitializingBean接口,如果实现就调用bean的afterPropertiesSet方法。
SHAPE \* MERGEFORMAT
查看bean是否实现InitializingBean接口
|
Spring虽然可以通过InitializingBean完成一个bean初始化后对这个bean的回调,但是这种方式要求bean实现 InitializingBean接口。一但bean实现了InitializingBean接口,那么这个bean的代码就和Spring耦合到一起了。通常情况下我不鼓励bean直接实现InitializingBean,可以使用Spring提供的init-method的功能来执行一个bean 子定义的初始化方法。
写一个java class,这个类不实现任何Spring的接口。定义一个没有参数的方法init()。
package research.spring.beanfactory.ch4;
public class LifeCycleBean{
public void init(){
System.out.println("LifeCycleBean.init...");
}
}
在Spring中配置这个bean:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean"
init-method="init">
bean>
beans>
当Spring实例化lifeBean时,你会在控制台上看到” LifeCycleBean.init...”。
Spring要求init-method是一个无参数的方法,如果init-method指定的方法中有参数,那么Spring将会抛出java.lang.NoSuchMethodException
init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。
init-method指定的方法可以是声明为抛出异常的,就像这样:
final protected void init() throws Exception{
System.out.println("init method...");
if(true) throw new Exception("init exception");
}
如果在init-method方法中抛出了异常,那么Spring将中止这个Bean的后续处理,并且抛出一个org.springframework.beans.factory.BeanCreationException异常。
InitializingBean和init-method可以一起使用,Spring会先处理InitializingBean再处理init-method。
org.springframework.beans.factory.support. AbstractAutowireCapableBeanFactory完成一个Bean初始化方法的调用工作。 AbstractAutowireCapableBeanFactory是XmlBeanFactory的超类,再 AbstractAutowireCapableBeanFactory的invokeInitMethods方法中实现调用一个Bean初始化方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java:
//……
//在一个bean的合作者设备完成后,执行一个bean的初始化方法。
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mergedBeanDefinition)
throws Throwable {
//判断bean是否实现了InitializingBean接口
if (bean instanceof InitializingBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//调用afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
}
//判断bean是否定义了init-method
if(mergedBeanDefinition!=null&&mergedBeanDefinition.getInitMethodName() != null) {
//调用invokeCustomInitMethod方法来执行init-method定义的方法
invokeCustomInitMethod(beanName, bean, mergedBeanDefinition.getInitMethodName());
}
}
//执行一个bean定义的init-method方法
protected void invokeCustomInitMethod(String beanName, Object bean, String initMethodName)
throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom init method '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
//使用方法名,反射Method对象
Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
if (initMethod == null) {
throw new NoSuchMethodException(
"Couldn't find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
//判断方法是否是public
if (!Modifier.isPublic(initMethod.getModifiers())) {
//设置accessible为true,可以访问private方法。
initMethod.setAccessible(true);
}
try {
//反射执行这个方法
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
//………..
通过分析上面的源代码我们可以看到,init-method是通过反射执行的,而afterPropertiesSet是直接执行的。所以 afterPropertiesSet的执行效率比init-method要高,不过init-method消除了bean对Spring依赖。在实际使用时我推荐使用init-method。
需要注意的是Spring总是先处理bean定义的InitializingBean,然后才处理init-method。如果在Spirng处理InitializingBean时出错,那么Spring将直接抛出异常,不会再继续处理init-method。
如果一个bean被定义为非单例的,那么afterPropertiesSet和init-method在bean的每一个实例被创建时都会执行。单例 bean的afterPropertiesSet和init-method只在bean第一次被实例时调用一次。大多数情况下 afterPropertiesSet和init-method都应用在单例的bean上。
Spring BeanFactory提供了类似pico container中自动装配组件依赖的对象的功能。自动装配能应用在每个组件上,可以为一些组件定义自动装配,而另一些组件则不使用。
使用”autowire”属性可以设置自动装配,autowire有五种模式:
默认属性,不进行自动装配。
通过bean的属性名称自动装配合作者。
SHAPE \* MERGEFORMAT
Spring用bean 中set方法名和BeanFactory中定义的合作者的名称做匹配,一但2者匹配,Sping就会把合作者进行注入。
可以使用id属性也可以使用name属性定义合作者的名称,这2个属性在Spring进行自动装配时没有区别。
当有多个名称相同的合作者在Spring中定义时,Srping在自动装配时选择最后一个定义的合作者注入。
SHAPE \* MERGEFORMAT
在多个合作者名称相同进行自动装配时,合作者的id属性并不会比name属性优先处理。无论怎样定义Spring总会把最后一个定义的合作者注入。
通过bean set方法中参数的类型和BeanFactory中定义合作者的类型做匹配,Spring会找到匹配的合作者进行注入。
SHAPE \* MERGEFORMAT
在byType自动装配模式中,Spring不关心合作者的名称,只关心合作者的类型是否满足条件。
类似上面介绍的byName的方式,在byType方式中,当具有相同名称并且有相同类型的多个合作者被找到时,Spring会注入最后一个定义的合作者。
SHAPE \* MERGEFORMAT
在byType装配时,如果有2个不同名称但是类型相同的合作者被找到,那么Spring会抛出一个依赖异常。
SHAPE \* MERGEFORMAT
抛出依赖异常,通知用户在byType方式中同样类型的Bean只能定义一个。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dao' defined in class path resource [research/spring/beanfactory/ch3/context.xml]: Unsatisfied dependency expressed through bean property 'database': There are 2 beans of type [class research.spring.beanfactory.ch3.Database] for autowire by type. There should have been 1 to be able to autowire property 'database' of bean 'dao'...
constructor
constructor其实时按byType的方式进行构造函数的注入。
SHAPE \* MERGEFORMAT
constructor装配方式不关心构造参数的顺序,无论构造函数参数的顺序如何Spring都会按类型匹配到正确的合作者进行注入。
在byType方式中,当没有找到类型相同的合作者时Spring什么都不会去做。但是在constructor方式中,当没有找到和Bean构造函数中参数类型相匹配的合作者时,Spring会抛出异常。
Spring在进行constructor方式的自动装配时,强制要求所有的构造函数中所有的合作者都必须存在。
在autodetect的方式中,Spring检查一个Bean内部是否有默认的构造函数。如果有默认的参数Spring就使用byType的方式进行自动装配。如果没有默认的构造函数Spring则使用constructor的方式进行自动装配。
如果一个Bean同时定义了默认构造函数和带参数的构造函数,Spring仍会使用byType的方式进行装配。
不管使用上述哪种装配方式,都可以在Bean中显示的定义合作者。显示定义的依赖关系优先级比自动装配高。
自动装配的功能可以和自动依赖检查一起使用。Spring会首先进行自动装配,然后在进行依赖检查。
自动装配提供了简化配置的可能性,但是我并不建议在项目中大量的使用自动装配,特别时byType方式。因为自动装配,尤其时byType方式,破坏了Bean和合作者之间显示的依赖关系,所有的依赖关系都时不明显的。在使用自动装配后我们的依赖关系需要到源代码中才能看到,这使得维护或文档化Bean的依赖关系变得很困难。
适当的使用自动装配比如byName方式的装配,是有一些好处的。比如我们在一些特定的范围里可以借助byName自动装配的功能来实现“
以惯例来代替配置”的框架。
自动依赖检查可以保证所有java bean中的属性(set方法)都在Spring中正确的配置。如果在一个java bean中定义了一个name属性,并且也setName方法。那么在开启自动依赖检查功能后,就必须在Spring中定义这个属性,否则Spring将抛出异常。
请看下面的例子:
Dao.java
包含一个setName方法。
package research.spring.beanfactory.ch3;
public class Dao {
private String name;
public void setName(String name) {
this.name = name;
}
}
context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao"> </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
我们在context.xml没有定义Dao的name属性。上面的配置,Spring可以正常的实例化Dao对象。
下面我们修改context.xml:
我们通过dependency-check=all,在Dao上增加了自动依赖检查的功能。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" dependency-check="all" > </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
当配置依赖检查时,Spring实例化Dao时会抛出一个异常:
Spring定义了4种依赖检查的策略:
不进行依赖检查。
只对简单属性和集合中的简单属性进行检查。不对依赖的对象检查。
只对为对象类型的属性进行检查。
对所有类型进行检查。
如果把上面例子里的context.xml改成这样:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" dependency-check="objects" > </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
Spring将不会抛出异常,因为objects只对依赖的对象进行检查。
dependency-check在Spring中又以下的限制:
- 不能对构造函数中的参数进行检查。
- 即使属性中有默认值,只要包含了set方法,那么dependency-check仍然需要检查Spring中是否配置了这个属性。
package research.spring.beanfactory.ch3;
public class Dao {
private Database database;
private String name="chenjie";
//dependency-check仍然会检查这个属性是否配置注入
public void setName(String name) {
this.name = name;
}
public void setDatabase(Database database) {
this.database = database;
}
}
即使Dao设置里name得默认值,但是只要有setName方法,dependency-check仍然会判断是否在配置文件中设置了setName对应的注入。
depend-on用来表示一个Bean的实例化依靠另一个Bean先实例化。如果在一个bean A上定义了depend-on B那么就表示:A 实例化前先实例化 B。
这种情况下,A可能根本不需要持有一个B对象。
比如说,你的DAO Bean实例化之前你必须要先实例化Database Bean,DAO Bean并不需要持有一个Database Bean的实例。因为DAO的使用是依赖Database启动的,如果Database Bean不启动,那么DAO即使实例化也是不可用的。这种情况DAO对Database的依赖是不直接的。
除了在DAO上使用构造函数注入Database Bean以外,Spring没有任何依赖注入的关系能够满足上面的情况。但是DAO也许根本不需要Database的实例被注入,因为DAO是通过JDBC访问数据库的,它不需要调用Database 上的任何方法和属性。
在这种情况下你可以使用depends-on来定义在DAO被实例化之前先去实例化Database。你可这样定义:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database">
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
通过定义depends-on=”database”可以控制Sping实例化dao的顺序。在任何时候Spring总会保证实例化DAO之前先实例Database。
通常depends-on常常应用在上面的场景中。如果DAO depend-on Database的同时需要得到Database的实例,那么使用构造函数注入是一个比较好的解决办法。因为构造函数注入的方式是要先实例化目标对象依赖的对象然后在实例化目标对象。关于构造函数的输入请参考另一篇文章
《Spring内核研究-set方法注入和构造函数注入》
DAO depend-on Database时,也可以在DAO上定义setDatabase方法来接收一个Database的实例。这样Sping会保证DAO创建前先创建Database实例,然后在把实例化DAO后调用DAO的setDatabase方法把刚才创建的Database的实例注入给DAO。前提条件时Database必须定义成单例的。否则Spring在DAO depend-on Database时会创建一个Database的实例,在DAO.setDatabase时又会创建Database另外的一个实例。这种情况可能不是你想要的,而且很可能会造成比较隐蔽的错误。
使用set方法注入depend-on的对象:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database ">
<property name="database">
<ref bean="database"></ref>
</property>
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
一般在depends-on一个对象并且又需要这个对象实例的情况下,我都建议你使用构造函数的注入方式替换depend-on。只有不能构造函数中添加依赖对象参数的情况下才使用上面例子里的方式。
可以同时使用depends-on和构造函数注入,如A depends-on B 并且 new A(B b)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database">
<constructor-arg>
<ref bean="database"></ref>
</constructor-arg>
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
然而这种做法是不合适的,因为在构在函数中注入依赖对象的方式可以包含depends-on的情况。也就时说new A(B b)包含了A depends-on B的所有情况。既然已经定义了new A(B b)就没有必要在定义A depends-on B。所以,new A(B b)可以替代A depends-on B。在A创建前必须创建B,而且A不需要使用B实例的情况下只能使用A depends-on B。
Spring允许Bean和Bean依赖的Bean(合作者)上同时定义depends-on。比如A depends-on B && B depends-on C && C depends-on D。下面这样定义是合法的。Sping实例化他们的顺序是D->C->B->A。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" depends-on="c" />
<bean name="c" class="research.spring.beanfactory.ch3.C" depends-on="D" />
<bean name="d" class="research.spring.beanfactory.ch3.D" />
</beans>
但是Spring不允许A depends-on B && B depends-on A的情况。看下面的例子,由于D又依赖回A,这种在依赖关系中形成了一个闭环,Spring将无法处理这种依赖关系。所以下面的这种定义是不合法的。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" depends-on="c" />
<bean name="c" class="research.spring.beanfactory.ch3.C" depends-on="D" />
<bean name="d" class="research.spring.beanfactory.ch3.D" depends-on="A"/>
</beans>
一个Bean可以同时depends-on多个对象如,A depends-on D,C,B。可以使用“,”或“;”定义多个depends-on的对象。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="d,c,b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" />
<bean name="c" class="research.spring.beanfactory.ch3.C" />
<bean name="d" class="research.spring.beanfactory.ch3.D" />
</beans>
上面的例子中A的实例化需要先实例化D,C,B。Spring会按照depend-on中定义的顺序来处理Bean。在这个例子里Spring实例化对象的顺利是D->C->B->A。虽然实例化对象的顺序和前面“A depends-on B && B depends-on C && C depends-on D”的情况一下,但是这里的意义是完全不同的。不能用“A depends-on D,C,B”代替“A depends-on B && B depends-on C && C depends-on D”。
depends-on是一个非常又用的功能,借助depends-on我们可以管理那些依赖关系不明显或者没有直接依赖关系的对象。
Spring专门设计了对工厂模式支持,你可以使用静态工厂方法来创建一个Bean,也可以使用实例工厂的方法来创建Bean。下面分别介绍这2种方法。
定义一个Bean使用自己类上的静态工厂方法来创建自己。
context.xml
factory-menthod定义了userDao Bean使用UserDao类的getInstance方法来创建自己的实例。userManager仍然通过lookup方法获得userDao。Lookup方法并不关心一个Bean的实例时怎样创建的,所以可以混合使用lookup方法和factory-menthod方法。
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-method="getInstance" / >
beans>
UserDao.java
增加一个getInstance方法来创建自己的实例。
package research.spring.beanfactory.ch2;
public class UserDao {
public static UserDao getInstance() {
return new UserDao("static factory method");
}
private String name = "";
public UserDao(String name) {
this.name = name;
}
public void create() {
System.out.println("create user from - " + name);
}
}
Test.java
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser();
}
}
运行Test.java,你会看到:
create user from - static factory method
这说明userDao使用它自己得静态工厂创建得。
静态工厂方法存在一些限制:
- 静态工厂方法上不能有参数,也不能在Spring种定义静态工厂方法的参数。
- 静态工厂方法只能是public的,不能是private或protected的。
- 静态工厂方法不能和构造函数注入一起使用。下面的定义时不能正常工作的:
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser();
}
}
定义一个Bean使用这个Bean的工厂对象上的工厂方法来创建自己。
我们定义一个UserDao的Factory来创建UserDao。
UserDaoFactory.java
package research.spring.beanfactory.ch2;
public class UserDaoFactory{
public UserDao getUserDao(){
return new UserDao("UserDaoFactory");
}
}
修改context.xml:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-bean="userDaoFactory" factory-method="getUserDao" >
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory">
bean>
beans>
再次运行Test.java你会看到:
create user from – UserDaoFactory
通过上面的配置Spring已经使用userDaoFactory实例的工厂方法来创建userDao了。
- factory-bean定义了工厂Bean
- factory-method定义了工厂方法
实例工厂和静态工厂一样都存在相同的限制:
- 静态工厂方法上不能有参数,也不能在Spring种定义静态工厂方法的参数。
- 静态工厂方法只能是public的,不能是private或protected的。
- 静态工厂方法不能和构造函数注入一起使用。
和静态工厂不同的是:
- 实例工厂方法不能是静态的,而静态工厂方法必须是静态的。
通过上面的例子我们看到Spring对工厂模式对了完整的支持。但是这里还是需要说明,如果使用IoC模式设计的系统一般情况下不需要为任何Bean做工厂类。在我的观点里,工厂模式仅仅是遗留系统,使用依赖注入模式可以取代工厂模式。Spring对工厂的支持仅仅是为了可以很好的集成遗留系统。
“Lookup方法”可以使Spring替换一个bean原有的,获取其它对象具体的方法,并自动返回在容器中的查找结果。
我们来看这个例子:
UserDao.java
在UserDao的构造函数中接受一个name参数,创建UserDao的对象会把自己的名字传递给userDao,这样userDao的create方法中就会把userDao的创建者打印出来。
package research.spring.beanfactory.ch2;
public class UserDao {
private String name="";
public UserDao(String name){
this.name=name;
}
public void create(){
System.out.println("create user from - "+name);
}
}
UserManager.java
在这段代码中UserManager依靠getUserDao方法来获取UserDao对象。由于在getUserDao方法里显示的声明了如何去实例一个UserDao,所以上面的代码不符合IoC模式的风格。虽然使用GetUserDao封装了UserDao的创建过程,但是UserManager和UserDao的关系仍然非常紧密。
package research.spring.beanfactory.ch2;
public class UserManager {
public UserDao getUserDao() {
return new UserDao("UserManager.getUserDao()");
}
public void createUser() {
UserDao dao = getUserDao(); //通过getUserDao获得userDao
dao.create();
}
}
LookupMethodTest.java
通过BeanFactory获得UserManager,并调用createUser方法。
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new
XmlBeanFactory(new ClassPathResource("research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser(); //create a User
}
}
context.xml
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
bean>
<bean name="userDao class="research.spring.beanfactory.ch2.UserDao" >
bean>
beans>
运行LookupMethodTest你会看到屏幕输入” create user from - UserManager.getUserDao()”。
由于是遗留系统,所以我们不能修改UserManager。现在我希望让这个UserManager依赖的Dao对象由spring管理,而不修改原有的代码。
在这个场景中我们就可以利用Spring提供的“Lookup方法”来替换原有的getUserDao方法,实现自动获取userDao的功能。修改context.xml:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
<constructor-arg>
<value>lookup methodvalue>
constructor-arg>
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory">
bean>
beans>
再次运行LookupMethodTest你会看到不同的输出结果“create user from - lookup method”。字符串“lookup method”是通过构造函数注入给userDao的。原来的userManager.java并没有作任何修改,仍然是通过UserDao dao = getUserDao();来获得userDao的。这说明Spring已经替换了原有的getUserDao方法的实现,当执行getUserDao时Spring会在容器中寻找指定的Bean,并返回这个Bean。
通过这种机制我们可以在不修改原系统代码的情况下,可以轻易的把UserDao换成别的类型相容的对象而不会影响原系统。Spring是使用CGLIB在字节码级别动态实现出userManager的子类,并重写getUserDao方法的方式来实现这个神奇的功能的。
修改LookupMethodTest.java:
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
System.out.println(manager.toString()); //打印userManager的信息
manager.createUser(); //create a User
}
}
我们在获取UserManager的实例后打印出这个实例的信息,再次运行LookupMethodTest你会看到:
注意manager.toString()打印出了:
这个是CGLIG动态生成的类,而不是原来的UserManager的实例。所以请记住在任何时候只要定义了一个Bean的Lookup方法,那么这个Bean的实例将是一个CGLIB动态生成的实例而不是原来类的实例。
Spring允许在一个Bean中定义多个Lookup方法。
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
<lookup-method name="getOtherDao" bean="otherDao" />
bean>
上面的做法是合法的,并且可以正常工作。
虽然Spring会用CGLIB动态生成一个带有Lookup方法bean的子类,但是这并不影响Spring完成其它的功能。Sping还是允许Lookup方法和setXXX、构造函数注入以及后面我们将介绍的自动依赖检查和自动装配的功能同时使用。
Spring还允许Lookup方法中定义的方法带有参数,但是Sping不会处理这些参数。
修改UserManager:
package research.spring.beanfactory.ch2;
public class UserManager {
private UserDao dao;
public void setDao(UserDao dao) {
this.dao = dao;
}
public UserDao getUserDao(String daoName) {
return new UserDao("UserManager.getUserDao()");
}
public void createUser() {
UserDao dao = getUserDao(“userDao”); //通过getUserDao获得userDao
dao.create();
}
}
虽然方法上由参数,但是上面的代码可以正常工作。Spring不会处理这些参数。
Spring对Lookup方法也存在一些限制:
- 方法不能是private的,但可以是protected的。
- 方法不能是静态的。
有一个比较有趣的用法,就是在抽象类上定义Lookup方法。你一定记得经典的工厂模式吧。定义一个抽象工厂,然后为每一类具体产品实现一个具体产品的工厂。
一个抽象工厂:
package research.spring.beanfactory.ch2;
public abstract class Factory {
public abstract UserDao getProduct();
}
具体一类产品的工厂:
package research.spring.beanfactory.ch2;
public class UserDaoFactory extends Factory{
public UserDao getProduct(){
return new UserDao("UserDaoFactory");
}
}
用户可以通过:
new UserDaoFactory().getProduce();
来获取具体的UserDao产品。
但是如果有很多产品就需要做出实现出很多工厂如,DocumentDaoFactory、GroupDaoFactory等等,这样系统中会出现大量的工厂。工厂的泛滥并不能说明系统的设计是合理的。
既然Spring可以在抽象类上使用Lookup方法,那么我们就可以不同实现真的去实现那么多的子类了。我们可以在抽象类上直接定义Lookup方法和目标对象。用户直接通过抽象类来获得需要的产品对象。看下面这个例子:
Factory.ava
package research.spring.beanfactory.ch2;
public abstract class Factory {
public abstract Object getProduct();
}
context.xml
如果指定userDaoFactory的类为一个抽象类,并且再这个bean里定义了Lookup方法,那么Spring会自动生成这个抽象类的子类实现。
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
<constructor-arg>
<value>lookup methodvalue>
constructor-arg>
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="userDao" />
bean>
beans>
Test.java
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
//获得抽象工厂
Factory abstractFactory=(Factory) factory.getBean("userDaoFactory");
UserDao userDao=(UserDao) abstractFactory.getProduct();
System.out.println(userDao.toString());
userDao.create();
}
}
运行Test你会看到:
research.spring.beanfactory.ch2.UserManager$$EnhancerByCGLIB$$cc2f8f1c@12c7568
create user from - lookup method
对,这个结果和上面的例子是完全一样的。UserDao并没有改变,我们通过抽象的Factory获得了具体的UserDao的实例。这样即使系统中很多的具体产品我们也不需要实现每类产品的工厂类了。只需要在系统中配置多个抽象工厂,并且配置每个工厂的singlton为false,在用户使用时使用不同抽象工厂的实例就可以了。
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="userDao" />
bean>
<bean name="documentDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="documentDao" />
bean>
Spring不关心抽象类中的定义的lookup方法是否时抽象的,Spring都会重写这个方法。
既然Sping可以动态实现抽象类的子类那么,它能不能动态创建出实现一个接口的类呢。答案时肯定的。上面的例子可以直接把Factory变成一个接口,仍然可以正常工作。
这里需要注意的是,只要在一个Bean上明确的定义了Lookup方法,Spring才会使用CGLIB来做原对象的字节码代理。如果一个没有定义Lookup方法的抽象类或接口是不能直接被Spring实例的。
本文介绍了Lookup方法的使用和工作原理,希望读者能够对Lookup方法有了比较深入的了解。虽然我的例子可以简化工厂模式,但是我并不鼓励大家在实际系统中这样做。因为我始终认为“工厂模式”只要在遗留系统中才会碰到。使用IoC模式基本上可以替代所有的对象创建模式。本章的例子只是为了说明Lookup方法如何使用,和Lookup方法的一些特殊情况。Lookup方法一般只在处理遗留代码时使用。