AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。

那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。

AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。

1、定义接口和实现

  1. package com.meituan.hyt.test3.service;
  2. public interface UserService {
  3. public String getName(int id);
  4. public Integer getAge(int id);
  5. }
  1. package com.meituan.hyt.test3.service.impl;
  2. import com.meituan.hyt.test3.service.UserService;
  3. public class UserServiceImpl implements UserService {
  4. @Override
  5. public String getName(int id) {
  6. System.out.println("------getName------");
  7. return "Tom";
  8. }
  9. @Override
  10. public Integer getAge(int id) {
  11. System.out.println("------getAge------");
  12. return 10;
  13. }
  14. }

2、jdk动态代理实现

  1. package com.meituan.hyt.test3.jdk;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class MyInvocationHandler implements InvocationHandler {
  5. private Object target;
  6. MyInvocationHandler() {
  7. super();
  8. }
  9. MyInvocationHandler(Object target) {
  10. super();
  11. this.target = target;
  12. }
  13. @Override
  14. public Object invoke(Object o, Method method, Object[] args) throws Throwable {
  15. if("getName".equals(method.getName())){
  16. System.out.println("++++++before " + method.getName() + "++++++");
  17. Object result = method.invoke(target, args);
  18. System.out.println("++++++after " + method.getName() + "++++++");
  19. return result;
  20. }else{
  21. Object result = method.invoke(target, args);
  22. return result;
  23. }
  24. }
  25. }
  1. package com.meituan.hyt.test3.jdk;
  2. import com.meituan.hyt.test3.service.UserService;
  3. import com.meituan.hyt.test3.service.impl.UserServiceImpl;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Proxy;
  6. public class Main1 {
  7. public static void main(String[] args) {
  8. UserService userService = new UserServiceImpl();
  9. InvocationHandler invocationHandler = new MyInvocationHandler(userService);
  10. UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
  11. userService.getClass().getInterfaces(), invocationHandler);
  12. System.out.println(userServiceProxy.getName(1));
  13. System.out.println(userServiceProxy.getAge(1));
  14. }
  15. }

运行结果

++++++before getName++++++
------getName------
++++++after getName++++++
Tom
------getAge------
10

3、cglib动态代理实现

Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

CGLIB的核心类:
    net.sf.cglib.proxy.Enhancer – 主要的增强类
    net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
    Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

  1. package com.meituan.hyt.test3.cglib;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  5. public class CglibProxy implements MethodInterceptor {
  6. @Override
  7. public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
  9. System.out.println(method.getName());
  10. Object o1 = methodProxy.invokeSuper(o, args);
  11. System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");
  12. return o1;
  13. }
  14. }
  1. package com.meituan.hyt.test3.cglib;
  2. import com.meituan.hyt.test3.service.UserService;
  3. import com.meituan.hyt.test3.service.impl.UserServiceImpl;
  4. import net.sf.cglib.proxy.Enhancer;
  5. public class Main2 {
  6. public static void main(String[] args) {
  7. CglibProxy cglibProxy = new CglibProxy();
  8. Enhancer enhancer = new Enhancer();
  9. enhancer.setSuperclass(UserServiceImpl.class);
  10. enhancer.setCallback(cglibProxy);
  11. UserService o = (UserService)enhancer.create();
  12. o.getName(1);
  13. o.getAge(1);
  14. }
  15. }

运行结果:

++++++before CGLIB$getName$0++++++
getName
------getName------
++++++before CGLIB$getName$0++++++
++++++before CGLIB$getAge$1++++++
getAge
------getAge------
++++++before CGLIB$getAge$1++++++

最新文章

  1. x01.Weiqi.12: 定式布局
  2. Java获取本机ip和服务器ip
  3. imp导入oracle的dmp备份数据
  4. JqGrid单选
  5. IOS自定义场景切换动画。
  6. Sharepoint中用treeview来显示组织机构的人员状态的webpart
  7. Java源码中的发现:快速判断一个int值是几位数
  8. 关于安装PHP补装PDO与PDO_MYSQL操作
  9. HCE:Host-based Card Emulation基于Android设备的卡片模拟器
  10. Python 数据挖掘 工具包整理
  11. [vue最新实战] gank客户端(vue2 + vue-router2 + vuex +webpace + es6)新手福利,干货多多
  12. linkList hashSet ArrayList IO 序列化 1.1.瞬态transient .字符编码表 Properties
  13. 使用String. localeCompare比较字符串
  14. UGUI背包系统
  15. rabbitMQ windows 安装 入门
  16. python requests接口测试
  17. 一条命令,根据进程名判断有进程输出up,无进程无输出
  18. Ajax复习
  19. 如何判断JavaScript数据具体类型
  20. 【转载】Java8 HashMap之tableSizeFor

热门文章

  1. 创建一个包括菜单栏,工具栏,状态栏,文本编辑部件的经典GUI应用程序的骨架
  2. 4.前端注册表单验证 && 表单回填
  3. Docker 1
  4. 关于Linux DNS部分处理
  5. Open Live writer 远程博客管理客户端
  6. Ubuntu14.04下codeblocks手动编译配置bost_1_57_0
  7. 搜索引擎solr系列---solr分词配置
  8. mysql更新(八) 可视化工具Navicat的使用 索引
  9. 管道(pipe),进程之间的共享内存(Manager,Value)
  10. tornado-通过判断后台数据限制登陆--简单的