- 浏览: 172434 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (72)
- java (21)
- spring (3)
- struts2 (0)
- hibernate (0)
- sql (3)
- linux (20)
- java web (2)
- maven (1)
- cache (2)
- memcached api (1)
- 简化jdbc操作数据库 (2)
- JAVA 网络编程 (3)
- JAVA 多线程 (0)
- C&C++ (1)
- plsql (1)
- SVN常用命 (2)
- Hadoop2 (0)
- jvm (0)
- flex (0)
- html5 & css3 & ECMAscript (0)
- RFC 文档翻译 (0)
- http (0)
- Hadoop (1)
- android (0)
- BIG_DATA (2)
- v (0)
- linux php tomcat tengine (1)
最新评论
-
yuanliangding:
没整理。文章还有重复的部分。
Java虚拟机(JVM)中的内存设置详解 -
svsecomm:
...
转javassist用法 -
svsecomm:
[u][/u]
转javassist用法 -
hvang1988:
用惯了spirng,Guice的注解方式真有点不习惯反向的感 ...
Google Guice 5 web -
xaocaotanghui:
Java虚拟机(JVM)中的内存设置详解
2 AOP 面向切面编程
2.1 AOP入门
在前面的章节主要讲Guice的依赖注入,有了依赖注入的基础后我们再来看Guice的AOP。我们先从一个例子入手,深入浅出的去理解Guice的AOP的原理和实现。
首先我们定义服务Service,这个服务有一个简单的方法sayHello,当然了我们有一个服务的默认实现ServiceImpl,然后使用@ImplementedBy将服务和默认实现关联起来,同时将服务的实现标注为单例模式。
2 public interface Service {
3 void sayHello();
4 }
在服务的实现ServiceImpl中,我们sayHello方法就是输出一行信息,这行信息包含服务的类名,hashCode以及方法名称和执行的时间。
2 public class ServiceImpl implements Service {
3
4 @Override
5 @Named("log")
6 public void sayHello() {
7 System.out.println(String.format("[%s#%d] execute %s at %d", this.getClass().getSimpleName(),hashCode(),"sayHello",System.nanoTime()));
8 }
9
10 }
11
接下来定义一个AOP的实现。在Aopalliance中(大家都认可的AOP联盟)实现我们的方法拦截器。这个拦截器LoggerMethodInterceptor 也没有做什么特别的事情,只是记录些执行的时间,当然了由于执行时间比较短我们用纳秒来描述(尽管不是那么精确)。
在MethodInvocation中我们一定要调用proceed()方法,这样我们的服务才能被执行。当然了如果为了做某些控制我们就能决定是否调用服务代码了。
2
3 import org.aopalliance.intercept.MethodInterceptor;
4 import org.aopalliance.intercept.MethodInvocation;
5
6 public class LoggerMethodInterceptor implements MethodInterceptor {
7
8 @Override
9 public Object invoke(MethodInvocation invocation) throws Throwable {
10 String methodName = invocation.getMethod().getName();
11 long startTime=System.nanoTime();
12 out.println(String.format("before method[%s] at %s", methodName, startTime));
13 Object ret = null;
14 try {
15 ret = invocation.proceed();
16 } finally {
17 long endTime=System.nanoTime();
18 out.println(String.format(" after method[%s] at %s, cost(ns):%d", methodName, endTime,(endTime-startTime)));
19 }
20 return ret;
21 }
22 }
23
最后才是我们的客户端程序,注意在这里我们需要绑定一个拦截器,这个拦截器匹配任何类的带有log注解的方法。所以这就是为什么我们服务的实现方法需要用log标注的原因了。
2 @Inject
3 private Service service;
4
5 public static void main(String[] args) {
6 Injector inj = Guice.createInjector(new Module() {
7 @Override
8 public void configure(Binder binder) {
9 binder.bindInterceptor(Matchers.any(),//
10 Matchers.annotatedWith(Names.named("log")),//
11 new LoggerMethodInterceptor());
12 }
13 });
14 inj.getInstance(AopDemo.class).service.sayHello();
15 inj.getInstance(AopDemo.class).service.sayHello();
16 inj.getInstance(AopDemo.class).service.sayHello();
17 }
18 }
19
我们的程序输出了我们期望的结果。
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811321912287
after method[sayHello] at 7811322140825, cost(ns):16073369
before method[sayHello] at 7811322315064
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811322425280
after method[sayHello] at 7811322561835, cost(ns):246771
before method[sayHello] at 7811322710141
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811322817521
after method[sayHello] at 7811322952455, cost(ns):242314
关于此结果有几点说明。
(1)由于使用了AOP我们的服务得到的不再是我们写的服务实现类了,而是一个继承的子类,这个子类应该是在内存中完成的。
(2)除了第一次调用比较耗时外(可能guice内部做了比较多的处理),其它调用事件为0毫秒(我们的服务本身也没做什么事)。
(3)确实完成了我们期待的AOP功能。
我们的例子暂且说到这里,来看看AOP的相关概念。
2.2 AOP相关概念
老实说AOP有一套完整的体系,光是概念就有一大堆,而且都不容易理解。这里我们结合例子和一些场景来大致了解下这些概念。
通知(Advice)
所谓通知就是我们切面需要完成的功能。比如2.1例子中通知就是记录方式执行的耗时,这个功能我们就称之为一个通知。
比如说在很多系统中我们都会将操作者的操作过程记录下来,但是这个记录过程又不想对服务侵入太多,这样就可以使用AOP来完成,而我们记录日志的这个功能就是一个通知。通知除了描述切面要完成的工作外还需要描述何时执行这个工作,比如是在方法的之前、之后、之前和之后还是只在有异常抛出时。
连接点(Joinpoint)
连接点描述的是我们的通知在程序执行中的时机,这个时机可以用一个“点”来描述,也就是瞬态。通常我们这个瞬态有以下几种:方法运行前,方法运行后,抛出异常时或者读取修改一个属性等等。总是我们的通知(功能)就是插入这些点来完成我们额外的功能或者控制我们的执行流程。比如说2.1中的例子,我们的通知(时间消耗)不仅在方法执行前记录执行时间,在方法的执行后也输出了时间的消耗,那么我们的连接点就有两个,一个是在方法运行前,还有一个是在方法运行后。
切入点(Pointcut)
切入点描述的是通知的执行范围。如果通知描述的是“什么时候”做“什么事”,连接点描述有哪些“时候”,那么切入点可以理解为“什么地方”。比如在2.1例子中我们切入点是所有Guice容器管理的服务的带有@Named(“log”)注解的方法。这样我们的通知就限制在这些地方,这些地方就是所谓的切入点。
切面(Aspect)
切面就是通知和切入点的结合。就是说切面包括通知和切入点两部分,由此可见我们所说的切面就是通知和切入点。通俗的讲就是在什么时候在什么地方做什么事。
引入(Introduction)
引入是指允许我们向现有的类添加新的方法和属性。个人觉得这个特性尽管很强大,但是大部分情况下没有多大作用,因为如果一个类需要切面来增加新的方法或者属性的话那么我们可以有很多更优美的方式绕过此问题,而是在绕不过的时候可能就不是很在乎这个功能了。
目标(Target)
目标是被通知的对象,比如我们2.1例子中的ServiceImpl 对象。
代理(Proxy)
代理是目标对象被通知引用后创建出来新的对象。比如在2.1例子中我们拿到的Service对象都不是ServiceImpl本身,而是其包装的子类ServiceImpl$$EnhancerByGuice$$96717882。
织入(Weaving)
所谓织入就是把切面应用到目标对象来创建新的代理对象的过程。通常情况下我们有几种实际来完成织入过程:
编译时:就是在Java源文件编程成class时完成织入过程。AspectJ就存在一个编译器,运行在编译时将切面的字节码编译到目标字节码中。
类加载时:切面在目标类加载到JVM虚拟机中时织入。由于是在类装载过程发生的,因此就需要一个特殊的类装载器(ClassLoader),AspectJ就支持这种特性。
运行时:切面在目标类的某个运行时刻被织入。一般情况下AOP的容器会建立一个新的代理对象来完成目标对象的功能。事实上在2.1例子中Guice就是使用的此方式。
Guice支持AOP的条件是:
- 类必须是public或者package (default)
- 类不能是final类型的
- 方法必须是public,package或者protected
- 方法不能使final类型的
- 实例必须通过Guice的@Inject注入或者有一个无参数的构造函数
2.3 切面注入依赖
如果一个切面(拦截器)也需要注入一些依赖怎么办?没关系,Guice允许在关联切面之前将切面的依赖都注入。比如看下面的例子。
我们有一个前置服务,就是将所有调用的方法名称输出。
2 public interface BeforeService {
3
4 void before(MethodInvocation invocation);
5 }
6
2
3 @Override
4 public void before(MethodInvocation invocation) {
5 System.out.println("before method "+invocation.getMethod().getName());
6 }
7 }
8
然后有一个切面,这个切面依赖前置服务,然后输出一条方法调用结束语句。
2 @Inject
3 private BeforeService beforeService;
4 @Override
5 public Object invoke(MethodInvocation invocation) throws Throwable {
6 beforeService.before(invocation);
7 Object ret = null;
8 try {
9 ret = invocation.proceed();
10 } finally {
11 System.out.println("after "+invocation.getMethod().getName());
12 }
13 return ret;
14 }
15 }
在AopDemo2中演示了如何注入切面的依赖。在第9行,AfterMethodInterceptor 请求Guice注入其依赖。
2 @Inject
3 private Service service;
4 public static void main(String[] args) {
5 Injector inj = Guice.createInjector(new Module() {
6 @Override
7 public void configure(Binder binder) {
8 AfterMethodInterceptor after= new AfterMethodInterceptor();
9 binder.requestInjection(after);
10 binder.bindInterceptor(Matchers.any(),//
11 Matchers.annotatedWith(Names.named("log")),//
12 after);
13 }
14 });
15 AopDemo2 demo=inj.getInstance(AopDemo2.class);
16 demo.service.sayHello();
17 }
18 }
尽管切面允许注入其依赖,但是这里需要注意的是,如果切面依赖仍然走切面的话那么程序就陷入了死循环,很久就会堆溢出。
2.4 Matcher
Binder绑定一个切面的API是
com.google.inject.Binder.bindInterceptor(Matcher<? super Class<?>>, Matcher<? super Method>, MethodInterceptor...)
第一个参数是匹配类,第二个参数是匹配方法,第三个数组参数是方法拦截器。也就是说目前为止Guice只能拦截到方法,然后才做一些切面工作。
对于Matcher有如下API:
- com.google.inject.matcher.Matcher.matches(T)
- com.google.inject.matcher.Matcher.and(Matcher<? super T>)
- com.google.inject.matcher.Matcher.or(Matcher<? super T>)
其中第2、3个方法我没有发现有什么用,好像Guice不适用它们,目前没有整明白。
对于第一个方法,如果是匹配Class那么这里T就是一个Class<?>的类型,如果是匹配Method就是一个Method对象。不好理解吧。看一个例子。
2 @Override
3 public Matcher<Class<?>> and(Matcher<? super Class<?>> other) {
4 return null;
5 }
6 @Override
7 public boolean matches(Class<?> t) {
8 return t==ServiceImpl.class;
9 }
10 @Override
11 public Matcher<Class<?>> or(Matcher<? super Class<?>> other) {
12 return null;
13 }
14 }
在前面的例子中我们是使用的Matchers.any()对象匹配所有类而通过标注来识别方法,这里可以只匹配ServiceImpl类来控制服务运行流程。
事实上Guice里面有一个Matcher的抽象类com.google.inject.matcher.AbstractMatcher<T>,我们只需要覆盖其中的matches方法即可。
大多数情况下我们只需要使用Matchers提供的默认类即可。Matchers中有如下API:
- com.google.inject.matcher.Matchers.any():任意类或者方法
- com.google.inject.matcher.Matchers.not(Matcher<? super T>):不满足此条件的类或者方法
- com.google.inject.matcher.Matchers.annotatedWith(Class<? extends Annotation>):带有此注解的类或者方法
- com.google.inject.matcher.Matchers.annotatedWith(Annotation):带有此注解的类或者方法
- com.google.inject.matcher.Matchers.subclassesOf(Class<?>):匹配此类的子类型(包括本身类型)
- com.google.inject.matcher.Matchers.only(Object):与指定类型相等的类或者方法(这里是指equals方法返回true)
- com.google.inject.matcher.Matchers.identicalTo(Object):与指定类型相同的类或者方法(这里是指同一个对象)
- com.google.inject.matcher.Matchers.inPackage(Package):包相同的类
- com.google.inject.matcher.Matchers.inSubpackage(String):子包中的类(包括此包)
- com.google.inject.matcher.Matchers.returns(Matcher<? super Class<?>>):返回值为指定类型的方法
通常只需要使用上面的方法或者组合方法就能满足我们的需求。
通过上面的学习可以看出,Guice的AOP还是很弱的,目前仅仅支持方法级别上的,另外灵活性也不是很高。
发表评论
-
java annotation
2013-10-19 14:27 712元数据的作用 如果要对于元数据的作用进行分类,目前还没有明 ... -
sun directory server
2013-10-17 14:07 736Sun One Directory Server(LDAP) ... -
sun ldap
2013-10-16 16:07 413LDAP快速入门 1. LDAP简介 LDAP(轻 ... -
httpclient 上传文件客户端处理
2013-10-07 11:09 898/** * */package cn.vwall.playe ... -
httpclient 上传文件 服务端解析
2013-10-07 11:07 848//commons-fileupload.jar common ... -
ffmpeg 视频加水印
2013-09-02 14:22 8271ffmpeg为视频添加水印watermark【转加补充】 ... -
ffmpeg args
2013-08-25 02:17 937先从Mencoder这个开始,将所有格式转AVI, RM ... -
socket 2
2013-06-11 20:57 975二、用于获得和设置Socket选项的getter和sette ... -
StringUtils
2012-12-31 01:45 1742StringUtils详细介绍 public stat ... -
转javassist用法
2012-10-15 17:15 13371Javassist是一个执行字 ... -
转 Quartz Cron
2012-10-02 10:28 1120Cron表达式 Quartz使用类似于Linux下的Cr ... -
apache db
2012-05-23 16:15 937queryRunner 摘抄 查询 runner ... -
Excel 色表
2012-05-11 16:09 1213按颜色菜单的色块位置排列 第1列 第2列 ... -
Google Guice 5 web
2012-05-11 16:09 31913 Web 和 Servlet 3.1 快速开始 我 ... -
Google Guice 4
2012-05-02 23:00 1893本章节继续讨论依赖注入的其他话题,包括作用域(scope ... -
Google Guice 3
2012-05-02 22:43 15491.3 更多话题 1.3.1 接口多实现 如果一个接 ... -
Google Guice 2
2012-05-02 22:30 11311.2 属性注入(Field Inject) 1.2. ... -
Google Guice 1
2012-05-02 22:23 15061. 依赖注入 1.1 类依赖注入 所谓的绑定就是将 ... -
Java虚拟机(JVM)中的内存设置详解
2012-04-13 17:03 32769Java虚拟机(JVM)中的内存设置详解 在一些 ... -
hashCode、hashCode()与HashSet集合
2011-09-09 14:02 1030hashCode、hashCode()与HashSet集合 ...
相关推荐
sisu-guice-3.1.3-no_aop.jar
Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC)。Guice非常小而且快。Guice是类型安全的,它能够对构造函数,属性,方法(包含任意个参数的任意方法,而不仅仅是setter...
guice-2.0-no_aop.jar
Google Guice 这个高效的与Spring类似的依赖注入框架; MyBatis配置和使用; Google Guice与MyBatis集成,支持注解事务,简单的无法想象; Mybatis与mysql集成;实现发送邮件轮询; 源码是个web项目,里面有数据库的...
用 Guice 写 Java Guice 1.0 用户指南 王咏刚 译 Guice (读作"juice")是超轻量级的,下一代的,为Java 5及后续版本设计的依赖注入容器。
google guice 3.0源码,官方下载,帮助你更好理解google guice实现的原理
Google Guice: Agile Lightweight Dependency Injection Framework will not only tell you "how," it will also tell you "why" and "why not," so that all the knowledge you gain will be as widely applicable ...
谷歌Guice开发英文文档,很详细,对开发很有帮助,可当成工具书使用!
Google Guice需要的jar包: Guice-3.0.jar javax.inject.jar
DI容器,例如spring,picoContainer,EJB容器等等 与Guice的不同
google-guice用户手册,据说和spring pk
NULL 博文链接:https://m635674608.iteye.com/blog/2090042
博文链接:https://avengerbevis.iteye.com/blog/69237
aopalliance-1.0-20100517.210215-13.jar, aopalliance-1.0-sources.jar, aopalliance-1.0.jar, aopalliance-alpha1.jar, com.springsource.org.aopalliance-1.0.0.jar, ...guice-aopalliance.jar
java运行依赖jar包
Robojuice jar 包文件下载(内含guice-2.0-no_aop.jar 和roboguice-1.1.2.jar ) 另外附 Rogojuice 教程.txt
guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar
许可证检查 Guice AOP示例项目
用户指南 博文链接:https://hejianjie.iteye.com/blog/83374