ORACLE 外键关联表和字段查询
查看外键关联的对象
select a.owner, --主键拥有者 a.table_name, --主键表 b.column_name, --主键列 c.OWNER, --外键拥有者 c.table_name, --外键表 d.column_name, --外键列, a.constraint_name --约束名称 from user_constraints a left join user_cons_columns b on a.constraint_name=b.constraint_name left join user_constraints c on c.R_CONSTRAINT_NAME=a.constraint_name left join user_cons_columns d on c.constraint_name=d.constraint_name where 1=1 and a.constraint_type='R' --约束类型,R是外键,P是主键,U是唯一性约束 -- and a.table_name='T_B' --需要查看主外键关系的表 and a.constraint_name = 'FK_X' --约束名称 order by a.table_name;
「转」JVM垃圾收集器与内存分配策略
一、如何判断对象是否还在存活
-
引用计数法:
主流的Java虚拟机没有使用这种方法管理内存, 因为它很难解决循环依赖
-
可达性分析:
通过一系列的称为”GC Roots“的对象作为起始点, 从这些节点开始向下搜索, 搜索所走过的路径称为引用链, 当一个对象到GC Roots没有与任何引用链相连时, 则证明该对象是不可用的。
作为GC Roots的对象包括以下几种:虚拟机栈中引用的对象、 方法区中类静态属性引用的对象、方法区中常量引用的对象以及本地方法栈中JNI引用的对象。
二、引用:
-
定义:
如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址, 就称这块内存代表着一个引用。
-
分类:
-
-
强引用:
代码之中普遍存在的, 如Object obj = new Object(), 只要强引用还在, GC就永远不会回收该内容。
-
软引用:
描述有用但非必须的对象。 对于软引用关联着的对象, 在系统将要抛出内存异常之前, 会将这些对象列进回收范围进行二次回收。 如果这次回收还没有足够的内存, 才会抛出异常。(SoftReference)
-
弱引用:
弱引用也用来描述非必须的对象。被若引用关联的对象只能活到下次垃圾回收发生之前。 当垃圾收集器工作时, 无论当前内存是否足够, 都会回收掉只被弱引用关联的对象。
-
虚引用:
又称为幽灵引用或者幻影引用。 一个对象是否有虚引用的存在, 丝毫不会影响对象的生存时间, 也不能通过虚引用获得对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。
-
三、对象标记之后就会回收吗
-
可达性分析之后, 没有在任何GC Roots引用链上的对象, 就会被第一次标记, 标记之后还有一次筛选的过程;
-
筛选的条件是: 此对象是否有必要执行finalize方法,有必要执行的条件是:该对象覆盖了finalize方法, 并且没有虚拟机调用过, 也就是说任何一个对象的finalize方法都只会被系统执行一次。
-
如果有必要执行finalize方法, 该对象则将会被放置一个成为F_Queue的队列之中, 并在稍后由一个有虚拟机自动建立的、低优先级的Finalizer线程去触发该方法。但不会等待finalize执行结束。
-
finalize方法是对象逃脱死亡命运的最后一次机会。稍后GC将对F_Queue中的对象进行第二次小规模的标记, 如果能与引用链上任何一个对象建立关系, 对象就不会被再次标记, 从而活下来。
-
注:如果没有必要执行finalize对象, 是不是就会立即被GC 回收呢
四、方法区的回收
-
在堆中,尤其是新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间, 而永久代的垃圾收集效率远低于此。
-
永久代中的垃圾收集主要回收两部分内容: 废弃常量和无用的类
-
废弃常量: 当前系统中没有任何一个对象引用该常量。
-
无用的类: 该类所有的实例都已被回收(Java堆中不存在该类的任何实例)、加载该类的ClassLoader被回收、该类对应的java.lang.Class对象没有在任何地方被引用, 无法在任何地方通过反射访问该类的方法。
五、垃圾收集算法
1、标记-清除算法:
首先是标记出所有需要回收的对象, 在标记完成后回收所有被标记的对象。
缺点:
-
-
效率问题: 标记和清楚两个过程的效率都不高
-
空间问题: 标记清除之后会造成大量不连续的内存碎片, 空间碎片太多导致需要分配较大对象时, 无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
-
2、复制算法:
描述:将可用内存按容量划分为两块, 每次只是用其中的一块, 当这一块内存用完了, 就将还存活的对象复制到另外一块内存, 然后再把已使用过的内存空间一次性的清理掉。
优点:不用考虑内存碎片
缺点:内存缩小为原来的一半
应用:目前的商业虚拟机都采用这种收集算法来回收新生代,具体如下:
将内存分为一块较大的Eden空间和两块较小的Survivior空间, 每次使用Eden和其中一个Survivior空间。
当回收时, 将Eden区和Survivior中还存活着的对象一次性的复制到另外一块Survivior空间上, 最后清理Eden区和另一块Survivior区。
Hotspot 虚拟机默认Eden和Survivior的大小比例是8:1
当另外一块Survivior空间没有足够的空间存放上一次新生代存活的对象时, 这些对象将直接通过分配担保机制进入老年代。
3、标记-整理算法:
标记之后, 让所有存活的对象都向一端移动, 然后直接清理掉端边界以外的内存。
4、分代收集算法:
当前虚拟机的垃圾收集都采用”分代收集“算法。一般是把Java堆分为新生代和老年代;
新生代: 每次垃圾收集时都会有大批对象死去, 只有少量存活, 该区域采用复制算法。
老年代:对象存活率较高, 而且没有额外空间对她进行分配担保, 就必须使用”标记-清理“或者”标记-整理“算法进行回收。
六、HotSpot 的算法
1、枚举根节点:
GC停顿:
可达性分析要确保在一个一致性的快照中进行, 确保分析过程中引用关系不再变化。 GC进行时, 必须停顿所有的Java执行线程。 Stop the World
如何枚举根节点:
GC Roots主要在全局性的引用, 如常量、类静态属性, 与执行上下文中, 如果逐个检查, 必然消耗大量的时间。
使用OopMap: HotSpot使用一组OopMap来记录对象的引用, 加快GC Roots的枚举。
2、安全点:
背景: 如果每个指令都生成对应的OopMap, 那会需要大量的额外空间, 这样GC的成本将会变得非常高。
解决办法: 只在特定位置记录对象的引用情况, 这些特点的位置我们称之为安全点。
安全点的选定条件:是否具有让程序长时间执行的指令 (原因)
如何保证GC时, 让所有线程都跑到最近的安全点上再停顿下来:
-
-
主动式中断:
GC需要中断线程的时候, 不直接对线程操作, 而是简单的设置一个标志, 而是在执行到安全点时轮训该标志, 如果标志为真就自己中断挂起。 -
抢先式中断:
GC发生时, 首先把所有的线程全部中断, 如果发现有线程中断的地方不在安全点上, 就恢复线程, 让他”跑“到安全点上。
现在几乎没有虚拟机采用这种方式来暂停线程以响应GC事件
-
3、安全区域:
背景: 安全点机制保证了程序执行时, 在不太长时间就会遇到可进入GC的安全点, 如果程序没有执行呢, 比如出于sleep或者blocked状态,
这时候线程无法响应jvm的中断请求, Jvm也显然不太可能等待线程被重新分配CPU时间。
安全区域:在一段代码之中, 引用关系不会发生变化, 在这个区域中任意地方开始GC都是安全的
实现: 当线程执行到安全区域后, 首先会标示自己进入了安全区域, 那样, 当在这段时间内发生GC时, 就不用管这样的线程了,当线程要离开
该区域时, 要检查系统是否已经完成了根节点枚举, 如果没完成, 它就必须等待直到收到可以安全离开安全区域的信号。
七,垃圾收集器
目前新生代垃圾收集器有Serial,ParNew,Parallel Scavenge; 老年代收集器有CMS,Serial Old,Parallel Old;G1这款垃圾收集器既能用于新生代又能用于老年代。
1,Serial收集器:
描述: 单线程收集器;他进行垃圾收集时,必须暂停所有的工作线程, 直到它收集结束;新生代采取复制算法暂停所有用户线程, 老年代采取标记-整理算法暂停所有用户线程。
现状: 目前为止, 依然是虚拟机运行在Client模式下的默认新生代收集器。收集几十兆甚至一两百兆的新生代, 停顿时间完全可以控制在几十毫秒最多一百毫秒以内。
2,ParNew收集器:
描述: 其实就是Serial的多线程版本;使用多线程进行垃圾收集;新生代采取复制算法暂停所有用户线程,老年代采用标记-整理算法暂停所有用户线程。
现状: 许多运行在Server模式下的虚拟机默认的新生代收集器;一个与性能无关的原因是:除了Serial收集器外, 目前只有它能与CMS收集器配合使用。
注:
-
-
ParNew收集器是使用-XX:+UseConcMarkSweepGC选项后的默认新生代收集器; 也可以使用-XX:UseParNewGC指定它
-
可以使用-XX:+ParallelGCThreads参数限制垃圾收集的线程数
-
3,Parallel Scavenge收集器:
描述: Parallel Scavenge收集器的目标是达到一个可控制的吞吐量。所谓吞吐量就是cpu用于运行用户代码的时间与CPU总消耗时间的比值, 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
参数: 最大垃圾收集停顿时间设置:-XX:MaxGCPauseMillis, 设置值是一个大于0的毫秒数, 收集器将尽可能地保证内存回收花费的时间不超过设定值。
GC停顿时间的缩短是以牺牲吞吐量和新生代空间来换取的,可能会把新生代调小一些, 以使在规定的时间内可以完成垃圾回收; 也可能为了减小停顿时间而增大GC频率。
-XX:+GCTimeRatio:设置一个大于0且小于100的整数值, 也就是垃圾收集时间占总时间的比率,如果设置成x, GC时间的占比就是 1/(1+x)
-XX:+UseAdaptiveSizePolicy: 这是一个开关参数, 打开这个参数后, 不需要手工指定新生代大小,Eden和Survior区的比例,晋升老年代对象年龄等细节参数。 虚拟机会根据当前系统的运行情况动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。
与ParNew收集器的区别:可以设置吞吐量和最大停顿时间; 具有自适应调节策略。
4,Serial Old收集器:
描述:Serial Old是Serial收集器的老年代版本, 单线程收集器, 使用“标记-整理”算法。
5,Parallel Old收集器:
描述: Parallel Scavenge收集器的老年代版本;吞吐量优先的收集器
6,CMS收集器: Concurrent Mark Sweep
描述:以获取最短回收停顿时间为目标;基于“标记-清除”算法
步骤:
-
-
初始标记:标记GC Roots能直接关联到的对象, 速度很快; 该阶段需要stop the world
-
并发标记:进行GC Roots trace, 并发进行
-
重新标记:修正并发期间因为用户程序继续运行而导致变动的那一部分对象的标记记录。
-
并发清除:
-
缺点:
-
-
CMS收集器对CPU资源非常敏感, 它虽然不会导致用户线程停顿, 但是由于占用了一部分cpu时间而导致应用变慢, 总吞吐量会降低。
-
CMS收集器无法处理浮动垃圾, 可能出现Concurrent Mode Failure失败, 导致另一次full gc的产生。并发清理过程中, 用户线程生成的垃圾不能被本次回收所清理, 只能等到下次GC时清理, 这部分垃圾称为“浮动垃圾”; 每次垃圾清理时, 都要为用户线程的运行留出内存空间, 所以不能等到没有空间时才回收。如果CMS运行期间预留的内存空间无法满足程序需要, 就会出出现一次“Concurrent Mode Failure”失败, 这时虚拟机将启动后背预案:临时使用Serial Old收集器重新进行老年代的垃圾收集; 使用-XX:CMSInitiatingOccupancyFraction设置阈值
-
CMS是基于“标记-清理”算法实现的收集器, 意味着收集结束时会产生大量的空间碎片。 空间碎片过多时, 会因为无法找到足够连续的内存而无法为大对象分配内存, 造成FULL GC
-
八、 理解GC日志
<img src="http://jishu.family.baidu.com/portal/lib/tpubbs/ueditor/themes/default/images/spacer.gif" no-repeat="" center="" center;border:1px="" solid="" #ddd"="" style="border: 0px; display: block;">
-
停顿类型:
[GC : minor GC, [Full GC: full GC
-
GC的位置:
[DefNew:Default New Generation Serial收集器新生代
[ParNew: Parallel 新生代
[PSYoungGen: Parallel Scanvenge收集器的新生代
[Tenured: 老年代
[Perm: 永久代
-
回收前后内存空间变化:
35592K -> 1814K(36288K): 回收前内存空间大小 -> 回收后内存空间大小(总的内存空间大小
九、内存分配与回收策略:
大的方向说, 对象主要分配在堆的新生代的Eden区上,如果启动了本地线程分配缓冲, 将按线程优先在TLAB上分配。
-
对象优先在Eden上分配:
当Eden上没有足够的空间分配时, 虚拟机会发起一次Minor GC, 将Eden上和一个survivior上存活的对象复制到另外一个Survivor空间上, 如果另外一个Survivior空间上没有足够的空间, 将会将存活的对象直接移动到老年代, 如果老年代也没有足够的空间, 虚拟机将会发起一次Full GC, 如果Full GC之后还是放不下, 则会报OOM异常。
-
大对象会直接进入老年代:
虚拟机提供一个参数, -XX:PretenureSizeThreshould, 大于这个值的对象直接在老年代分配, 避免Eden区域Survivior区的来回复制。
-
长期存活的对象直接进入老年代:
虚拟机每个对象定义了一个对象年龄计算器, 每经过一次Minor GC, 对象年龄加一, 当对象年龄达到一定数时(默认15),将会晋升到老年代。 阈值设置参数:-XX:MaxTenurngThreshould
-
动态对象年龄判断:
如果Survivior空间中相同年龄的对象的大小总和大于survivior空间的一半时, 大于等于该年龄的对象就可以直接进入老年代。
-
空间分配担保:
准备Minor GC时, 虚拟机首先会检查老年代的剩余空间是否大于新生代所有对象的总空间, 如果大于, 则可以进行Minor GC; 否则, 会去查看是否允许担保失败(HandlePromotionFailure), 如果不允许,虚拟机会直接发起一次Full GC; 如果允许, 虚拟机会去检查老年代剩余空间是否大于历次晋升到老年代对象的平均大小, 如果不大于, 则会发起一次Full GC; 如果大于, 则会发起Minor GC, 如果这时发现, 老年代没有足够空间来容纳新生代晋升来的对象的总大小, 这时仍要触发一次Full GC, 这个圈子绕的就有点大了。
十、Full GC 和Minor GC:
Minor GC: 发生在新生代, 速度比较快
Full GC: 发生在老年代, 一般都伴随这一次Minor GC
Full GC一般都要比Minor GC慢十倍以上,因为新生代采用复制算法, 速度比较快; 而老年代一般采用标记-清理/整理算法;
oracle 创建dblink
oracle 创建dblink方式除了使用tnsnames.ora,别名方式,还可以使用全链接地址的方式.如下
CREATE PUBLIC DATABASE LINK xxx CONNECT TO non-sys IDENTIFIED BY ***** USING '//172.1.2.3:1521/service_name';
vim注释和光标高亮行列的颜色设置
vim设置高亮显示当前行。
如果只是想在这一次使用vim是启用,只需要在正常模式下输入:set cursorline就好了。
要是想每一次打开vim都启用,就要在vimrc(macvim对应.gvimrc, 命令:mvim ~/.gvimrc)文件(ubuntu下位于/etc/vim/vimrc)里加入set cursorline这一句。
如果想修改高亮的颜色,可以在vimrc中加入这几句
“开启光亮光标行
set cursorline
hi CursorLine cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white
"开启高亮光标列
set cursorcolumn
hi CursorColumn cterm=NONE ctermbg=darkred ctermfg=white guibg=darkred guifg=white
其中Cursorline和CursorColumn分别表示光标所在的行和列,根据一般用户的习惯,高亮行就可以了(我试过高亮列,看起来异常不协调,哈哈哈,有兴趣的可以试试),
可以把darkred,white等换成你喜欢的颜色。另附上vim官网关于高亮显示当前行的tip
----------------------------------------------------------------------------------
Vim识别三种不同的终端:term,黑白终端;cterm,彩色终端;gui,Gvim窗口。
term,可以定义其字体显示为:bold、underline、reverse、italic或standout。例如以下命令,用逗号来组合使用这些属性:
:highlight Keyword term=reverse,bold
cterm,可以用ctermfg设置前景色;用ctermbg设置背景色。例如以下命令,定义蓝底红字并使用下划线来显示注释:
:highlight Comment cterm=underline ctermfg=red ctermbg=blue
gui,可以使用选项gui=attribute,来定义图形窗口下语法元素的显示属性。选项guifg和guibg,用来定义了前景色和背景色。推荐使 用的颜色包括:black, brown, grey, blue, green, cyan, magenta, yellow, white。
[转]Spring AOP 详解
此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题。最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP 来解决。一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容。本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智。
- 对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况
- 监控部分重要函数,若抛出指定的异常,需要以短信或邮件方式通知相关人员
- 金控部分重要函数的执行时间
事实上,以上需求没有AOP也能搞定,只是在实现过程中比较郁闷摆了。
- 需要打印日志的函数分散在各个包中,只能找到所有的函数体,手动添加日志。然而这些日志都是临时的,待问题解决之后应该需要清除打印日志的代码,只能再次手动清除^_^!
- 类 似1的情况,需要捕获异常的地方太多,如果手动添加时想到很可能明天又要手动清除,只能再汗。OK,该需求相对比较固定,属于长期监控的范畴,并不需求临 时添加后再清除。然而,客户某天要求,把其中20%的异常改为短信提醒,剩下的80%改用邮件提醒。改之,两天后,客户抱怨短信太多,全部改成邮件提 醒...
- 该需求通常用于监控某些函数的执行时间,用以判断系统执行慢的瓶颈所在。瓶颈被解决之后,烦恼同情况1
终于下定决心,采用AOP来解决!代码如下:
切面类TestAspect
- package com.spring.aop;
- /**
- * 切面
- *
- */
- public class TestAspect {
- public void doAfter(JoinPoint jp) {
- System.out.println("log Ending method: "
- + jp.getTarget().getClass().getName() + "."
- + jp.getSignature().getName());
- }
- public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
- long time = System.currentTimeMillis();
- Object retVal = pjp.proceed();
- time = System.currentTimeMillis() - time;
- System.out.println("process time: " + time + " ms");
- return retVal;
- }
- public void doBefore(JoinPoint jp) {
- System.out.println("log Begining method: "
- + jp.getTarget().getClass().getName() + "."
- + jp.getSignature().getName());
- }
- public void doThrowing(JoinPoint jp, Throwable ex) {
- System.out.println("method " + jp.getTarget().getClass().getName()
- + "." + jp.getSignature().getName() + " throw exception");
- System.out.println(ex.getMessage());
- }
- private void sendEx(String ex) {
- //TODO 发送短信或邮件提醒
- }
- }
- package com.spring.service;
- /**
- * 接口A
- */
- public interface AService {
- public void fooA(String _msg);
- public void barA();
- }
- package com.spring.service;
- /**
- *接口A的实现类
- */
- public class AServiceImpl implements AService {
- public void barA() {
- System.out.println("AServiceImpl.barA()");
- }
- public void fooA(String _msg) {
- System.out.println("AServiceImpl.fooA(msg:"+_msg+")");
- }
- }
- package com.spring.service;
- /**
- * Service类B
- */
- public class BServiceImpl {
- public void barB(String _msg, int _type) {
- System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");
- if(_type == 1)
- throw new IllegalArgumentException("测试异常");
- }
- public void fooB() {
- System.out.println("BServiceImpl.fooB()");
- }
- }
ApplicationContext
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
- default-autowire="autodetect">
- <aop:config>
- <aop:aspect id="TestAspect" ref="aspectBean">
- <!--配置com.spring.service包下所有类或接口的所有方法-->
- <aop:pointcut id="businessService"
- expression="execution(* com.spring.service.*.*(..))" />
- <aop:before pointcut-ref="businessService" method="doBefore"/>
- <aop:after pointcut-ref="businessService" method="doAfter"/>
- <aop:around pointcut-ref="businessService" method="doAround"/>
- <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>
- </aop:aspect>
- </aop:config>
- <bean id="aspectBean" class="com.spring.aop.TestAspect" />
- <bean id="aService" class="com.spring.service.AServiceImpl"></bean>
- <bean id="bService" class="com.spring.service.BServiceImpl"></bean>
- </beans>
测试类AOPTest
- public class AOPTest extends AbstractDependencyInjectionSpringContextTests {
- private AService aService;
- private BServiceImpl bService;
- protected String[] getConfigLocations() {
- String[] configs = new String[] { "/applicationContext.xml"};
- return configs;
- }
- /**
- * 测试正常调用
- */
- public void testCall()
- {
- System.out.println("SpringTest JUnit test");
- aService.fooA("JUnit test fooA");
- aService.barA();
- bService.fooB();
- bService.barB("JUnit test barB",0);
- }
- /**
- * 测试After-Throwing
- */
- public void testThrow()
- {
- try {
- bService.barB("JUnit call barB",1);
- } catch (IllegalArgumentException e) {
- }
- }
- public void setAService(AService service) {
- aService = service;
- }
- public void setBService(BServiceImpl service) {
- bService = service;
- }
- }
运行结果如下:
- log Begining method: com.spring.service.AServiceImpl.fooA
- AServiceImpl.fooA(msg:JUnit test fooA)
- log Ending method: com.spring.service.AServiceImpl.fooA
- process time: 0 ms
- log Begining method: com.spring.service.AServiceImpl.barA
- AServiceImpl.barA()
- log Ending method: com.spring.service.AServiceImpl.barA
- process time: 0 ms
- log Begining method: com.spring.service.BServiceImpl.fooB
- BServiceImpl.fooB()
- log Ending method: com.spring.service.BServiceImpl.fooB
- process time: 0 ms
- log Begining method: com.spring.service.BServiceImpl.barB
- BServiceImpl.barB(msg:JUnit test barB type:0)
- log Ending method: com.spring.service.BServiceImpl.barB
- process time: 0 ms
- log Begining method: com.spring.service.BServiceImpl.barB
- BServiceImpl.barB(msg:JUnit call barB type:1)
- log Ending method: com.spring.service.BServiceImpl.barB
- method com.spring.service.BServiceImpl.barB throw exception
- 测试异常
《Spring参考手册》中定义了以下几个AOP的重要概念,结合以上代码分析如下:
- 切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中<aop:aspect>来配置。
- 连接点(Joinpoint) :程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。
- 通知(Advice) :“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect
- 切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定
- 目标对象(Target Object) :被一个或者多个切面所通知的对象。例如,AServcieImpl和BServiceImpl,当然在实际运行时,Spring AOP采用代理实现,实际AOP操作的是TargetObject的代理对象。
-
AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,例如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将
<aop:config>
的proxy-target-class
属性设为true
通知(Advice)类型
- 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方法
- 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行
- 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
- 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如,TestAspect中的doAround方法。
- 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,TestAspect中的doThrowing方法。
切入点表达式
- 通常情况下,表达式中使用”execution“就可以满足大部分的要求。表达式格式如下:
- execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:方法的操作权限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:参数名
throws-pattern:异常
其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。
- 通知参数
可以通过args来绑定参数,这样就可以在通知(Advice)中访问具体参数了。例如,<aop:aspect>配置如下
- <aop:config>
- <aop:aspect id="TestAspect" ref="aspectBean">
- <aop:pointcut id="businessService"
- expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
- <aop:after pointcut-ref="businessService" method="doAfter"/>
- </aop:aspect>
- </aop:config>
TestAspect的doAfter方法中就可以访问msg参数,但这样以来AService中的barA()和BServiceImpl中的barB()就不再是连接点,因为execution(* com.spring.service.*.*(String,..))只配置第一个参数为String类型的方法。其中,doAfter方法定义如下:
- public void doAfter(JoinPoint jp,String msg)
- 访问当前的连接点
任何通知(Advice)方法可以将第一个参数定义为 org.aspectj.lang.JoinPoint
类型。JoinPoint
接口提供了一系列有用的方法, 比如 getArgs()
(返回方法参数)、getThis()
(返回代理对象)、getTarget()
(返回目标)、getSignature()
(返回正在被通知的方法相关信息)和 toString()
(打印出正在被通知的方法的有用信息。
spring获取webapplicationcontext,applicationcontext几种方法详解
方法一:在初始化时保存ApplicationContext对象
代码:
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ac.getBean("beanId");
说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。
方法二:通过Spring提供的工具类获取ApplicationContext对象
代码:
import org.springframework.web.context.support.WebApplicationContextUtils;
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc);
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
ac1.getBean("beanId");
ac2.getBean("beanId");
说明:
这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。
上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
其中 servletContext sc 可以具体 换成 servlet.getServletContext()或者 this.getServletContext() 或者 request.getSession().getServletContext(); 另外,由于spring是注入的对象放在ServletContext中的,所以可以直接在ServletContext取出 WebApplicationContext 对象: WebApplicationContext webApplicationContext = (WebApplicationContext) servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
方法三:继承自抽象类ApplicationObjectSupport
说明:抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
方法四:继承自抽象类WebApplicationObjectSupport
说明:类似上面方法,调用getWebApplicationContext()获取WebApplicationContext
方法五:实现接口ApplicationContextAware
说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。
Spring初始化时,会通过该方法将ApplicationContext对象注入。
在web应用中一般用ContextLoaderListener加载webapplication,如果需要在action之外或者control类之外获取webapplication思路之一是,单独写个类放在static变量中,
类似于:
private static AppContext instance;
private AbstractApplicationContext appContext;
public synchronized static AppContext getInstance() {
if (instance == null) {
instance = new AppContext();
}
return instance;
}
private AppContext() {
this.appContext = new ClassPathXmlApplicationContext(
"/applicationContext.xml");
}
public AbstractApplicationContext getAppContext() {
return appContext;
}
}
不过这样,还是加载了2次applicationcontext,servlet一次,路径加载一次;觉得不如直接用路径加载,舍掉servlet加载
在网上也找了些其他说法:实现ApplicationContextAware,,, 接口,或者servletcontextAware接口,还要写配置文件。有的竟然要把配置文件里的listener,换成自己的类,这样纯粹多此一举。不过有的应用不是替换,是在补一个listener,
我在一版的jpetstore(具体那一版不知道)里发现了这个:
[web.xml]里
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.ibatis.jpetstore.util.SpringInit</listener-class>
</listener>
其中SpringInit实现接口ServletContextListener :
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class SpringInit implements ServletContextListener {
private static WebApplicationContext springContext;
public SpringInit() {
super();
}
public void contextInitialized(ServletContextEvent event) {
springContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
}
public static ApplicationContext getApplicationContext() {
return springContext;
}
}
在其中的一个bean的构造里SpringInit获取applicationcontext,代码:
this(
(AccountService) SpringInit.getApplicationContext().getBean("accountService"),
(OrderService) SpringInit.getApplicationContext().getBean("orderService") );
}
恩,这种在action,servlet之外的bean里获取applicationcontext的方法值得参考,应该有用
[笔记] 一个生成基于gfwlist生成动态代理脚本的小程序pacmaker.py
已经添加代理实现,现在直接运行就可以生成基于gfwlist的自动代理脚本了.先贴出代码
#!/usr/bin/env python #Filename: pacmaker.py ''' 用于生成一个自动代理的脚本,使用autoproxy规则 ''' import os from optparse import OptionParser import urllib.request as req import urllib.parse as urlparse import base64 port = 80 protol = 'HTTP' host = 'localhost' avail_protol = ('HTTP','SOCKS4','SOCKS5',) output = os.path.realpath('.') + os.path.sep + 'pacmaked.pac' autoproxy_gfwlist = "http://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt" online_proxy = ( ( 'http://co.xinyali.info/includes/process.php?action=update', { 'type':0, 'u':autoproxy_gfwlist } ), ) gfwlist_file = None def make_pac(): gfwlist=None if gfwlist_file: with open(gfwlist_file,'r') as gfwfile: gfwlist = gfwfile.read() gfwlist = base64.b64decode(gfwlist).decode() else: import http.cookiejar cookie = http.cookiejar.CookieJar() opener = req.build_opener(req.HTTPCookieProcessor(cookie)) data = urlparse.urlencode(online_proxy[0][1]).encode() res = opener.open(online_proxy[0][0],data) gfwlist = res.read() gfwlist = base64.b64decode(gfwlist).decode() #print(gfwlist) pac_content = ''' function regExpMatch(url, pattern) { try { return new RegExp(pattern).test(url); } catch(ex) { return false; } } function FindProxyForURL(url, host) { %s return "DIRECT"; } ''' pac_part = [] for line in (i for i in gfwlist.split('\n') if i.find("!") != 0 and i.find('[') != 0 and len(i)>=3): return_str = 'return "{protol} {host}:{port}; DIRECT"; \n'.format( protol=protol, host=host, port=port ) #print(line) if line.find('@@') == 0: return_str = 'return "DIRECT"; \n' line = line[2:] if line.find('||') == 0: line = line[2:] match_str = line.replace('.',r'\\.') match_str = match_str.replace('/',r'\\/') if_str = r' if(regExpMatch(url,"^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?{match}"))'.format(match=match_str) else: if_str = r' if(shExpMatch(url, "http://*' + line + '*"))' pac_part.append(if_str+' '+return_str) #print(''.join(pac_part)) #print(pac_content % (''.join(pac_part))) pac_content = pac_content % (''.join(pac_part)) with open(output,'w') as outfile: outfile.write(pac_content) if __name__ == '__main__': usage = 'usage: %prog [option]' parser = OptionParser(usage=usage) parser.add_option('-o','--output',help='output file',dest='output',default=output) parser.add_option('-H','--host',help='hostname or ip of proxy',dest='host',default=host) parser.add_option('-p','--port',help='port of proxy',dest='port',default=port) parser.add_option('-t','--type',help='type of proxy',dest='protol',default=protol) parser.add_option('-i','--input',help="autoproxy input file",dest='input') (options,args) = parser.parse_args() print(options) if options.protol.upper() not in avail_protol: raise Exception('it\'s not support proxy protol.') protol = options.protol.upper() port = options.port host = options.host output = options.output gfwlist_file = options.input make_pac()
转 如何走编程之路
无论是处于爱好还是别的原因而选择程序员这个职业,时间久了都会面临一些难题,那就是如何坚持走这条路和如何走的问题.
和计算机打交道相对来说是比较困难的。计算机不是一个人,它不会说话,没有情感,但是程序员需要长时间的和它交流思想,很多时候为了把问题给它描述清楚需 要尝试各种方法让它去理解你的意思,按照你设计的方案去执行任务,出现问题了你还得有足够的耐心设法测试很多细节地方你依照它的理解方式描述的对不对,记 得微软有道面试题“你如何教自己的奶奶使用微软Excel表格系统?”,很多人认为很变态的面试题,奶奶是个文盲怎么教啊,那么试想一下这个问题和教计算 机下棋哪个简单呢,而程序员往往做的就是这种问题描述工作。编程是在自娱自乐,自己制定了游戏规则并描述给计算机,然后自己又跟踪计算机执行任务的思路, 来检测它是否完全理解并遵循了游戏规则,甚至在很多不人性化的工作环境下程序员就有点像小说《鲁滨孙漂流记》中的“鲁滨孙”,需要长时间跟和他一样孤单的 球沟通,可见程序员这个职业的工作难度和工作环境有很大的挑战性。
编程是一种特殊的交流方式,要将自己的想法按照计算机的理解方式描述给它,让它依照这个想法去执行任务,从这个角度来看程序员、教师、作家甚至翻译官的工 作性质是很像的,因此表达能力很重要,编程是在解决实际问题,花费时间更多的应该是锻炼自己问题的抽象能力和描述能力。比如在实际中一个用户频道购买的认 证文件,文件的内容是用户1订阅了哪些频道,用户2订阅了哪些频道……,这个认证文件(字符串)就可以抽象为计算机很容易理解的东西,一个二维数组 arr[x][y],用一维数组索引代表频道,如a[1],a[2]分别代表频道1,频道2,数组元素的值0表示没有购买,1表示购买,一维数组的指针代 表用户,将这些指针(也就是每个用户)存储到另一个一维数组,这样两个一维数组组合形成的二维数组代表了这个认证文件,判断arr[2][3]是否等于1 就可以表示用户2是否购买了索引3对应的频道,这样做的目的实际中主要是为了提高效率或者简化问题。再比如做一个网游外挂程序(不考虑从内存获取网游信 息),如何找怪,可以先观察怪物的特点,比如怪物身上有特殊的颜色,就可以让计算机屏幕模糊找色,找到坐标之后,将鼠标移动上去,如果鼠标形状变为攻击时 的形状,说明找到目标。如何自动加血,就可以想象一下玩家的操作,当血量低于上限50%时,按下键盘上对应加血的快捷键,转化为计算机语言,首先计算机需 要找到一个参考点,改点应该比较固定,不会随游戏窗口的移动或者缩放而改变与血条的相对位置,然后定时检测计算血条当前长度占总长度的百分比,低于 50%,按下快捷键完成加血。编程主要做的也就是这些事,这样编程路线就被简化了,那就是不管什么语言选择一个之后不要轻易更换,利用较少的时间掌握其语 法规则,然后花较多的时间培养自己两个能力,把实际中的问题抽象为计算机可以理解的符号和按照计算机的理解方式向计算机描述问题,具备了这样的能力就可以 解决平时编程中遇到的大多数问题。
如果你始终在追求和探索其中的乐趣,编程是可以充满乐趣的。在接触计算机之后第一个让我感兴趣的是Flash,记得当时完全是处于好奇,第一次上机就在机 房呆了6个小时左右,当时是大一没有遇到困难上网搜索的意识,从电脑里面导进去几张图片在那里琢磨,回忆老师做过的步骤,然后心里想着就这些可以按的东 西,挨个试试,总共也不知道做了多少试验,最后终于知道了一些简单的像补间动画这样的操作,但是原理还是一点都不都的,有时候可以有时候有不可以实现那些 功能。后来偶然的接触到了金鹰的AS3视频教程,一个暑假没有回家,几乎是天天从早到晚,自己一个人忙忙碌碌的学教程,做自己感觉好玩和新奇的东西,那时 候我可以肯定完全靠的是兴趣,学习过程中遇到了很多困难,虽然有点辛苦,但是收获了它带来的快乐和成就感,一个暑假我感觉做的最好的是一个播放器,界面全 部是自己PS的,实现了常见的各种功能,还有个简单的游戏,转盘抽奖的程序,模拟QQ空间那样的相册翻页……。现在想想已经没有了3年前的那种探索激情, 然而对Flash的爱好,始终没有太多改变,几乎每一次探索都会带来新的收获和欣喜,惊叹那些设计师们的创造才能和技术的博大精深。我关注着版本升级后的 新增功能以及Flash在游戏开发和网络上的各种应用,其中发现人人派对这个游戏的漏洞也是缘于此。Flash一直伴随着我学习编程的这几年,认识了很多 能为生活带来很多乐趣的人甚至一些公司的高级人才。从有的角度来看也许会有个矛盾,其实我已经选择了做一个C++程序员,但是却一直做着AS3的程序,因 为现实提供了太少的舞台给C++,而较多的却是AS3,我不愿意放弃任何一个机会。实践证明我的做法是对的,C++方面的确有很多可以发展的方向,如游戏 事业或者嵌入式,我并没有做出一个选择,而是仅仅满足于可以灵活使用MFC开发,做一个东西似乎就要为自己找一个做这个东西的理由,如果找不到做的理由那 就不做,还是我上面说过的问题,编程应该怎么学的问题,无论什么语言不重要,重要的是能不能解决实际中遇到的问题,用不用换语言衡量的标准是做了之后是不 是可以培养自己解决实际问题能力最好有额外的收益,最应该培养的东西培养好了才是最重要的。能力提高了,无论什么问题都可以轻松的转化描述给为计算机,自 己实力的进步是可以通过做东西感受得到的。休学这一年无论自己有什么想法,我都去做了尝试,做外挂,网络兼职,百度、中兴的程序比赛,感觉时间过得很快, 虽然总是自己一个人,但是自由、快乐,生活得精彩,因为我在乎的都不是最终的结果。
程序员真的伤不起!关于坚持,有个人说的很好“一个人端起杯子喝一口水,然后放下,那是正常的动作;一个人端起杯子坚持五个小时不放下,他就有点不正常 了;一个人端着杯子一动不动五十年,那他就是文物了,别人看一眼都得给钱”,不同的人应该会有不同的理解,在我看来,应该善待和保护自己,不要勉强自己去 做一些事,否则下次就害怕了,有怎么会喜欢做这些事。可以在感觉累的时候听听自己喜欢的音乐,看电影,上人人网,想玩就大胆的去玩,网游……,也是增加见 识,见识有时候很重要,直接影响一个人对事物的认识,认识要是改变了,也许整个人都改变了。无论何时都应该保持一个积极的心态,这样工作才不会累而且会有 较高的工作效率,合理的控制与机器打交道的时间,不要太久。另外有几个程序员社区是可以加入的,CSDN、ITeye、天地会等社区无疑是程序员的天堂, 在那里从来高手云集没有解决不了的问题,还可以随意自己发表一些见解和同行一起交流,百度知道也可以,这里都是技术上一些鸡毛算笔的小问题,要是感觉自己 实力还比较弱,可以在百度知道上看看大家都什么问题不会,自己能不能解决,也是个能力提高的过程,还可以寻找自己选择的编程语言的QQ群进去,这样就再也 不是自己一个人,而是一个群体,候融合进去会感觉很有气氛。如果条件允许可以,多出去走走,用心去感受大自然奥妙,你会发现没有什么解决不了的问题,也没 有什么改变不了的事,只要善于发现,勇于探索,敢于尝试,精彩无处不在.
[蛋疼][备忘]皮尔士定律
什么是皮尔士定律? 皮尔斯定律就是指的"如果你能使得P蕴含Q,强制P为真,那么P就必定为真". 数学符号表达式: ((P→Q)→P)→P .
云计算,网格计算,分布式计算,集群计算的区别?