Spring AOP实现原理-动态代理
代理模式
我们知道,Spring AOP的主要作用就是不通过修改源代码的方式、将非核心功能代码织入来实现对方法的增强。那么Spring AOP的底层如何实现对方法的增强?实现的关键在于使用了代理模式
代理模式的作用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各种问题。
代理主要分为两种方式:静态代理和动态代理
静态代理
静态代理主要通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的
示例代码:
/**
* 代理类与目标类的共同接口
*/
public interface Subject {
void request();
void response();
}
/**
* 目标类
*/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("执行目标对象的request方法......");
}
@Override
public void response() {
System.out.println("执行目标对象的response方法......");
}
}
/**
* 代理类
*/
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void request() {
System.out.println("before 前置增强");
subject.request();
System.out.println("after 后置增强");
}
@Override
public void response() {
System.out.println("before 前置增强");
subject.response();
System.out.println("after 后置增强");
}
}
public class Main {
public static void main(String[] args) {
//目标对象
Subject realSubject = new RealSubject();
//代理对象 通过构造器注入目标对象
Subject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
proxySubject.response();
}
}
运行结果:
before 前置增强
执行目标对象的request方法......
after 后置增强
before 前置增强
执行目标对象的response方法......
after 后置增强
通过以上的代码示例,我们不难发现静态代理的缺点。假如我们的Subject接口要增加其它的方法,则ProxySubject代理类也必须同时代理这些新增的方法。同时我们也看到,request方法和response方法所织入的代码是一样的,这会使得代理类中出现大量冗余的代码,非常不利于扩展和维护。为了解决静态代理的这些缺陷,于是有了动态代理
动态代理
与静态代理相比,动态代理的代理类不需要程序员自己手动定义,而是在程序运行时动态生成
动态代理可以分为JDK动态代理和CgLib动态代理
JDK动态代理
JDK动态代理与静态代理一样,目标类需要实现一个代理接口,它的开发步骤如下:
1.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
2.将InvocationHandler对象作为参数传入java.lang.reflect.Proxy的newProxyInstance方法中
3.通过调用java.lang.reflect.Proxy的newProxyInstance方法获得动态代理对象
4.通过代理对象调用目标方法
示例代码:
/**
* 自定义InvocationHandler的实现类
*/
public class JdkProxySubject implements InvocationHandler {
private Subject subject;
public JdkProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before 前置通知");
Object result = null;
try {
result = method.invoke(subject, args);
}catch (Exception ex) {
System.out.println("ex: " + ex.getMessage());
throw ex;
}finally {
System.out.println("after 后置通知");
}
return result;
}
}
public class Main {
public static void main(String[] args) {
//获取InvocationHandler对象 在构造方法中注入目标对象
InvocationHandler handler = new JdkProxySubject(new RealSubject());
//获取代理类对象
Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);
//调用目标方法
proxySubject.request();
proxySubject.response();
}
}
运行结果:
before 前置通知
执行目标对象的request方法......
after 后置通知
before 前置通知
执行目标对象的response方法......
after 后置通知
CgLib动态代理
CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理。它的开发步骤:
1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法
2.获取org.springframework.cglib.proxy.Enhancer类的对象
3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象
4.通过代理对象调用目标方法
示例代码:
/**
* 自定义MethodInterceptor实现类
*/
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before 前置通知");
Object result = null;
try {
result = methodProxy.invokeSuper(obj, args);
}catch (Exception ex) {
System.out.println("ex: " + ex.getMessage());
throw ex;
}finally {
System.out.println("after 后置通知");
}
return result;
}
}
public class Main {
public static void main(String[] args) {
//获取Enhancer 对象
Enhancer enhancer = new Enhancer();
//设置代理类的父类(目标类)
enhancer.setSuperclass(RealSubject.class);
//设置回调方法
enhancer.setCallback(new MyMethodInterceptor());
//获取代理对象
Subject proxySubject = (Subject)enhancer.create();
//调用目标方法
proxySubject.request();
proxySubject.response();
}
}
before 前置通知
执行目标对象的request方法......
after 后置通知
before 前置通知
执行目标对象的response方法......
after 后置通知
两种代理的区别
JDK动态代理和CgLib动态代理的主要区别:
JDK动态代理只能针对实现了接口的类的接口方法进行代理
CgLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理
Spring AOP的代理
Spring AOP中的代理使用的默认策略是:
如果目标对象实现了接口,则默认采用JDK动态代理
如果目标对象没有实现接口,则采用CgLib进行动态代理
如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理
关注公众号githubcn,免费获取更多学习视频教程

