Eclipse 快捷键大全
类别 命令 键序列 说明
C/C++ Source Add Block Comment Ctrl+Shift+/ C/C++ Editor
C/C++ Source Add Include Ctrl+Shift+N C/C++ Editor
C/C++ Source Comment Ctrl+/ C/C++ Editor
C/C++ Source Find Declaration Ctrl+G C/C++ Editor
C/C++ Source Find References Ctrl+Shift+G C/C++ Editor
C/C++ Source Format Ctrl+Shift+F C/C++ Editor
C/C++ Source Go to Matching Bracket Ctrl+Shift+P C/C++ Editor
C/C++ Source Go to next C/C++ member Ctrl+Shift+向下键 C/C++ Editor
C/C++ Source Go to previous C/C++ member Ctrl+Shift+向上键 C/C++ Editor
C/C++ Source Open Declaration F3 C/C++ Editor
C/C++ Source Open Definition Ctrl+F3 C/C++ Editor
C/C++ Source Open Type Ctrl+Shift+T C/C++ Editor
C/C++ Source Remove Block Comment Ctrl+Shift+\ C/C++ Editor
C/C++ Source Show outline Ctrl+O C/C++ Editor
C/C++ Source Uncomment Ctrl+\ C/C++ Editor
Makefile Source Comment Ctrl+/ Makefile Editor
Makefile Source Open declaration F3 Makefile Editor
Makefile Source Uncomment Ctrl+\ Makefile Editor
Refactor - C/C++ Redo - Refactoring Alt+Shift+Y C/C++ Editor
Refactor - C/C++ Rename - Refactoring Alt+Shift+R C/C++ Editor
Refactor - C/C++ Undo - Refactoring Alt+Shift+Z C/C++ Editor
View Zoom In Ctrl+= 在窗口中
View Zoom Out Ctrl+- 在窗口中
搜索 工作空间中的声明 Ctrl+G 在窗口中
搜索 工作空间中的引用 Ctrl+Shift+G 在窗口中
搜索 打开“搜索”对话框 Ctrl+H 在窗口中
搜索 显示“文件中的出现位置”快速菜单 Ctrl+Shift+U 在窗口中
文件 “新建”菜单 Alt+Shift+N 在窗口中
文件 保存 Ctrl+S 在窗口中
文件 全部保存 Ctrl+Shift+S 在窗口中
文件 全部关闭 Ctrl+Shift+F4 在窗口中
文件 全部关闭 Ctrl+Shift+W 在窗口中
文件 关闭 Ctrl+F4 在窗口中
文件 关闭 Ctrl+W 在窗口中
文件 刷新 F5 在窗口中
文件 属性 Alt+Enter 在窗口中
文件 打印 Ctrl+P 在窗口中
文件 新建 Ctrl+N 在窗口中
文件 重命名 F2 在窗口中
文本编辑 上一个词语 Ctrl+左箭头 编辑文本
文本编辑 上滚行 Ctrl+向上键 编辑文本
文本编辑 下一个词语 Ctrl+右箭头 编辑文本
文本编辑 下滚行 Ctrl+向下键 编辑文本
文本编辑 全部展开 Ctrl+Numpad_Multiply 编辑文本
文本编辑 切换折叠 Ctrl+Numpad_Divide 编辑文本
文本编辑 删除上一个词语 Ctrl+Backspace 编辑文本
文本编辑 删除下一个词语 Ctrl+Delete 编辑文本
文本编辑 删除至行末 Ctrl+Shift+Delete 编辑文本
文本编辑 删除行 Ctrl+D 编辑文本
文本编辑 在当前行上面插入行 Ctrl+Shift+Enter 编辑文本
文本编辑 在当前行下面插入行 Shift+Enter 编辑文本
文本编辑 复制行 Ctrl+Alt+向下键 编辑文本
文本编辑 将行上移 Alt+向上键 编辑文本
文本编辑 将行下移 Alt+向下键 编辑文本
文本编辑 展开 Ctrl+Numpad_Add 编辑文本
文本编辑 折叠 Ctrl+Numpad_Subtract 编辑文本
文本编辑 改写切换 Insert 编辑文本
文本编辑 更改为大写 Ctrl+Shift+X 编辑文本
文本编辑 更改为小写 Ctrl+Shift+Y 编辑文本
文本编辑 选择上一个词语 Ctrl+Shift+左箭头 编辑文本
文本编辑 选择下一个词语 Ctrl+Shift+右箭头 编辑文本
文本编辑 重复行 Ctrl+Alt+向上键 编辑文本
查看 Java 包资源管理器 Alt+Shift+Q,P 在窗口中
查看 Java 声明 Alt+Shift+Q,D 在窗口中
查看 Java 类型层次结构 Alt+Shift+Q,T 在窗口中
查看 Javadoc Alt+Shift+Q,J 在窗口中
查看 变量 Alt+Shift+Q,V 在窗口中
查看 同步 Alt+Shift+Q,Y 在窗口中
查看 备忘单 Alt+Shift+Q,H 在窗口中
查看 控制台 Alt+Shift+Q,C 在窗口中
查看 搜索 Alt+Shift+Q,S 在窗口中
查看 断点 Alt+Shift+Q,B 在窗口中
查看 显示视图 (查看: 大纲) Alt+Shift+Q,O 在窗口中
查看 显示视图 (查看: 问题) Alt+Shift+Q,X 在窗口中
浏览 &Quick Cross References Alt+Shift+P 编辑 Java 源代码
浏览 Open AspectJ Type Alt+Shift+A 在窗口中
浏览 Open AspectJ Type in Hierarchy Alt+Shift+H 在窗口中
浏览 “显示位置”菜单 Alt+Shift+W 在窗口中
浏览 上一个编辑位置 Ctrl+Q 在窗口中
浏览 下一页 Ctrl+. 在窗口中
浏览 前一页 Ctrl+, 在窗口中
浏览 前移历史记录 Alt+右箭头 在窗口中
浏览 后退历史记录 Alt+左箭头 在窗口中
浏览 在层次结构中打开类型 Ctrl+Shift+H 在窗口中
浏览 快速大纲 Ctrl+O 编辑 Java 源代码
浏览 快速层次结构 Ctrl+T 编辑 Java 源代码
浏览 打开声明 F3 在窗口中
浏览 打开外部 Javadoc Shift+F2 在窗口中
浏览 打开类型 Ctrl+Shift+T 在窗口中
浏览 打开类型层次结构 F4 在窗口中
浏览 打开结构 Ctrl+F3 编辑 Java 源代码
浏览 打开调用层次结构 Ctrl+Alt+H 在窗口中
浏览 打开资源 Ctrl+Shift+R 在窗口中
浏览 转至上一个成员 Ctrl+Shift+向上键 编辑 Java 源代码
浏览 转至下一个成员 Ctrl+Shift+向下键 编辑 Java 源代码
浏览 转至匹配的方括号 Ctrl+Shift+P 编辑 Java 源代码
浏览 转至行 Ctrl+L 编辑文本
源代码 切换 Ant 标记出现 Alt+Shift+O 编辑 Ant 构建文件
源代码 切换标记出现 Alt+Shift+O 编辑 Java 源代码
源代码 切换注释 Ctrl+/ 编辑 Java 源代码
源代码 切换注释 Ctrl+7 编辑 Java 源代码
源代码 切换注释 Ctrl+Shift+C 编辑 Java 源代码
源代码 在文件中重命名 Alt+Shift+R 编辑 Ant 构建文件
源代码 快速辅助 - 在文件中重命名 Ctrl+2,R 编辑 Java 源代码
源代码 快速辅助 - 指定给字段 Ctrl+2,F 编辑 Java 源代码
源代码 快速辅助 - 指定给局部变量 Ctrl+2,L 编辑 Java 源代码
源代码 打开外部文档 Shift+F2 编辑 Ant 构建文件
源代码 显示工具提示描述 F2 编辑 Ant 构建文件
源代码 显示源代码快速菜单 Alt+Shift+S 在窗口中
源代码 格式 Ctrl+Shift+F 编辑 Ant 构建文件
源代码 格式化 Ctrl+Shift+F 编辑 Java 源代码
源代码 添加 Javadoc 注释 Alt+Shift+J 在窗口中
源代码 添加块注释 Ctrl+Shift+/ 编辑 Java 源代码
源代码 添加导入 Ctrl+Shift+M 编辑 Java 源代码
源代码 组织导入 Ctrl+Shift+O 在窗口中
源代码 缩进行 Ctrl+I 编辑 Java 源代码
源代码 除去出现注释 Alt+Shift+U 编辑 Java 源代码
源代码 除去块注释 Ctrl+Shift+\ 编辑 Java 源代码
窗口 上一个编辑器 Ctrl+Shift+F6 在窗口中
窗口 上一个视图 Ctrl+Shift+F7 在窗口中
窗口 上一个透视图 Ctrl+Shift+F8 在窗口中
窗口 下一个编辑器 Ctrl+F6 在窗口中
窗口 下一个视图 Ctrl+F7 在窗口中
窗口 下一个透视图 Ctrl+F8 在窗口中
窗口 切换至编辑器 Ctrl+Shift+E 在窗口中
窗口 将活动视图或编辑器最大化 Ctrl+M 在窗口中
窗口 打开编辑器下拉列表 Ctrl+E 在窗口中
窗口 显示标尺上下文菜单 Ctrl+F10 编辑文本
窗口 显示系统菜单 Alt+- 在窗口中
窗口 显示视图菜单 Ctrl+F10 在窗口中
窗口 显示键辅助 Ctrl+Shift+L 在对话框和窗口中
窗口 激活编辑器 F12 在窗口中
编辑 Add Block Comment Ctrl+Shift+/ Editing in Structured Text Editors
编辑 Format Active Elements Ctrl+I Editing in Structured Text Editors
编辑 Format Document Ctrl+Shift+F Editing in Structured Text Editors
编辑 Move Alt+Shift+V Editing JSP Source
编辑 Occurrences in File Ctrl+Shift+A Editing in Structured Text Editors
编辑 Open Selection F3 Editing in Structured Text Editors
编辑 Quick Fix Ctrl+1 Editing in Structured Text Editors
编辑 Remove Block Comment Ctrl+Shift+\ Editing in Structured Text Editors
编辑 Rename Alt+Shift+R Editing JSP Source
编辑 Rename XSD element Alt+Shift+R Editing XSD context
编辑 Restore Last Selection Alt+Shift+向下键 Editing in Structured Text Editors
编辑 Select Enclosing Element Alt+Shift+向上键 Editing in Structured Text Editors
编辑 Select Next Element Alt+Shift+右箭头 Editing in Structured Text Editors
编辑 Select Previous Element Alt+Shift+左箭头 Editing in Structured Text Editors
编辑 Show Tooltip Description F2 Editing in Structured Text Editors
编辑 Toggle Comment Ctrl+Shift+C Editing in Structured Text Editors
编辑 “快速差别”开关 Ctrl+Shift+Q 编辑文本
编辑 上下文信息 Alt+? 在窗口中
编辑 上下文信息 Alt+Shift+? 在窗口中
编辑 内容辅助 Alt+/ 在对话框和窗口中
编辑 切换插入方式 Ctrl+Shift+Insert 编辑文本
编辑 删除 Delete 在窗口中
编辑 剪切 Ctrl+X 在对话框和窗口中
编辑 剪切 Shift+Delete 在对话框和窗口中
编辑 增量查找 Ctrl+J 编辑文本
编辑 增量逆向查找 Ctrl+Shift+J 编辑文本
编辑 复制 Ctrl+C 在对话框和窗口中
编辑 复制 Ctrl+Insert 在对话框和窗口中
编辑 复原上一个选择 Alt+Shift+向下键 编辑 Java 源代码
编辑 快速修正 Ctrl+1 在窗口中
编辑 撤消 Ctrl+Z 在窗口中
编辑 文字补全 Ctrl+Alt+/ 编辑文本
编辑 显示工具提示描述 F2 编辑 Java 源代码
编辑 查找上一个 Ctrl+Shift+K 编辑文本
编辑 查找下一个 Ctrl+K 编辑文本
编辑 查找并替换 Ctrl+F 在窗口中
编辑 粘贴 Ctrl+V 在对话框和窗口中
编辑 粘贴 Shift+Insert 在对话框和窗口中
编辑 选择上一个元素 Alt+Shift+左箭头 编辑 Java 源代码
编辑 选择下一个元素 Alt+Shift+右箭头 编辑 Java 源代码
编辑 选择全部 Ctrl+A 在对话框和窗口中
编辑 选择外层元素 Alt+Shift+向上键 编辑 Java 源代码
编辑 重做 Ctrl+Y 在窗口中
运行/调试 Debug AspectJ/Java Application Alt+Shift+D,C 在窗口中
运行/调试 Debug on Server Alt+Shift+D,R 在窗口中
运行/调试 EOF Ctrl+Z 在控制台中
运行/调试 Profile on Server Alt+Shift+P,R 在窗口中
运行/调试 Run AspectJ/Java Application Alt+Shift+X,C 在窗口中
运行/调试 Run on Server Alt+Shift+X,R 在窗口中
运行/调试 切换单步执行过滤器 Shift+F5 在窗口中
运行/调试 切换行断点 Ctrl+Shift+B 在窗口中
运行/调试 单步跳入 F5 调试
运行/调试 单步跳入选择的内容 Ctrl+F5 调试
运行/调试 单步跳过 F6 调试
运行/调试 单步返回 F7 调试
运行/调试 执行 Ctrl+U 在窗口中
运行/调试 显示 Ctrl+Shift+D 在对话框和窗口中
运行/调试 检查 Ctrl+Shift+I 在对话框和窗口中
运行/调试 继续 F8 调试
运行/调试 调试 Ant 构建 Alt+Shift+D,Q 在窗口中
运行/调试 调试 Eclipse 应用程序 Alt+Shift+D,E 在窗口中
运行/调试 调试 JUnit 插件测试 Alt+Shift+D,P 在窗口中
运行/调试 调试 JUnit 测试 Alt+Shift+D,T 在窗口中
运行/调试 调试 Java Applet Alt+Shift+D,A 在窗口中
运行/调试 调试 Java 应用程序 Alt+Shift+D,J 在窗口中
运行/调试 调试 SWT 应用程序 Alt+Shift+D,S 在窗口中
运行/调试 调试上次启动 F11 在窗口中
运行/调试 运行 Ant 构建 Alt+Shift+X,Q 在窗口中
运行/调试 运行 Eclipse 应用程序 Alt+Shift+X,E 在窗口中
运行/调试 运行 JUnit 插件测试 Alt+Shift+X,P 在窗口中
运行/调试 运行 JUnit 测试 Alt+Shift+X,T 在窗口中
运行/调试 运行 Java Applet Alt+Shift+X,A 在窗口中
运行/调试 运行 Java 应用程序 Alt+Shift+X,J 在窗口中
运行/调试 运行 SWT 应用程序 Alt+Shift+X,S 在窗口中
运行/调试 运行上次启动 Ctrl+F11 在窗口中
运行/调试 运行至行 Ctrl+R 调试
重构 - Java 内联 Alt+Shift+I 在窗口中
重构 - Java 将局部变量转换为字段 Alt+Shift+F 编辑 Java 源代码
重构 - Java 抽取局部变量 Alt+Shift+L 在窗口中
重构 - Java 抽取方法 Alt+Shift+M 在窗口中
重构 - Java 撤销 - 重构 Alt+Shift+Z 在窗口中
重构 - Java 显示重构快速菜单 Alt+Shift+T 在窗口中
重构 - Java 更改方法特征符 Alt+Shift+C 在窗口中
重构 - Java 移动 - 重构 Alt+Shift+V 在窗口中
重构 - Java 重做 - 重构 Alt+Shift+Y 在窗口中
重构 - Java 重命名 - 重构 Alt+Shift+R 在窗口中
项目 全部构建 Ctrl+B 在窗口中
posted @
2006-04-05 13:33 阿成 阅读(218) |
评论 (0) |
编辑 收藏
Spring学习笔记(一)依赖注入
依赖注入——是Spring最灵魂的设计思想,有人也叫做控制反转。
1、不管是依赖注入(DI:Dependency Injection)还是控制反转(IoC:Inversion of Control)它的思想是:控制权由应用代码中转到了外部
容器,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
2、依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,带来灵活性。
3、举个例子来说明这个问题。
我曾看到夏昕的《Spring 开发指南》,上面为说明这个思想,举了电脑、USB硬盘和U盘的例子,感觉还是不太贴切,今天想了个自认为比较好
理解的例子:
有两种变形金刚的玩具,
一种是固定的,我把它比作原来那种控制权由应用代码写死的程序
一种可以拆开重新装配的,我把它比作用了依赖注入设计思想的程序
变形金刚的各个部件就象程序的各个组件。
变形金刚的厂家,相对它来说是内部的。就象程序的代码
变形金刚的玩家,相对它来说是外部的。就象程序的容器
大家试想一下,固定变形金刚的控制权是不是厂家决定的,外部无能为力
而可拆卸的变形金刚的控制权转移到了外部的玩家,玩家在玩之前可以重新决定各个组件的连接关系
而这种组件的连接图是不是也很像依赖注入思想里的配置文件。
大家再从这个例子分析一下依赖注入的目标。
posted @
2006-03-23 13:46 阿成 阅读(305) |
评论 (0) |
编辑 收藏
Spring使用BeanFactory模式来管理Bean,但Spring中提到的Bean不是标准的意义上的JavaBean(仅包含一个默认的构造函数,在属性后面定义相对应的setter和getter方法),而是任何你想让它管理的类,比如连接池、甚至BeanFactory本身。
一)Bean的设计常用下面几种模式
1、标准Bean:
使用默认的构造函数和基于setter、getter方法的依赖注射
Bean类代码:
java代码:
|
public
class ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; publicvoid setBeanOne(BeanOne beanOne){ this.beanOne = beanOne; } publicvoid setBeanTwo(BeanTwo beanTwo){ this.beanTwo = beanTwo; } publicvoid setCount(int count){ this.count = count; } }
|
在配置文件中定义:
java代码:
|
<bean id="exampleBean" class="examples.ExampleBean"> <property name="beanOne"><ref bean="bean1"/></property> <property name="beanTwo"><ref bean="bean2"/></property> <property name="count"><value>1</value></property> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
2、构造函数模式
自定义的构造函数,基于构造函数参数的依赖注射
Bean类代码:
java代码:
|
public
class ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; public ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ this.beanOne = beanOne; this.beanTwo = beanTwo; this.count = count; } }
|
在配置文件中定义:
java代码:
|
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、静态工厂方法模式
静态工厂方法必须是static的,基于方法参数的依赖注射。
Bean类代码:
java代码:
|
public
class ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; //构造函数私有 private ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ this.beanOne = beanOne; this.beanTwo = beanTwo; this.count = count; } //对外提供静态的方法 publicstatic ExampleBean createInstance(BeanOne beanOne, BeanTwo beanTwo, int count){ ExampleBean eb = new ExampleBean(beanOne,beanTwo,count); return eb; } }
|
在配置文件中定义:
java代码:
|
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、实例工厂方法模式
调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean,基于方法参数的依赖注射
该模式没有Bean类;
在配置文件中定义:
java代码:
|
<bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
<bean id="myFactoryBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
二)Bean其它参数的配置
一个常用Bean的配置参数和解释
<bean id="" ——标志符,用它引用唯一的Bean
class="" ——该Bean对应的类,前面说到实例工厂方法模式创建的Bean没有类
singleton="" ——值为true或false,标识该Bean是否为单实例模式?如果为false则对这个bean
的每次请求都会创建一个新的bean实例
init-method="" ——向应用层返回引用前执行的初始化方法
destroy-method="" ——该Bean的销毁方法
depends-on=""> ——在Bean加载前,首先加载的指定资源
....
</bean>
三)property(或constructor-arg元素)的配置
1、用字符串形式指定常见类型的属性或参数的value值,JavaBean的PropertyEditor负责类型转化如:
java代码:
|
<property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/mydb</value> </property>
|
2、注意null和""(空串)的区别,如:
java代码:
|
<property name="email"><value></value></property> <property name="email"><null/></property>
|
3、list、set、map、以及 props 元素用来定义和设置Java对应类型List、Set、Map、和 Properties ,如:
java代码:
|
<property name="school"> <props> <prop key="school01">The xi'an technology university</prop> <prop key="school02">The BeiJing university</prop> </props> </property>
<property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource"/> </list> </property>
<property name="someMap"> <map> <entry key="001"> <value>just some string</value> </entry> <entry key="yup a ref"> <ref bean="myDataSource"/> </entry> </map> </property> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource"/> </set> </property>
|
4、内部Bean和ref元素引用容器管理的其他bean
一个内部Bean的例子:
java代码:
|
<bean id="dep" class="com.bean.Conpany"> <property name="manager"> <bean class="com.bean.Person"> <property name="name"><value>Tony</value></property> <property name="age"><value>51</value></property> </bean> </property> </bean>
|
ref元素引用的例子:
java代码:
|
<bean id="person_manger" class="com.bean.Person"> <property name="name"><value>Tony</value></property> <property name="age"><value>51</value></property> </bean>
<bean id="dep" class="com.bean.Conpany"> <property name="manager"> <idref bean="person_manager"/> </property> </bean>
|
注:元素引用可以是下面三种权限:
1)<idref bean="person_manager"/>
引用的Bean可以在同一个BeanFactory/ApplicationContext(无论是否在同一个XML文件中)中,也可以在父BeanFactory/ApplicationContext中
2)<idref local="person_manager"/>
引用的bean在同一个XML文件中
3)<idref parent="person_manager"/>
引用的bean必须在当前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中.
时间: 2006-3-23 12:58:26 标题: Spring学习笔记(二)Bean | |
|
Spring使用BeanFactory模式来管理Bean,但Spring中提到的Bean不是标准的意义上的JavaBean(仅包含一个默认的构造函数,在属性后面定义相对应的setter和getter方法),而是任何你想让它管理的类,比如连接池、甚至BeanFactory本身。
一)Bean的设计常用下面几种模式
1、标准Bean:
使用默认的构造函数和基于setter、getter方法的依赖注射
Bean类代码:
java代码: | publicclass ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; publicvoid setBeanOne(BeanOne beanOne){ this.beanOne = beanOne; } publicvoid setBeanTwo(BeanTwo beanTwo){ this.beanTwo = beanTwo; } publicvoid setCount(int count){ this.count = count; } }
|
在配置文件中定义:
java代码: | <bean id="exampleBean" class="examples.ExampleBean"> <property name="beanOne"><ref bean="bean1"/></property> <property name="beanTwo"><ref bean="bean2"/></property> <property name="count"><value>1</value></property> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
2、构造函数模式
自定义的构造函数,基于构造函数参数的依赖注射
Bean类代码:
java代码: | publicclass ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; public ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ this.beanOne = beanOne; this.beanTwo = beanTwo; this.count = count; } }
|
在配置文件中定义:
java代码: |
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、静态工厂方法模式
静态工厂方法必须是static的,基于方法参数的依赖注射。
Bean类代码:
java代码: | publicclass ExampleBean { private BeanOne beanOne; private BeanTwo beanTwo; privateint count; //构造函数私有 private ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ this.beanOne = beanOne; this.beanTwo = beanTwo; this.count = count; } //对外提供静态的方法 publicstatic ExampleBean createInstance(BeanOne beanOne, BeanTwo beanTwo, int count){ ExampleBean eb = new ExampleBean(beanOne,beanTwo,count); return eb; } }
|
在配置文件中定义:
java代码: | <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、实例工厂方法模式
调用一个已存在的bean(这个bean应该是工厂类型)的工厂方法来创建新的bean,基于方法参数的依赖注射
该模式没有Bean类;
在配置文件中定义:
java代码: | <bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
<bean id="myFactoryBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg><ref bean="bean1"/></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
二)Bean其它参数的配置
一个常用Bean的配置参数和解释
<bean id="" ——标志符,用它引用唯一的Bean class="" ——该Bean对应的类,前面说到实例工厂方法模式创建的Bean没有类 singleton="" ——值为true或false,标识该Bean是否为单实例模式?如果为false则对这个bean 的每次请求都会创建一个新的bean实例 init-method="" ——向应用层返回引用前执行的初始化方法 destroy-method="" ——该Bean的销毁方法 depends-on=""> ——在Bean加载前,首先加载的指定资源 .... </bean>
三)property(或constructor-arg元素)的配置
1、用字符串形式指定常见类型的属性或参数的value值,JavaBean的PropertyEditor负责类型转化如:
java代码: | <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/mydb</value> </property>
|
2、注意null和""(空串)的区别,如:
java代码: |
<property name="email"><value></value></property> <property name="email"><null/></property>
|
3、list、set、map、以及 props 元素用来定义和设置Java对应类型List、Set、Map、和 Properties ,如:
java代码: |
<property name="school"> <props> <prop key="school01">The xi'an technology university</prop> <prop key="school02">The BeiJing university</prop> </props> </property>
<property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource"/> </list> </property>
<property name="someMap"> <map> <entry key="001"> <value>just some string</value> </entry> <entry key="yup a ref"> <ref bean="myDataSource"/> </entry> </map> </property> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource"/> </set> </property>
|
|
4、内部Bean和ref元素引用容器管理的其他bean
一个内部Bean的例子:
java代码:
|
<bean id="dep" class="com.bean.Conpany"> <property name="manager"> <bean class="com.bean.Person"> <property name="name"><value>Tony</value></property> <property name="age"><value>51</value></property> </bean> </property> </bean>
|
ref元素引用的例子:
java代码:
|
<bean id="person_manger" class="com.bean.Person"> <property name="name"><value>Tony</value></property> <property name="age"><value>51</value></property> </bean>
<bean id="dep" class="com.bean.Conpany"> <property name="manager"> <idref bean="person_manager"/> </property> </bean>
|
注:元素引用可以是下面三种权限:
1)<idref bean="person_manager"/>
引用的Bean可以在同一个BeanFactory/ApplicationContext(无论是否在同一个XML文件中)中,也可以在父BeanFactory/ApplicationContext中
2)<idref local="person_manager"/>
引用的bean在同一个XML文件中
3)<idref parent="person_manager"/>
引用的bean必须在当前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中
引用注明出处:http://spaces.msn.com/pococoon/blog/cns!D25B6032F7AD1992!193.entry
posted @
2006-03-23 13:42 阿成 阅读(290) |
评论 (0) |
编辑 收藏
Hibernate一共包括了23个jar包,令人眼花缭乱。本文将详细讲解Hibernate每个jar包的作用,便于你在应用中根据自己的需要进行取舍。
下载Hibernate,例如2.0.3稳定版本,解压缩,可以看到一个hibernate2.jar和lib目录下有22个jar包:
hibernate2.jar:
Hibernate的库,没有什么可说的,必须使用的jar包
cglib-asm.jar:
CGLIB库,Hibernate用它来实现PO字节码的动态生成,非常核心的库,必须使用的jar包
dom4j.jar:
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,dom4j无论在那个方面都是非常出色的。我早在将近两年之前就开始使用dom4j,直到现在。如今你可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这是必须使用的jar包,Hibernate用它来读写配置文件。
odmg.jar:
ODMG是一个ORM的规范,Hibernate实现了ODMG规范,这是一个核心的库,必须使用的jar包。
commons-collections.jar:
Apache Commons包中的一个,包含了一些Apache开发的集合类,功能比java.util.*强大。必须使用的jar包。
commons-beanutils.jar:
Apache Commons包中的一个,包含了一些Bean工具类类。必须使用的jar包。
commons-lang.jar:
Apache Commons包中的一个,包含了一些数据类型工具类,是java.lang.*的扩展。必须使用的jar包。
commons-logging.jar:
Apache Commons包中的一个,包含了日志功能,必须使用的jar包。这个包本身包含了一个Simple Logger,但是功能很弱。在运行的时候它会先在CLASSPATH找log4j,如果有,就使用log4j,如果没有,就找JDK1.4带的java.util.logging,如果也找不到就用Simple Logger。commons-logging.jar的出现是一个历史的的遗留的遗憾,当初Apache极力游说Sun把log4j加入JDK1.4,然而JDK1.4项目小组已经接近发布JDK1.4产品的时间了,因此拒绝了Apache的要求,使用自己的java.util.logging,这个包的功能比log4j差的很远,性能也一般。后来Apache就开发出来了commons-logging.jar用来兼容两个logger。因此用commons-logging.jar写的log程序,底层的Logger是可以切换的,你可以选择log4j,java.util.logging或者它自带的Simple Logger。不过我仍然强烈建议使用log4j,因为log4j性能很高,log输出信息时间几乎等于System.out,而处理一条log平均只需要5us。你可以在Hibernate的src目录下找到Hibernate已经为你准备好了的log4j的配置文件,你只需要到Apache 网站去下载log4j就可以了。commons-logging.jar也是必须的jar包。
使用Hibernate必须的jar包就是以上的这几个,剩下的都是可选的。
ant.jar:
Ant编译工具的jar包,用来编译Hibernate源代码的。如果你不准备修改和编译Hibernate源代码,那么就没有什么用,可选的jar包
optional.jar:
Ant的一个辅助包。
c3p0.jar:
C3PO是一个数据库连接池,Hibernate可以配置为使用C3PO连接池。如果你准备用这个连接池,就需要这个jar包。
proxool.jar:
也是一个连接池,同上。
commons-pool.jar, commons-dbcp.jar:
DBCP数据库连接池,Apache的Jakarta组织开发的,Tomcat4的连接池也是DBCP。
实际上Hibernate自己也实现了一个非常非常简单的数据库连接池,加上上面3个,你实际上可以在Hibernate上选择4种不同的数据库连接池,选择哪一个看个人的偏好,不过DBCP可能更通用一些。另外强调一点,如果在EJB中使用Hibernate,一定要用App Server的连接池,不要用以上4种连接池,否则容器管理事务不起作用。
connector.jar:
JCA 规范,如果你在App Server上把Hibernate配置为Connector的话,就需要这个jar。不过实际上一般App Server肯定会带上这个包,所以实际上是多余的包。
jaas.jar:
JAAS是用来进行权限验证的,已经包含在JDK1.4里面了。所以实际上是多余的包。
jcs.jar:
如果你准备在Hibernate中使用JCS的话,那么必须包括它,否则就不用。
jdbc2_0-stdext.jar:
JDBC2.0的扩展包,一般来说数据库连接池会用上它。不过App Server都会带上,所以也是多余的。
jta.jar:
JTA规范,当Hibernate使用JTA的时候需要,不过App Server都会带上,所以也是多余的。
junit.jar:
Junit包,当你运行Hibernate自带的测试代码的时候需要,否则就不用。
xalan.jar, xerces.jar, xml-apis.jar:
Xerces是XML解析器,Xalan是格式化器,xml-apis实际上是JAXP。一般App Server都会带上,JDK1.4也包含了解析器,不过不是Xerces,是Crimson,效率比较差,不过Hibernate用XML只不过是读取配置文件,性能没什么紧要的,所以也是多余的。
posted @
2006-03-14 20:42 阿成 阅读(221) |
评论 (0) |
编辑 收藏
这是好友面试的一道题,其实我知道使用的区别,StringBuffer必须new出来,StringBuffer的append的效率比string的+=的效率高,
其实发现还有很大的区别,看了看以前scjp的考题
public class Test {
public static void stringReplace (String text) {
text = text.replace('j' , 'i');
}
public static void bufferReplace (StringBuffer text) {
text = text.append("C");
}
public static void main (String args[]) {
String textString = new String ("java");
StringBuffer textBuffer = new StringBuffer ("java");
stringReplace (textString);
bufferReplace (textBuffer);
System.out.println (textString + textBuffer);
}
}
答案是 javajavaC
这是Java参数传递(by value)造成的,是不可变的(immutable).,例如 基本类型,String传值,复制了值传递过去;可变的(Object)传值,复制了引用传递过去。
而题目中第七行text = text.append (“C”),append方法会改变text中的值
而这个text与main中的textBuffer是指向同一个对象,所以对应的输出是javac。
string的值永远不会改变!
String a = "a";//假设a指向地址0x0001,
a = "b";//重新负值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的。
从表面上看String类型的对象改变了值,但事实是他不能改变值,只能改变指向的地址
StringBuffer则不同,直接改变指向的地址中保留的值
还有
StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
s1.equals(s2)//为什么是false
String s1 = new String("a");
String s2 = new String("a");
s1.equals(s2)//为什么是true
StringBuffer类中没有重新定义equals这个方法,因此这个方法就来自Object类,
而Object类中的equals方法是用来比较地址的,所以等于false.
String类中重新定义了equals这个方法,而且比较的是值,而不是地址。所以会是
true。
对于这样能不能面试出真正的水平,感到怀疑。
posted @
2006-03-09 21:45 阿成 阅读(286) |
评论 (0) |
编辑 收藏
这是在网上发现的一篇关于Spring AOP编程的教程,读完这篇文章后,Spring AOP不再难以理解,因此我把它译成中文,推荐给Spring AOP的初学者。这是译文的
链接
。
AOP正在成为软件开发的下一个圣杯。使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。AOP可以防止代码混乱。
为了理解AOP如何做到这点,考虑一下记日志的工作。日志本身不太可能是你开发的主程序的主要任务。如果能将“不可见的”、通用的日志代码注入主程序中,那该多好啊。AOP可以帮助你做到。
Spring framework是很有前途的AOP技术。作为一种非侵略性的,轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需一人要对付AOP framework,其他人还是象往常一样编程。
AOP是很多直觉难以理解的术语的根源。幸运的是,你只要理解三个概念,就可以编写AOP模块。这三个概念是:advice,pointcut和advisor。advice是你想向别的程序内部不同的地方注入的代码。pointcut定义了需要注入advice的位置,通常是某个特定的类的一个public方法。advisor是pointcut和advice的装配器,是将advice注入主程序中预定义位置的代码。
既然我们知道了需要使用advisor向主要代码中注入“不可见的”advice,让我们实现一个Spring AOP的例子。在这个例子中,我们将实现一个before advice,这意味着advice的代码在被调用的public方法开始前被执行。以下是这个before advice的实现代码:
代码:
|
package com.company.springaop.test;
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice;
public class TestBeforeAdvice implements MethodBeforeAdvice {
public void before(Method m, Object[] args, Object target) throws Throwable { System.out.println("Hello world! (by " + this.getClass().getName() + ")"); } } |
接口MethodBeforeAdvice只有一个方法before需要实现,它定义了advice的实现。before方法共用三个参数,它们提供了相当丰富的信息。参数Method m是advice开始后执行的方法。方法名称可以用作判断是否执行代码的条件。Object[] args是传给被调用的public方法的参数数组。当需要记日志时,参数args和被执行方法的名称,都是非常有用的信息。你也可以改变传给m的参数,但要小心使用这个功能;编写最初主程序的程序员并不知道主程序可能会和传入参数的发生冲突。Object target是执行方法m对象的引用。
在下面的BeanImpl类中,每个public方法调用前,都会执行advice:
代码:
|
package com.company.springaop.test;
public class BeanImpl implements Bean {
public void theMethod() { System.out.println(this.getClass().getName() + "." + new Exception().getStackTrace()[0].getMethodName() + "()" + " says HELLO!"); } } |
类BeanImpl实现了下面的接口Bean:
代码:
|
package com.company.springaop.test;
public interface Bean { public void theMethod(); } |
虽然不是必须使用接口,但面向接口而不是面向实现编程是良好的编程实践,Spring也鼓励这样做。
pointcut和advice通过配置文件来实现,因此,接下来你只需编写主方法的Java代码:
代码:
|
package com.company.springaop.test;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) { //Read the configuration file ApplicationContext ctx = new FileSystemXmlApplicationContext("springconfig.xml");
//Instantiate an object Bean x = (Bean) ctx.getBean("bean");
//Execute the public method of the bean (the test) x.theMethod(); } } |
我们从读入和处理配置文件开始,接下来马上要创建它。这个配置文件将作为粘合程序不同部分的“胶水”。读入和处理配置文件后,我们会得到一个创建工厂ctx。任何一个Spring管理的对象都必须通过这个工厂来创建。对象通过工厂创建后便可正常使用。
仅仅用配置文件便可把程序的每一部分组装起来。
代码:
|
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans> <!--CONFIG--> <bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.company.springaop.test.Bean</value> </property> <property name="target"> <ref local="beanTarget"/> </property> <property name="interceptorNames"> <list> <value>theAdvisor</value> </list> </property> </bean>
<!--CLASS--> <bean id="beanTarget" class="com.company.springaop.test.BeanImpl"/>
<!--ADVISOR--> <!--Note: An advisor assembles pointcut and advice--> <bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theBeforeAdvice"/> </property> <property name="pattern"> <value>com\.company\.springaop\.test\.Bean\.theMethod</value> </property> </bean>
<!--ADVICE--> <bean id="theBeforeAdvice" class="com.company.springaop.test.TestBeforeAdvice"/> </beans> |
四个bean定义的次序并不重要。我们现在有了一个advice,一个包含了正则表达式pointcut的advisor,一个主程序类和一个配置好的接口,通过工厂ctx,这个接口返回自己本身实现的一个引用。
BeanImpl和TestBeforeAdvice都是直接配置。我们用一个唯一的ID创建一个bean元素,并指定了一个实现类。这就是全部的工作。
advisor通过Spring framework提供的一个RegexMethodPointcutAdvisor类来实现。我们用advisor的一个属性来指定它所需的advice-bean。第二个属性则用正则表达式定义了pointcut,确保良好的性能和易读性。
最后配置的是bean,它可以通过一个工厂来创建。bean的定义看起来比实际上要复杂。bean是ProxyFactoryBean的一个实现,它是Spring framework的一部分。这个bean的行为通过一下的三个属性来定义:
- 属性proxyInterface定义了接口类。
- 属性target指向本地配置的一个bean,这个bean返回一个接口的实现。
- 属性interceptorNames是唯一允许定义一个值列表的属性。这个列表包含所有需要在beanTarget上执行的advisor。注意,advisor列表的次序是非常重要的。
Spring工具
虽然你可以手工修改Ant构建脚本,但使用SpringUI(译注:SpringUI现在是Spring framework的一部分,并改名为spring-ide),使用Spring AOP变得很简单,只要点点鼠标即可。你可以把SpringUI安装成Eclipse的一个plug-in。然后,你只需在你的project上右击鼠标,并选择“add Spring Project Nature”。在project属性中,你可以在“Spring Project”下添加Spring配置文件。在编译前把下面的类库加入project:aopalliance.jar,commons-logging.jar,jakarta-oro-2.0.7.jar和spring.jar。运行程序时你会看到下面的信息:
... (logging information)
Hello world! (by com.company.springaop.test.TestBeforeAdvice)
com.company.springaop.test.BeanImpl.theMethod() says HELLO!
优点和缺点
Spring比起其他的framework更有优势,因为除了AOP以外,它提供了更多别的功能。作为一个轻型framework,它在J2EE不同的部分都可以发挥作用。因此,即使不想使用Spring AOP,你可能还是想使用Spring。另一个优点是,Spring并不要求开发团队所有的人员都会用它。学习Spring应该从Spring reference的第一页开始。读了本文后,你应该可以更好地理解Spring reference了。Spring唯一的缺点是缺乏更多的文档,但它的mailing list是个很好的补充,而且会不断地出现更多的文档。
posted @
2006-03-09 21:32 阿成 阅读(305) |
评论 (0) |
编辑 收藏
很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了。
我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化。
hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了。
二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache等,需要设置hibernate.cache.provider_class,我们这里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查询缓存,加上
hibernate.cache.use_query_cache=true
缓存可以简单的看成一个Map,通过key在缓存里面找value。
Class的缓存
对于一条记录,也就是一个PO来说,是根据ID来找的,缓存的key就是ID,value是POJO。无论list,load还是iterate,只要读出一个对象,都会填充缓存。但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,如果在缓存里面有,就从缓存取,没有的话就去数据库load。假设是读写缓存,需要设置:
<cache usage="read-write"/>
如果你使用的二级缓存实现是ehcache的话,需要配置ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示缓存是不是永远不超时,timeToLiveSeconds是缓存中每个元素(这里也就是一个POJO)的超时时间,如果eternal="false",超过指定的时间,这个元素就被移走了。timeToIdleSeconds是发呆时间,是可选的。当往缓存里面put的元素超过500个时,如果overflowToDisk="true",就会把缓存中的部分数据保存在硬盘上的临时文件里面。
每个需要缓存的class都要这样配置。如果你没有配置,hibernate会在启动的时候警告你,然后使用defaultCache的配置,这样多个class会共享一个配置。
当某个ID通过hibernate修改时,hibernate会知道,于是移除缓存。
这样大家可能会想,同样的查询条件,第一次先list,第二次再iterate,就可以使用到缓存了。实际上这是很难的,因为你无法判断什么时候是第一次,而且每次查询的条件通常是不一样的,假如数据库里面有100条记录,id从1到100,第一次list的时候出了前50个id,第二次iterate的时候却查询到30至70号id,那么30-50是从缓存里面取的,51到70是从数据库取的,共发送1+20条sql。所以我一直认为iterate没有什么用,总是会有1+N的问题。
(题外话:有说法说大型查询用list会把整个结果集装入内存,很慢,而iterate只select id比较好,但是大型查询总是要分页查的,谁也不会真的把整个结果集装进来,假如一页20条的话,iterate共需要执行21条语句,list虽然选择若干字段,比iterate第一条select id语句慢一些,但只有一条语句,不装入整个结果集hibernate还会根据数据库方言做优化,比如使用mysql的limit,整体看来应该还是list快。)
如果想要对list或者iterate查询的结果缓存,就要用到查询缓存了
查询缓存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然后
query.setCacheable(true);//激活查询缓存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可选
第二行指定要使用的cacheRegion是myCacheRegion,即你可以给每个查询缓存做一个单独的配置,使用setCacheRegion来做这个指定,需要在ehcache.xml里面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略第二行,不设置cacheRegion的话,那么会使用上面提到的标准查询缓存的配置,也就是net.sf.hibernate.cache.StandardQueryCache
对于查询缓存来说,缓存的key是根据hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
参数是"tiger%",那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了):
select * from cat c where c.name like ? , parameter:tiger%
这样,保证了同样的查询、同样的参数等条件下具有一样的key。
现在说说缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。
可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。
这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。
另外,如果hql查询包含select字句,那么查询缓存里面的value就是整个结果集了。
当hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢?
hibernate在一个地方维护每个表的最后更新时间,其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。
当通过hibernate更新的时候,hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表,当hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。
可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低。
Collection缓存
需要在hbm的collection里面设置
<cache usage="read-write"/>
假如class是Cat,collection叫children,那么ehcache里面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。
这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新。
缓存策略
只读缓存(read-only):没有什么好说的
读/写缓存(read-write):程序可能要的更新数据
不严格的读/写缓存(nonstrict-read-write):需要更新数据,但是两个事务更新同一条记录的可能性很小,性能比读写缓存好
事务缓存(transactional):缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境,这个我没有怎么研究过
读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,然后就直接取数据库查询。
在hibernate2.1的ehcache实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到60秒后超时。
不严格读写缓存不锁定缓存中的数据。
使用二级缓存的前置条件
你的hibernate程序对数据库有独占的写访问权,其他的进程更新了数据库,hibernate是不可能知道的。你操作数据库必需直接通过hibernate,如果你调用存储过程,或者自己使用jdbc更新数据库,hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的,但是据说3.1已经解决了这个问题。
这个限制相当的棘手,有时候hibernate做批量更新、删除很慢,但是你却不能自己写jdbc来优化,很郁闷吧。
SessionFactory也提供了移除缓存的方法,你一定要自己写一些JDBC的话,可以调用这些方法移除缓存,这些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不过我不建议这样做,因为这样很难维护。比如你现在用JDBC批量更新了某个表,有3个查询缓存会用到这个表,用evictQueries(String cacheRegion)移除了3个查询缓存,然后用evict(Class persistentClass)移除了class缓存,看上去好像完整了。不过哪天你添加了一个相关查询缓存,可能会忘记更新这里的移除代码。如果你的jdbc代码到处都是,在你添加一个查询缓存的时候,还知道其他什么地方也要做相应的改动吗?
----------------------------------------------------
总结:
不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。
如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。
在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。
posted @
2006-03-09 20:56 阿成 阅读(267) |
评论 (0) |
编辑 收藏
前几天接到了新的任务,开始了公司项目管理软件的开发(复杂其中一部分).
这段时间开始由项目经理带着我们开发,这几天跟他学到的东西还是不少的.如果每天都能这样
该多好呀. 而且意识到严谨的学习态度的重要性,不能囫囵吞枣.要学就要学通.
posted @
2006-03-09 20:52 阿成 阅读(219) |
评论 (0) |
编辑 收藏
转自:Potain 的BLOG
OpenSessionInView
Created by
potian. Last edited by
admin 61 days ago. Viewed 181 times.
[edit]
[attach]
Hibernate的Lazy初始化1:n关系时,你必须保证是在同一个Session内部使用这个关系集合,不然Hiernate将抛出例外。
另外,你不愿意你的DAO测试代码每次都打开关系Session,因此,我们一般会采用OpenSessionInView模式。
OpenSessionInViewFilter解决Web应用程序的问题
如果程序是在正常的Web程序中运行,那么Spring的
OpenSessionInViewFilter能够解决问题,它:
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory();
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
try {
filterChain.doFilter(request, response);
}
finally {
TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
closeSession(session, sessionFactory);
}
}
可以看到,这个Filter在request开始之前,把sessionFactory绑定到TransactionSynchronizationManager,和这个SessionHolder相关。这个意味着所有request执行过程中将使用这个session。而在请求结束后,将和这个sessionFactory对应的session解绑,并且关闭Session。
为什么绑定以后,就可以防止每次不会新开一个Session呢?看看HibernateDaoSupport的情况:
publicfinal void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
protectedfinal HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
我们的DAO将使用这个template进行操作:
publicabstract class BaseHibernateObjectDao
extends HibernateDaoSupport
implements BaseObjectDao {
protected BaseEntityObject getByClassId(finallong id) {
BaseEntityObject obj =
(BaseEntityObject) getHibernateTemplate()
.execute(new HibernateCallback() {
publicObject doInHibernate(Session session)
throws HibernateException {
return session.get(getPersistentClass(),
newLong(id));
}
});
return obj;
}
public void save(BaseEntityObject entity) {
getHibernateTemplate().saveOrUpdate(entity);
}
public void remove(BaseEntityObject entity) {
try {
getHibernateTemplate().delete(entity);
} catch (Exception e) {
thrownew FlexEnterpriseDataAccessException(e);
}
}
public void refresh(final BaseEntityObject entity) {
getHibernateTemplate().execute(new HibernateCallback() {
publicObject doInHibernate(Session session)
throws HibernateException {
session.refresh(entity);
returnnull;
}
});
}
public void replicate(finalObject entity) {
getHibernateTemplate().execute(new HibernateCallback() {
publicObject doInHibernate(Session session)
throws HibernateException {
session.replicate(entity,
ReplicationMode.OVERWRITE);
returnnull;
}
});
}
而HibernateTemplate试图每次在execute之前去获得Session,执行完就力争关闭Session
publicObject execute(HibernateCallback action) throws DataAccessException {
Session session = (!this.allowCreate ?
SessionFactoryUtils.getSession(getSessionFactory(),
false) :
SessionFactoryUtils.getSession(getSessionFactory(),
getEntityInterceptor(),
getJdbcExceptionTranslator()));
boolean existingTransaction =
TransactionSynchronizationManager.hasResource(getSessionFactory());
if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
session.setFlushMode(FlushMode.NEVER);
}
try {
Object result = action.doInHibernate(session);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// callback code threw application exception
throw ex;
}
finally {
SessionFactoryUtils.closeSessionIfNecessary(
session, getSessionFactory());
}
}
而这个SessionFactoryUtils能否得到当前的session以及closeSessionIfNecessary是否真正关闭session,端取决于这个session是否用sessionHolder和这个sessionFactory在我们最开始提到的TransactionSynchronizationManager绑定。
publicstatic void closeSessionIfNecessary(Session session,
SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
if (session == null ||
TransactionSynchronizationManager.hasResource(sessionFactory)) {
return;
}
logger.debug("Closing Hibernate session");
try {
session.close();
}
catch (JDBCException ex) {
// SQLException underneath
thrownew CleanupFailureDataAccessException(
"Cannot close Hibernate session", ex.getSQLException());
}
catch (HibernateException ex) {
thrownew CleanupFailureDataAccessException(
"Cannot close Hibernate session", ex);
}
}
HibernateInterceptor和OpenSessionInViewInterceptor的问题
使用同样的方法,这两个Interceptor可以用来解决问题。但是关键的不同之处在于,它们的力度只能定义在DAO或业务方法上,而不是在我们的Test方法上,除非我们把它们应用到TestCase的方法上,但你不大可能为TestCase去定义一个接口,然后把Interceptor应用到这个接口的某些方法上。直接使用HibernateTransactionManager也是一样的。因此,如果我们有这样的测试:
Category parentCategory = new Category ();
parentCategory.setName("parent");
dao.save(parentCategory);
Category childCategory = new Category();
childCategory.setName("child");
parentCategory.addChild(childCategory);
dao.save(childCategory);
Category savedParent = dao.getCategory("parent");
Category savedChild = (Category ) savedParent.getChildren().get(0);
assertEquals(savedChild, childCategory);
将意味着两件事情:
- 每次DAO执行都会启动一个session和关闭一个session
- 如果我们定义了一个lazy的关系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);将会让hibernate报错。
解决方案
一种方法是对TestCase应用Interceptor或者TransactionManager,但这个恐怕会造成很多麻烦。除非是使用增强方式的AOP.我前期采用这种方法(Aspectwerkz),在Eclipse里面也跑得含好。
另一种方法是在TestCase的setup和teardown里面实现和Filter完全一样的处理,其他的TestCase都从这个TestCase继承,这种方法是我目前所使用的。
转自:Karl Baum's Weblog
Karl Baum's Weblog
All | General | Java
Thursday July 08, 2004
Lazy Initialization and the DAO pattern with Hibernate and Spring
Hibernate and Lazy Initialization
Hibernate object relational mapping offers both lazy and non-lazy modes of object initialization. Non-lazy initialization retrieves an object and all of its related objects at load time. This can result in hundreds if not thousands of select statements when retrieving one entity. The problem is compounded when bi-directional relationships are used, often causing entire databases to be loaded during the initial request. Of course one could tediously examine each object relationship and manually remove those most costly, but in the end, we may be losing the ease of use benefit sought in using the ORM tool.
The obvious solution is to employ the lazy loading mechanism provided by hibernate. This initialization strategy only loads an object's one-to-many and many-to-many relationships when these fields are accessed. The scenario is practically transparent to the developer and a minimum amount of database requests are made, resulting in major performance gains. One drawback to this technique is that lazy loading requires the Hibernate session to remain open while the data object is in use. This causes a major problem when trying to abstract the persistence layer via the Data Access Object pattern. In order to fully abstract the persistence mechanism, all database logic, including opening and closing sessions, must not be performed in the application layer. Most often, this logic is concealed behind the DAO implementation classes which implement interface stubs. The quick and dirty solution is to forget the DAO pattern and include database connection logic in the application layer. This works for small applications but in large systems this can prove to be a major design flaw, hindering application extensibility.
Being Lazy in the Web Layer
Fortunately for us, the Spring Framework has developed an out of box web solution for using the DAO pattern in combination with Hibernate lazy loading. For anyone not familiar with using the Spring Framework in combination with Hibernate, I will not go into the details here, but I encourage you to read Hibernate Data Access with the Spring Framework. In the case of a web application, Spring comes with both the OpenSessionInViewFilter and the OpenSessionInViewInterceptor. One can use either one interchangeably as both serve the same function. The only difference between the two is the interceptor runs within the Spring container and is configured within the web application context while the Filter runs in front of Spring and is configured within the web.xml. Regardless of which one is used, they both open the hibernate session during the request binding this session to the current thread. Once bound to the thread, the open hibernate session can transparently be used within the DAO implementation classes. The session will remain open for the view allowing lazy access the database value objects. Once the view logic is complete, the hibernate session is closed either in the Filter doFilter method or the Interceptor postHandle method. Below is an example of the configuration of each component:
Interceptor Configuration
<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
...
</bean>
...
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter Configuration
<web-app>
...
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
...
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.spring</url-pattern>
</filter-mapping>
...
</web-app>
Implementing the Hibernate DAO's to use the open session is simple. In fact, if you are already using the Spring Framework to implement your Hibernate DAO's, most likely you will not have to change a thing. The DAO's must access Hibernate through the convenient HibernateTemplate utility, which makes database access a piece of cake. Below is an example DAO.
Example DAO
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
Being Lazy in the Business Layer
Even outside the view, the Spring Framework makes it easy to use lazy load initialization, through the AOP interceptor HibernateInterceptor. The hibernate interceptor transparently intercepts calls to any business object configured in the Spring application context, opening a hibernate session before the call, and closing the session afterward. Let's run through a quick example. Suppose we have an interface BusinessObject:
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
The class BusinessObjectImpl implements BusinessObject:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
Through some configurations in the Spring application context, we can instruct the HibernateInterceptor to intercept calls to the BusinessObjectImpl allowing it's methods to lazily access data objects. Take a look at the fragment below:
<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>
When the businessObject bean is referenced, the HibernateInterceptor opens a hibernate session and passes the call onto the BusinessObjectImpl. When the BusinessObjectImpl has finished executing, the HibernateInterceptor transparently closes the session. The application code has no knowledge of any persistence logic, yet it is still able to lazily access data objects.
Being Lazy in your Unit Tests
Last but not least, we'll need the ability to test our lazy application from J-Unit. This is easily done by overriding the setUp and tearDown methods of the TestCase class. I prefer to keep this code in a convenient abstract TestCase class for all of my tests to extend.
public abstract class MyLazyTestCase extends TestCase {
private SessionFactory sessionFactory;
private Session session;
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
}
protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}
public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}
( Jul 08 2004, 09:39:55 AM EDT ) Permalink Comments [2]
Trackback URL: http://jroller.com/trackback/kbaum/Weblog/orm_lazy_initialization_with_dao
Comments:
A few things to keep in the back of your mind if you take this approach; 1. If any errors occur while attempting to lazy load relationships in the view (JSP) it would be hard to present a nice error to the user. 2. This would result in at least 2 hibernate sessions (db connections being open for any one request), so you might want to up the number of connections available. Cheers, Dan
Posted by Dan Washusen on July 08, 2004 at 09:02 PM EDT #
I am a little confused on why it would be difficult to show a nice error jsp. Couldn't we just use the provided servlet container error page mechanisms? In regards to the 2 hibernate sessions being opened. Are you saying that the OpenSessionInViewInterceptor would be run twice if an exception was thrown? Thanks for your feedback!
posted @
2006-03-09 08:52 阿成 阅读(1221) |
评论 (0) |
编辑 收藏
1、我认为最关键的,是要让mm看到你的上进心。
男人的最大魅力在于事业有成,年轻人工作时间不长谈不上“有成”,这时候你就要让mm觉得你是
个有上进心的人。
别的可以胡说八道,但这个问题不能含糊,你一定要告诉mm,你对未来充满信心,你不满足于现状,并且你已经有了长远的计划,总之你的未来不是梦。
2、要显得有信心、有责任心
不要像个小孩子,女孩子都很懒希望能找个依*,你要拿出自己的信心和责任心来。
有一个错的选择总比没有选择要好的多。
3、不要太正经,但也不要太随?nbsp;
该正经的地方就正经,该调侃的的时候就调侃。
女孩子都喜欢有点玩世不恭的男人,所以别显得对什么都特别在意,那样太呆板。
4、显得成熟一点
遇事镇定、从容不迫的男人对mm有致命的吸引力。
二、如何与mm展开进一步接触(时间:开始追的阶段)
1、这个阶段最关键的是不能着急,不要把事情弄的那么清楚,让人家一眼就能看出你在追人家。
想一想,一般人都不会一眼就看上你,但也不会看一眼就讨厌你,都是老百姓家的孩子(除非你长得象周润发刘德华或者凯文科斯特纳),好感是需要随着了解的不断增加而实现的,所以问题的关键是你要得的进一步发展的机会。
站在女孩子的角度替人家想一想:你这么直接了当的冲过来要搞对象,女孩子肯定有心理压力。这要是接触一阵后发现不喜欢你,那不就成了耍你了么?所以如果你开始就摆出志在必得的姿势出来,基本上会被立刻闷回去。
2、要低姿态起步
首先要把关系定位成“朋友”,本来是“普通朋友”,你希望成为“好朋友”,有品位的还可以要求对方成为“红颜知己”什么的,总之千万不要说“追你”。
你想想,你如果根本不提“追”,那么女孩子也就更没机会“拒绝”你——你没追她怎么拒绝你?!
这样可以减轻女孩子的心理压力,使你们能顺利的交往下去。不要幻想认识三天就答应嫁给你,要充分的交往、了解,感情不是凭空产生的。
3、交往的过程中不要太急躁
要有张有弛,不要整天缠着人家,谁这样对你,你也会腻。我有个好朋友对我说,追女孩子的关键是八个字—— “忽冷忽热、欲擒故纵”(这是我同学多少年心血的结晶)。
你整天缠着人家自然不觉得你好,你适当的冷个一两天,女孩子就会想起你在的好处了。
还有就是不要拿出“非你不娶”的志气来,太掉价了不好,有时候可以耍点花招。
4、要适当的创造机会
前面说了,不要使事情立刻变成“你在追别人”,而你又需要得到接近女孩子的机会,这时就要看你的创造力了。
你可以搜集情报,想办法把守株待兔变成一场邂逅;也可以装做漫不经心的找出最最充足的理由邀请对方和你一起做什么事。
总之这个是最有技术含量的地方,实在不行可以找前辈请教。
5、切忌切忌:随便送人家礼物是不礼貌的
有些人追女孩子心切,喜欢经常买东西送人家,殊不知追女孩子最忌讳这个。
俗话说“无功不受禄”,你这样送人家东西就是在施加压力,人家会觉得欠你的,所以会想办法还给你,如果没办法还给你就会想办法不和你交往,免得总是欠你人情。
如果你想显示自己的诚意,倒不妨请女孩子一起消费,比如说找好的餐厅吃饭,或者找贵的地方一起玩什么的,女孩子自然能看出你花了很多钱,但钱终究是两个人一起花了而不是变成东西带回家。
三、“女朋友”到底是什么?
1、“女朋友”是一种事
实,而不是一份承诺
你和女孩子开始交往,从“普通朋友”变成“好朋友”,再到“非常非常好、无话不谈的朋友”,某一个阳光灿烂的午后,你“不小心”拉了她的手;“月上柳梢头”,你突然袭击吻了她。这时她就已经是你的女朋友了,无论她是否承认,她心理已经认为你是他男友了。
我知道最高明的,直到上床了都没问过“你是否愿意做我女朋友”,最后还是女孩子急了:“你怎么还不求我做你女朋友啊!”
所以说,千万不要急于把窗户纸捅破,情况越朦胧对你越有利。
2、“表白”是什么?
前面说了,表白实际上就是一个形式而已,正确的顺序应该是:事实上已经成为你女朋友了,你才能向人家表白,水到渠成。 很多人弄不明白这个问题,总以为人家先答应做自己女朋友,然后再如何如何,我只能说他非常非常“单纯”,也非常非常“愚蠢”。
3、有没有“迫不得已非表白不可”的时候?
有,比如说出现第三者,或者你和女孩子关系没有成熟但两个人可能分开一段时间。
这时候的表白就是条件不成熟的表白,风险非常大,类似于下围棋的时候形势严峻,落後的一方迫于无奈放出“胜负手”,赢了就赢了,输了也只能说“倒霉都是天生的”。
4、“爱”字不要轻易出口
经常看见论坛出现“大胆的表白”,说实话我真的认为这是非常不成熟的一种表现。“爱”是一个神圣的字,意味着追求,也意味着承诺,甚至体现出一种责任。
随便说“爱”的男人是不负责任的。
四、文明恋爱,不可强求
1、不是每个mm都能追到手的
好女孩总会有很多人追,不可能遂了每个人的心愿,总会有失败者。举个例子,就算你刻苦钻研掌握了最搞超的追mm原理,你一样追不上twins里的任何一个。
换个角度考虑问题,一个小学没毕业的农村小保姆,即使对你再好,每个月赚600给你买700的礼物(透支),愿意为你“当牛做马”,你也不会爱上她。如果她每天哭哭啼啼的缠着,你肯定觉得烦。
所以说爱情是需要物质基础的,至少需要平衡。
2、追mm做是一种严肃的社会活动
千万不要把人家搞烦了,要给自己留后路。大丈夫何患无妻?有些mm确实势利眼(少数),如果不服气,你可以发愤图强,用事实证明“她当时瞎了眼”,绝对不要误人误己。
最后补充一点千万不要在mm面前显得愤世嫉俗,愤世嫉俗有时候意味着“你很失败”
posted @
2006-02-21 16:51 阿成 阅读(453) |
评论 (3) |
编辑 收藏