【Java】代理模式,静态代理和动态代理(基于JDK或CGLib)
当我们需要在一个方法之前或之后添加一段逻辑时,自然会想到使用代理类。代理类帮我们代理了实际类的调用,然后可以在实际调用之前和之后添加一些逻辑,从而不浸入实际类。
拓展:由于代理类能在实际类调用之前和之后添加逻辑,那么可做的事情就多了,常见的有4种,用AOP的术语描述就是:
- 前置增强:在实际方法前添加逻辑。比如,在方法执行前打印入参;在方法执行前判断用户是否有执行此方法的权限
- 后置增强:在实际方法后添加逻辑。比如,在方法执行后打印结果
- 环绕增强:在实际方法之前和之后都添加逻辑。
- 抛出增强:当实际方法发生异常时执行添加的逻辑。
不用代理
有时,需要在一些方法前后都打印一些日志。
这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果。(至于为什么不直接用+号运算,见【Java】Float计算不准确)
package com.nicchagil.study.java.demo.No09代理.No01不用代理; import java.math.BigDecimal; public class FloatCalculator { public float add(float a, float b) { BigDecimal b1 = new BigDecimal(a + "");
BigDecimal b2 = new BigDecimal(b + "");
float f = b1.add(b2).floatValue(); return f; }
}
我想在它运行前后打印,最直接的方式就是调用时打印了
package com.nicchagil.study.java.demo.No09代理.No01不用代理; public class Call { public static void main(String[] args) {
float f1 = 1f;
float f2 = 1f; System.out.println("f1 -> " + f1 + ", f2 -> " + f2);
float result = new FloatCalculator().add(f1, f2);
System.out.println("result -> " + result);
}
}
看到这日志,我很欣慰!
f1 -> 1.0, f2 -> 1.0
result -> 2.0
静态代理
随着项目变大,调用此方法的地方变得越来越多,如果有10个调用的地方,我岂不是要写100次打印的方法。
这时,静态代理的方式能帮助我们。
定义个接口
package com.nicchagil.study.java.demo.No09代理.No02静态代理; public interface ICalculator { /**
* <p>add</p>
*/
public float add(float a, float b); }
真实业务类
package com.nicchagil.study.java.demo.No09代理.No02静态代理; import java.math.BigDecimal; public class FloatCalculator implements ICalculator { @Override
public float add(float a, float b) { BigDecimal b1 = new BigDecimal(a + "");
BigDecimal b2 = new BigDecimal(b + "");
float f = b1.add(b2).floatValue(); return f; } }
代理类,这个类中,处理执行实际业务,还一并捆绑打印日志的任务
package com.nicchagil.study.java.demo.No09代理.No02静态代理; public class FloatCalculatorProxy implements ICalculator { ICalculator c = null; /**
* 构造方法
* @param c 需被代理的对象
*/
public FloatCalculatorProxy(ICalculator c) {
super();
this.c = c;
} @Override
public float add(float f1, float f2) {
System.out.println("f1 -> " + f1 + ", f2 -> " + f2); float result = this.c.add(f1, f2); System.out.println("result -> " + result);
return result;
} }
然后,我们调用时,只需调用代理类,不仅计算得结果,日志也乖乖地出来了
package com.nicchagil.study.java.demo.No09代理.No02静态代理; public class Call { public static void main(String[] args) {
System.out.println("代理的对象:");
ICalculator c2 = new FloatCalculatorProxy(new FloatCalculator());
c2.add(1f, 1f);
} }
看到日志,我很镇静
代理的对象:
f1 -> 1.0, f2 -> 1.0
result -> 2.0
动态代理
JDK基于接口的动态代理
如果现在不仅FloatCalculator这个类需要打印日志,还有其他各种类也需要打印日志,那么我们岂不是要写好多个代理类了?
这时需要使用动态代理。JDK有提供动态代理的实现,通过反射机制为我们实现动态代理。
接口类(ICalculator)、真实业务类(FloatCalculator)如同静态代理,不再重复
调用处理类。这个类实现InvocationHandler,主要任务是:
- 注入被调用对象
- 调用被调用对象的对应方法(在调用方法前后可自行添加逻辑)
package com.nicchagil.study.java.demo.No09代理.No03动态代理; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class DynamicProxyHandler implements InvocationHandler { private Object proxied = null; public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Clazz -> " + proxy.getClass());
System.out.println("method -> " + method);
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] -> " + args[i]);
} Object result = method.invoke(proxied, args); System.out.println("result -> " + (result != null ? result.toString() : ""));
return result;
} }
调用类。这里通过Proxy的newProxyInstance方法生成代理类,这个方法的入参有3个:
- 被代理类的类加载器
- 被代理类实现的接口(这也是JDK动态代理的缺点之一,需实现接口。基于此,Spring的AOP在类有实现接口时,使用JDK动态代理,无实现接口时,使用CGlib)
- 被代理对象
package com.nicchagil.study.java.demo.No09代理.No03动态代理; import java.lang.reflect.Proxy; import com.nicchagil.study.java.demo.No09代理.No02静态代理.FloatCalculator;
import com.nicchagil.study.java.demo.No09代理.No02静态代理.ICalculator; public class Call { public static void main(String[] args) {
/* 代理的对象 */
System.out.println("代理的对象:");
ICalculator c2 = (ICalculator)Proxy.newProxyInstance(ICalculator.class.getClassLoader(),
new Class[] {ICalculator.class}, new DynamicProxyHandler(new FloatCalculator()));
c2.add(1f, 1f);
} }
日志
代理的对象:
Clazz -> class $Proxy0
method -> public abstract float com.nicchagil.study.java.demo.No09代理.No02静态代理.ICalculator.add(float,float)
args[0] -> 1.0
args[1] -> 1.0
result -> 2.0
在main方法加入如下代码可生成动态代理类的代码:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
你会在com.sun.proxy包下发现动态生成的代理类:
package com.sun.proxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements ICalculator {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0; public $Proxy0(InvocationHandler var1) throws {
super(var1);
} public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
} public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} public final float add(float var1, float var2) throws {
try {
return (Float)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
} public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("ICalculator").getMethod("add", Float.TYPE, Float.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
查看代码,你会发现:
- extends Proxy implements ICalculator
- 有私有的、静态的Method变量,在静态代码块用反射赋予变量具体的引用
- 构造方法的入参为InvocationHandler对象,在本例中就是DynamicProxyHandler对象,也就是具体的方法中的super.h,本代理中会直接调用之前我们重写的invoke方法
- 实现ICalculator的各方法,实现体为用反射调用Method变量、InvocationHandler对象
super.h就是Proxy中的InvocationHandler,构造方法如下:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
CGLib动态代理
JDK动态代理有个缺点,只能对实现了接口的类进行代理,如果目标类没有实现接口,我们可以使用CGLib。
引入相关包:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
定义了需被代理的UserService,方法拦截器类,和main方法:
package com.nicchagil.exercise.cglib; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;
import java.util.Arrays; public class Call { public static void main(String[] args) {
UserService userServiceProxy = (UserService) Enhancer.create(UserService.class, new MyMethodInterceptor()); userServiceProxy.getById("123");
userServiceProxy.getById("Nick Huang");
} /**
* 测试的方法拦截器
*/
static class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before logic, parameter : " + Arrays.toString(objects));
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after logic, result : " + result); return result;
}
} /**
* User业务类
*/
static class UserService { public Object getById(String id) {
System.out.println("getById");
return new Object();
} public Object getByName(String name) {
System.out.println("getByName");
return new Object();
}
} }
日志:
before logic, parameter : [123]
getById
after logic, result : java.lang.Object@573fd745
before logic, parameter : [Nick Huang]
getById
after logic, result : java.lang.Object@15327b79
【Java】代理模式,静态代理和动态代理(基于JDK或CGLib)的更多相关文章
- 代理模式(Proxy)--动态代理(CGLIB)
上一篇:代理模式(Proxy)--动态代理(jdk) (1)CGLIB技术是第三方代理技术,可以对任何类生成代理,代理的原则是对目标对象进行继承代理 (2)如果目标对象被final修饰,则无法被CGL ...
- 代理模式(Proxy)--动态代理(JDK)
在是上一篇博客中实现了静态代理. 在上篇的结尾提到了一个问题: 思考:如果我们下需要对火车,自行车实现相同的代理,我们又该如何实现呢? 这篇博客就来解决这个问题: 解决这类问题需要用到动态代理技术,实 ...
- 【Java】代理模式、反射机制-动态代理
关于代理模式和动态代理参考自:https://www.cnblogs.com/gonjan-blog/p/6685611.html 这里通过参考博客中的例子整理个人理解. 代理模式: 访问某个类的方法 ...
- Java 代理模式 (二) 动态代理
代理模式 代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式:即通过代理访问目标对象. 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作.(扩展目标对象的功能). 代理模式的 ...
- java 代理模式-静态代理与动态代理
最近在研究SpringAOP,当然要学习AOP就要知道这么健硕.强大的功能的背后究竟隐藏着怎样不可告人的“秘密”?? 接下来就是查阅了许多资料详细的研究了一下Java的代理模式,感觉还是非常非常重要的 ...
- 代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
- java 设计模式之单利模式以及代理模式(静态)
1:单利模式: public class Singleton { private static Singleton uniqueInstance = null; private Singleton() ...
- Java反射,注解,以及动态代理
Java反射,注解,以及动态代理 基础 最近在准备实习面试,被学长问到了Java反射,注解和动态代理的内容,发现有点自己有点懵,这几天查了很多资料,就来说下自己的理解吧[如有错误,望指正] Java ...
- Java动态代理:一个面包店的动态代理帝国
文章首发于[博客园-陈树义],点击跳转到原文大白话说Java动态代理:一个面包店的动态代理帝国 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中, ...
- java 反射提取类信息, 动态代理 和过滤某些方法演示
java 反射提取类信息, 动态代理 和过滤某些方法演示 package org.rui.classts.reflects; import java.lang.reflect.Constructor; ...
随机推荐
- SqlServer数据库分离与附加
SQL Server提供了“分离/附加”数据库.“备份/还原”数据库.复制数据库等多种数据库的备份和恢复方法.这里介绍一种学习中常用的“分离/附加”方法,类似于大家熟悉的“文件拷贝”方法,即把数据库文 ...
- select的placeholder和分组效果
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...
- 解决RMI 客户端异常no security manager: RMI class loader disabled
解决方法: 客户端和服务端的Service包名改一致 ok!!
- cannot use 'throw' with exceptions disabled(转)
在为 DragonBonesCPP/refactoring 的 cocos2d-x-3.2 demo 增加 Android 编译时,NDK 报了一个编译错误: error: cannot use ‘t ...
- [Done]SnowFlake 分布式环境下基于ZK构WorkId
Twitter 的 Snowflake 大家应该都熟悉的,先上个图: 时间戳 序列号一般不会去改造,主要是工作机器id,大家会进行相关改造,我厂对工作机器进行了如下改造(估计大家都差不多吧,囧~~~ ...
- webservice系统学习笔记2-使用jdk的命令生成本地代码
使用jdk自带的命令wsimport生成远程服务的本地代码 C:\Documents and Settings\Administrator>wsimport -d E:\mhWorkspace\ ...
- C#编写的 8种初级+高级排序方法(转)
摘自:http://blog.csdn.net/mevin/article/details/6714520 程序代码: view plaincopy to clipboard using System ...
- springmvc异常统一处理
http://www.cnblogs.com/xd502djj/archive/2012/09/24/2700490.html
- 解释-DNS,A记录,CNAME记录,域名转向,SRV记录,TTL值,泛域名与泛解析,域名绑定
http://www.lihongye.net/post/dns.html DNS DNS,Domain Name System或者Domain Name Service(域名系统或者域名服务).域名 ...
- MVC笔记 Controller相关技术
一.Controller的责任 MVC的核心就是Controller(控制器),它负责处理浏览器传送过来的所有请求,并决定要将什么内容响应给浏览器.但Controller并不负责决定内容应该如何显示, ...