Spring AOP实现原理-动态代理的更多相关文章
- Spring AOP中的动态代理
0 前言 1 动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2 Spring AOP中的动态代理机制 2.1 ...
- 转:Spring AOP中的动态代理
原文链接:Spring AOP中的动态代理 0 前言 1 动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2 S ...
- spring---aop(4)---Spring AOP的CGLIB动态代理
写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...
- Spring Boot实践——Spring AOP实现之动态代理
Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践——AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...
- 新秀学习SSH(十四)——Spring集装箱AOP其原理——动态代理
之前写了一篇文章IOC该博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...
- Spring AOP关于cglib动态代理
一: Spring AOP的默认代理方式是jdk动态代理,还有另外一种代理方式是cglib代理,简单说前者基于接口,后者基于继承,基本思路是将被代理对象的类作为父类,然后创建子类来进行方法的调用,调用 ...
- spring---aop(2)---Spring AOP的JDK动态代理
写在前面 spring 事务是springAOP 的一个实现.我们以分析spring的事务,来分析spring的AOP实现. 基本知识 如果目标方法被spring的事务声明,则执行该目标方法的对象就会 ...
- Spring AOP系列(二) — 动态代理引言
接上一篇Spring AOP系列(一)- 代理模式,本篇来聊聊动态代理. 动态代理与静态代理的区别 要想了解动态代理与静态代理的区别,需要有两个前置知识点:java程序是如何执行的以及类加载机制. j ...
- Spring AOP系列(三) — 动态代理之JDK动态代理
JDK动态代理 JDK动态代理核心是两个类:InvocationHandler和Proxy 举个栗子 为便于理解,首先看一个例子: 希望实现这样一个功能:使用UserService时,只需关注自己的核 ...
随机推荐
- MT【181】横穿四象限
设函数$f(x)=\dfrac{1}{x-a}-\dfrac{\lambda}{x-2}$,其中$a,\lambda\in R$记$A_1=\{(x,y)|x>0,y>0\},A_2=\{ ...
- 【刷题】BZOJ 1924 [Sdoi2010]所驼门王的宝藏
Description Input 第一行给出三个正整数 N, R, C. 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室 ...
- 自学Aruba1.1-WLAN一些基本常识
点击返回:自学Aruba之路 自学Aruba1.1-WLAN一些基本常识 1. LAN.WAN.WLAN.WIFI术语 1.1 局域网(Local Area Network,LAN) 是指在某一区域内 ...
- 解题:HNOI 2013 Cards
题面 除了不洗牌以外,每种洗牌方式的每个循环里的颜色必须一样,然后大力背包一下就好了.最后记得把不洗牌的方案也算进去 #include<cstdio> #include<cstrin ...
- Linux SSH免登陆配置步骤
[jans@centos2 ~]# cd ~[jans@centos2 ~]# ssh-keygen -t rsa //生成rsa加密的公钥和密钥[jans@centos2 ~]# cat .ssh ...
- 使用ImageMagick 在图片上绘制粗斜体的中文也许是一个错误。
测试发现: ImageMagick使用中文字体,在图片上绘制带粗或斜体的中文,看不到效果. 如果使用英文字体,绘制粗或斜体的英文,99%都有效果. 今天无意看到一篇文章提到: convert -lis ...
- centos7下设置opencv环境变量
最近要装YOLO,但是MAKE的时候总是找不到OPENCV的路径, 原因是:我以前卸载过一次OPENCV,然后自己重新安装了opencv2.4.10, 因为当时只在QT 中用,所以编译完也没有设置环 ...
- sso接口的调用
之前一直想sso接口已经写好了,登录注册功能是怎么调用的呢?原来在登录注册的jsp页面实现的接口的调用,页面的校验和验证功能在jsp页面即可实现. 注册页面: <%@ page language ...
- ELK应用之二:Kibana显示Nginx中来访客户端IP地域分布
在Kibana的visualize中显示Nginx访问日志客户端IP地域分布图 官网介绍: https://www.elastic.co/guide/en/beats/packetbeat/curre ...
- 使用 maven 搭建web开发基本架构
我觉得可能,对于还没有使用过 IDEA 进行开发的童鞋来说,直接撸代码是有些尴尬的.那么我会把示例代码之前的那些事再在这里写一遍 按图步骤进行即可进行基本项目结构搭建 现在基本流行 maven 管理项 ...