AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。关于AOP的基础知识,相信多数童鞋都已经了如指掌,我们就略过这部分,来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。
        AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
        那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。
        AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释)

1、为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。
package jdkproxy;

/**
* 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口
*
* @author typ
*
*/
public interface Service {
/**
* add方法
*/
public void add();

/**
* update方法
*/
public void update();
}
2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise
package jdkproxy;

/**
* 被代理类,即目标类target
*
* @author typ
*
*/
public class AService implements Service {
/*
* (non-Javadoc)
*
* @see jdkproxy.Service#add()
*/
public void add() {
System.out.println("AService add>>>>>>>>>>>>>>>>>>");
}

/*
* (non-Javadoc)
*
* @see jdkproxy.Service#update()
*/
public void update() {
System.out.println("AService update>>>>>>>>>>>>>>>");
}
}
3、实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。
package jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* @author typ
*
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;

MyInvocationHandler() {
super();
}

MyInvocationHandler(Object target) {
super();
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
System.out.println("before-----------------------------");
// 程序执行
Object result = method.invoke(target, args);
// 程序执行后加入逻辑,MethodAfterAdviceInterceptor
System.out.println("after------------------------------");
return result;
}

}
4、测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);来生成的。
package jdkproxy;

import java.lang.reflect.Proxy;

/**
* @author typ
*
*/
public class Test {
public static void main(String[] args) {
Service aService = new AService();
MyInvocationHandler handler = new MyInvocationHandler(aService);
// Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
.getClass().getClassLoader(), aService.getClass()
.getInterfaces(), handler);
// 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
aServiceProxy.add();
System.out.println();
aServiceProxy.update();
// 以下是对B的代理
// Service bService = new BService();
// MyInvocationHandler handler = new MyInvocationHandler(bService);
// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
// .getClass().getClassLoader(), bService.getClass()
// .getInterfaces(), handler);
// bServiceProxy.add();
// System.out.println();
// bServiceProxy.update();
}
}
自此,jdk动态代理来实现AOP拦截机制的代码已经实现,下面我们看一下拦截的结果,程序输出结果如下:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
可以看到,在目标类AService的add和update方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。

二、cglib动态代理实现AOP拦截(代码中的关键地方都添加了注释)

1、目标类,cglib不需要定义目标类的统一接口
package cglibproxy;

/**
* 被代理类,即目标对象target
*
* @author typ
*
*/
public class Base {
/**
* 一个模拟的add方法
*/
public void add() {
System.out.println("add ------------");
}
}
2、实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);
package cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* 此为代理类,用于在pointcut处添加advise
*
* @author typ
*
*/
public class CglibProxy implements MethodInterceptor {

public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
System.out.println("before-------------");
// 执行目标类add方法
proxy.invokeSuper(object, args);
// 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
System.out.println("after--------------");
return null;
}

}
3、获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的,代码如下所示:
package cglibproxy;

import net.sf.cglib.proxy.Enhancer;

/**
* 工厂类,生成增强过的目标类(已加入切入逻辑)
*
* @author typ
*
*/
public class Factory {
/**
* 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
*
* @param proxy
* @return
*/
public static Base getInstance(CglibProxy proxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Base.class);
//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(proxy);
// 此刻,base不是单纯的目标类,而是增强过的目标类
Base base = (Base) enhancer.create();
return base;
}
}
4、测试类
package cglibproxy;

/**
* @author typ
*
*/
public class Test {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
// base为生成的增强过的目标类
Base base = Factory.getInstance(proxy);
base.add();
}
}
自此,cglib动态代理实现的AOP拦截机制已经基本实现,下面我们来看一下拦截的效果如何,程序执行结果如下:
before-------------
add ------------
after--------------
可以看到,在目标类Base的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。
此外,需要说明一下的是,cglib动态代理用到了第三方类库,需要在项目中引入两个jar包:cglib.jar和asm.jar。稍后会在csdn资源中上传这两个jar包。免积分的啊。
jar包和相关代码请见http://download.csdn.net/detail/dreamrealised/6427885

总之,AOP的核心机制和基本功能已经能够通过动态代理来实现了,至于AOP中,如何从配置文档中得到目标类target、advisor的bean,如何判断拦截器类型等问题,就借助于Spring中另一个核心IOC来解决了,后续会有IOC的核心实现机制讲解。

原文链接:https://blog.csdn.net/dreamrealised/java/article/details/12885739

