从代理模式 到 SpringAOP
前言
Spring AOP 就是通过代理模式来实现切面编程的。代理模式用来为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客户端和目标对象之间起到中介的作用。通过控制对这个对象的访问,可以做一些自己想做的事。比如在AOP中,方法调用前打印请求方的信息,结束时记录用时,便于后续分析。还可以在代理中进行权限校验,将职责进行清晰,一个类负责一件事,也易于分别进行测试。低耦合和对代码进行改造,拓展性好,而不用对目标对象进行改造。
本文会从静态代理模式到动态代理再到SpringAOP来介绍。
1. 静态代理
静态代理代码实现:
// 抽象主题
public interface IBus {
Object create();
}
// 具体主题
public class Bus implements IBus {
@Override
public Bus create() {
System.out.println("give you a bus");
return this;
}
}
// 代理主题角色
public class BusProxy implements IBus {
private String userName;
private Bus bus;
public BusProxy(Bus bus, String userName) {
this.userName = userName;
this.bus = bus;
}
@Override
public Object create() {
if (userName.equals("god")) {
return bus.create();
}
throw new IllegalStateException("you are not a god");
}
}
// 静态工厂 隐藏具体主题
public class Factory {
public static BusProxy getBus(String userName) {
return new BusProxy(new Bus(), userName);
}
}
UML
代理模式中是没有Factory 部分的,是本人加上的。只是为了对于用户隐藏具体对象。静态代理的缺点就是不能复用,每次代理都需要写一个代理类。
2. 动态代理
动态代理和静态代理有什么不一样?动态代理可以复用,静态代理不行。静态代理不同的类都需要写一套。动态代理可以理解为动态生成的。
2.1 JDK 动态代理
通过实现InvocationHandler接口
// 实现 InvocationHandler
public class JDKProxy implements InvocationHandler {
// 具体主题
private Object target;
// 构造器传入 具体主题
public JDKProxy(Object object) {
this.target = object;
}
// 调用者需使用bind() 本质上通过 Proxy.newProxyInstance 获取代理主题
public Object bind() {
Class clazz = target.getClass();
return Proxy.newProxyInstance(
clazz.getClassLoader(),
clazz.getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod();
// 反射执行
Object object = method.invoke(target, args);
afterMethod();
return object;
}
private void beforeMethod() {
System.out.println("before method");
}
private void afterMethod() {
System.out.println("after method");
}
}
// 测试类使用动态代理
public class Test {
public static void main(String[] args) {
IBus bus = (IBus) new JDKProxy(new Bus()).bind();
bus.create();
}
}
动态生成的代理类
源码中Proxy.newProxyInstance中式通过 静态方法ProxyGenrator.genrateClass 动态生成代理类的字节码。
方法中为代理的类"动态地"继承了Proxy 类。(所有生成的动态代理类都是Proxy类的子类)Java不支持多继承,因此JDK动态代理只能代理实现了接口的类,不能代理已经继承了别的类的类。
其中 JDKProxy 定义的是代理行为 而非代理类,而代理类是在运行时通过反射动态的创建得出来的。
2.2 Cglib 动态代理
通过代理类继承目标类的方式来实现实现代理类,无法代理final修饰的方法
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
代码
<!-- maven cglib 依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
public class CGLibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeMethod();
// 方法调用
Object result = methodProxy.invokeSuper(o, objects);
afterMethod();
return result;
}
private void beforeMethod() {
}
private void afterMethod() {
}
}
public class CGLibTest {
public static void main(String[] args) {
// 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(Bus.class);
// 设置回调函数
enhancer.setCallback(new CGLibProxy());
// 这里的creat方法就是正式创建代理类
IBus bus = (IBus) enhancer.create();
// 调用代理类的 create 方法
bus.create();
}
}
Spring 的 AOP
Spring AOP 面向切面编程。就像代码案例中的beforeMethod 和 afterMethod 一样。在 method中进行统一拦截和验证,日志记录,这样关注点进行了分离,职责分离。通用化的代码放在切面上,不再和原来的业务代码耦合。
Spring AOP代码使用
@Aspect
@Component
public class ControllerAspect {
private static final Logger log = LoggerFactory.getLogger(ControllerAspect.class);
// where 切入点
@Pointcut("execution(public * org.cs.backend.pattern.structure.proxy.spring..*.*(..))")
public void webLog() {
}
// when 什么时候切入
@Before("webLog()")
public void before(JoinPoint joinPoint) {
// what 作什么
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("request remote ip: [{}]", request.getRemoteAddr());
}
}
AOP 重点名词:
Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。(共有五种:before,after (无论是否发生异常,都进行执行的通知),around ,afterRunning,afterThrowing)
Target(目标对象):织入 Advice 的目标对象.。
Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
- Advice 的种类:
- 前置通知 Before
- 后置通知 AfterReturning
- 异常通知 AfterThrowing
- 最终通知 After
- 环绕通知 Around
AOP 将业务代码和通用代码分离之后,如何让通用代码在相应的代码上起作用的?(AOP 的三种织入方式)
- 编译时织入:需要特殊的Java编译器 如AspectJ
- 类加载的时候织入:需要特殊的Java编译器,如AspectJ 和ApspectWerkz
- 运行时织入: Spring采用的方式,通过动态代理的方式,实现简单
Spring在使用动态代理时如何选择的?
- 目标类是接口时,Spring就会用JDK的动态代理,否则默认使用Spring使用Cglib代理。
JDK 和 CGLib比较
JDK动态代理生成代理对象速度比cglib快;cglib生成的代理对象比JDK动态代理生成的代理对象执行效率高。
JDK以接口形式接收代理实例,而不是代理类。CGLib以类或接口形式接收代理实例
总结
代理模式就是在原有的对象上,在不影响原有的对象下进行增强。这样可以功职责分明,分别测试。和适配器模式不同的是,适配器模式和原有对象实现的接口是不同的,而代理模式是相同的。
动态代理中 JDK的方式需要目标对象未继承其他类的。而CGLib要求目标对象未被final修饰。
JDK动态代理用Java内部的反射机制,CGLib以继承的方式通过ASM创建字节码。
参考博客
从代理模式 到 SpringAOP的更多相关文章
- springAOP之代理模式
springAOP指的是在spring中的AOP,什么是AOP,相对于java中的面向对象(oop),在面向对象中一些公共的行为,像日志记录,权限验证等如果都使用面向对象来做,会在每个业务方法中都写上 ...
- 带你入门代理模式/SpringAop的运行机制
SpringAop 是spring框架中最重要的一项功能之一,同时也是企业级开发记录事物日志等不可或缺的一部分,如果说你的系统需要记录用户访问接口的操作,那SpringAop是很完美的了,当然,拦截器 ...
- Java静态代理与动态代理模式的实现
前言: 在现实生活中,考虑以下的场景:小王打算要去租房,他相中了一个房子,准备去找房东洽谈相关事宜.但是房东他很忙,平时上班没时间,总找不到时间去找他,他也没办法.后来,房东想了一个办法,他找到 ...
- Spring增强代理模式
1. 依赖注入;(掌握) 2. XML自动注入;(掌握) 3. 全注解配置;(掌握) 4. 代理模式;(掌握,难点) 依赖注入 构造参数注入 constructor-arg:构造器注入: index: ...
- Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景
我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...
- java 代理模式-静态代理与动态代理
最近在研究SpringAOP,当然要学习AOP就要知道这么健硕.强大的功能的背后究竟隐藏着怎样不可告人的“秘密”?? 接下来就是查阅了许多资料详细的研究了一下Java的代理模式,感觉还是非常非常重要的 ...
- java设计模式6——代理模式
java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...
- 大型Java进阶专题(六)设计模式之代理模式
代理模式 前言 又开始我的专题了,又停滞了一段时间了,加油继续吧.都知道 SpringAOP 是用代理模式实现,到底是怎么实现的?我们来一探究竟,并且自己仿真手写还原部分细节. 代理模式的应用 在生活 ...
- 12.java设计模式之代理模式
基本介绍: 代理模式(Proxy)为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,想在 ...
随机推荐
- Vue3.0新特性
Vue3.0新特性 Vue3.0的设计目标可以概括为体积更小.速度更快.加强TypeScript支持.加强API设计一致性.提高自身可维护性.开放更多底层功能. 描述 从Vue2到Vue3在一些比较重 ...
- Docker运行时资源限制
Docker 运行时资源限制Docker 基于 Linux 内核提供的 cgroups 功能,可以限制容器在运行时使用到的资源,比如内存.CPU.块 I/O.网络等. 内存限制概述Docker 提供的 ...
- 一道思维题 &&递归改循环
思路: 比如5 2 12345--> 1245 从3开始,这时候5变成了1.剩下4512,对应1234.只需要找到现在n-1,k中的数对应原来的编号的映射. 比如1-->3 是1+2 mo ...
- 【DP】区间DP入门
在开始之前我要感谢y总,是他精彩的讲解才让我对区间DP有较深的认识. 简介 一般是线性结构上的对区间进行求解最值,计数的动态规划.大致思路是枚举断点,然后对断点两边求取最优解,然后进行合并从而得解. ...
- 快速搞懂.NET 5/.NET Core应用程序的发布部署
.NET Framework时代,.NET 应用程序大多直接部署运行在Windows服务器上,当然也可以通过Mono部署运行在Linux上.无论部署exe,还是IIS站点.或是Windows Serv ...
- git branch All In One
git branch All In One Git Branch Management https://git-scm.com/book/en/v2/Git-Branching-Branch-Mana ...
- css scroll text without wrap & webkit-scrollbar
css scroll text without wrap hidden webkit-scrollbar .tabs-title-box::-webkit-scrollbar, .tabs-conte ...
- Fetch & POST
Fetch & POST fetch( `http://10.1.5.202/deploy/http/send/viewtree`, { method: "POST", m ...
- Baccarat中挖矿、兑换和做市的三角关系是什么?
NGK在这波DeFi潮中,推出了Baccarat,为用户带来了流动性挖矿收益,今天笔者就讲一讲Baccarat中挖矿.兑换和做市的关系. 兑换和做市是什么关系呢?众所周知,换币者,是用一种货币去换另一 ...
- React Portal - 弹出层的优秀解决方案
对于需要使用弹出层的需求 ,Portal可以说是提供了一种完美的解决方案.相比于React Native中的实现更多的使用Modal或者绝对定位,Portal实在是简易友好得多. 场景 对话框,确认提 ...