【Java】代处理?代理模式 - 静态代理,动态代理
当我们需要在一个方法之前或之后添加一段逻辑时,自然会想到使用代理类。代理类帮我们代理了实际类的调用,然后可以在实际调用之前和之后添加一些逻辑,从而不浸入实际类。
拓展:由于代理类能在实际类调用之前和之后添加逻辑,那么可做的事情就多了,常见的有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】代处理?代理模式 - 静态代理,动态代理的更多相关文章
- Spring代理模式(jdk动态代理模式)
有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...
- 代理模式及jdk动态代理原理
代理模式 :为其它对象提供代理,以控制对这个对象的访问. 代理模式的特征:代理类(proxyClass)与委托类(realClass)有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转 ...
- 代理模式之cglib动态代理
上一篇博客说了实现InvocationHandler接口的jdk动态代理,还有一种实现动态代理的方式则是:通过继承的方式实现的cglib动态代理. 先在程序中导入cglib的包,cglib-nodep ...
- java 代理模式二:动态代理
java动态代理: java动态代理类位于java.lang.reflect包下,一般主要涉及两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法:Objec ...
- Spring代理模式(CGLIB动态代理模式)
jdk动态代理和CGLIB动态代理 没什么太大的区别,CGLIB动态代理不需要接口,但是需要导入jar包. 房东出租房子的方法: package com.bjsxt.proxy2; public cl ...
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
- 代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
- java静态和动态代理原理
一.代理概念 为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代.代理类负责请求的预处理.过滤.将请求分派给委托类 ...
- 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...
随机推荐
- json对象与json字符串对象格式
var cStr = "{\"c\":\"{\\\"b\\\":\\\"000\\\",\\\"b2\\\&q ...
- Java的析构函数System的finalize()
一个对象是由产生 到使用 到销毁的过程 即C++中 构造函数-> body->析构函数 在Java之中为了回收不需要的空间可以使用System类的finalize() class A{ p ...
- GFS文件系统和在RedHat Linux下的配置
GFS的全称是Google file System,为了满足Google迅速增长的数据处理要求,Google设计并实现的Google文件系统(GFS).Google文件系统是一个可扩展的分布式文件系统 ...
- uploadify 报错集锦
1.404 : 没有路由 检查 路由的大小写 或者 拼写 2.500: linux 没有读写权限
- wpf 将Style应用到 ListView 中的 ListViewItem 元素
例: 为每个条目元素设置右键菜单 1. 新建右键菜单元素和样式元素 注意: 同时设置样式元素的 TargetType 属性和 x:Key 属性, 将样式元素限定为应用于 ListViewItem 类型 ...
- iOS下Html页面中input获取焦点弹出键盘时挡住input解决方案—scrollIntoView()
问题描述 iOS系统下,移动web页面,inpu获取焦点弹出系统虚拟键盘时,偶尔会出现挡住input的情况,尽管概率不大,但是十分影响用户体验. 问题重现 原始页面:页面中有header.main.f ...
- iOS应用架构谈 view层的组织和调用方案
当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们 ...
- java求素数和求一个数的一个正整数的质因数
1.题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少对? (1)程序分析:不难发现兔子的规律是:1,1 ...
- python file模块 替换输入内容脚本
root@python-10:/home/liujianzuo/python/test# ls passwd rc.local test1 root@python-10:/home/liujianzu ...
- curl 查看一个web站点的响应时间(rt)
1. curl 查看web站点rt curl -o /dev/null -s -w %{time_namelookup}::%{time_connect}::%{time_starttransfer} ...