AOP的底层实现-CGLIB动态代理和JDK动态代理的更多相关文章

  1. java代理(静态代理和jdk动态代理以及cglib代理)

    版权声明:本文为Fighter168原创文章,未经允许不得转载.   目录(?)[+]   说到代理,脑袋中浮现一大堆代理相关的名词,代理模式,静态代理,jdk代理,cglib代理等等. 记忆特别深刻 ...

  2. 静态代理和jdk动态代理

    要说动态代理,必须先聊聊静态代理. 静态代理 假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志. 你如何在不修改已有代码的前提下,完成这个需求? 我首先想到的是静态代理.具体做法是: 1 ...

  3. java静态代理和JDK动态代理

    静态代理 编译阶段就生产了对应的代理类 public interface IBussiness { void execute(); } public class BussinessImpl imple ...

  4. Spring学习之设计模式,动态代理和gclib动态代理

    传统的代理模式是静态代理,也就是在方法区域中写入方法. 而动态代理的作用是,不修改实现类的代码,能够在代码的前后或者抛出异常的前后执行某个方法. 动态代理类的实现 //Interface public ...

  5. Java三种代理模式:静态代理、动态代理和cglib代理

    一.代理模式介绍 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 简言之,代理模式就是 ...

  6. Spring 静态代理+JDK动态代理和CGLIB动态代理

    代理分为两种:静态代理 动态代理 静态代理:本质上会在硬盘上创建一个真正的物理类 动态代理:本质上是在内存中构建出一个类. 如果多个类需要进行方法增强,静态代理则需要创建多个物理类,占用磁盘空间.而动 ...

  7. 通过一个工具类更深入理解动态代理和Threadlocal

    动态代理和Threadlocal 一个代理类返回指定的接口,将方法调用指定的调用处理程序的代理类的实例.返回的是一个代理类,由指定的类装载器的定义和实现指定接口指定代理实例调用处理程序最近用到一个工具 ...

  8. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)

    第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...

  9. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理-转载

    内容是摘抄的,不知最初的原作者,见谅 Java 动态代理.具体有如下四步骤: 通过实现 InvocationHandler 接口创建自己的调用处理器: 通过为 Proxy 类指定 ClassLoade ...

随机推荐

  1. Requests方法 -- Token获取操作

    获取token和code流程如下:a.先登陆抓包查看post(提交表单操作)头中是否有token和code关键字b.已知步骤a中出现了token和code,不登录前刷新登陆页面,查看response中 ...

  2. .net core番外第2篇:Autofac的3种依赖注入方式(构造函数注入、属性注入和方法注入),以及在过滤器里面实现依赖注入

    本篇文章接前一篇,建议可以先看前篇文章,再看本文,会有更好的效果. 前一篇跳转链接:https://www.cnblogs.com/weskynet/p/15046999.html 正文: Autof ...

  3. 五个 .NET 性能小贴士

    原文:bit.ly/3wSpO4o 作者:Nikita Starichenko 翻译:精致码农 大家好!今天我想和大家分享几个 .NET 的性能小贴士与基准测试. 我的系统环境: BenchmarkD ...

  4. Skywalking-04:扩展Metric监控信息

    扩展 Metric 监控信息 官方文档 Source and Scope extension for new metrics 案例:JVM Thread 增加 Metrics 修改 Thread 的定 ...

  5. 【阅读笔记】Java核心技术卷一 #6.Chapter8

    8 泛型程序设计 8.1 为什么要使用泛型程序设计 类型参数(type parameters)(E.T.S...) 通配符类型(wildcard type)(?) 注意这两者用法用处并不同. 8.2 ...

  6. 【LeetCode】297. 二叉树的序列化与反序列化

    297. 二叉树的序列化与反序列化 知识点:二叉树:递归 题目描述 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一 ...

  7. etcd raft 处理流程图系列1-raftexample

    最近在看raft相关的代码和实现,发现etcd的raft模块在实现上还是比较灵活的,但缺点就是需要用户实现比较多的功能,如存储和网络等,同时带来的优点就是不会对用户的存储和传输作限制.网上对该模块的描 ...

  8. 使用Python玩转阿里云盘

    项目地址: https://github.com/foyoux/aligo 这个项目起源于我的一个简单需求, 我有25000个文件, 已经上传了9000个, 但是现在我把这些文件重新整理了, 最后我不 ...

  9. SQL Server 行转列 列转行操作

    1.多行转成一行(并以','分开) 表数据如下图: 查询结果如下图: SQL查询脚本: SELECT addPer, house_code = (STUFF((SELECT ',' + house_c ...

  10. GUI编程简介

    GUI编程(淘汰) GUI编程怎么学? 这是什么 它怎么玩 该如何去在我们平时运用 class -- 可阅读 组件 窗口 弹窗 面板 文本框 列表框 按钮 图片 监听事件 鼠标 键盘事件 破解工具 1 ...