当我们需要在一个方法之前或之后添加一段逻辑时,自然会想到使用代理类。代理类帮我们代理了实际类的调用,然后可以在实际调用之前和之后添加一些逻辑,从而不浸入实际类。

拓展:由于代理类能在实际类调用之前和之后添加逻辑,那么可做的事情就多了,常见的有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());
}
}
}

查看代码,你会发现:

  1. extends Proxy implements ICalculator
  2. 有私有的、静态的Method变量,在静态代码块用反射赋予变量具体的引用
  3. 构造方法的入参为InvocationHandler对象,在本例中就是DynamicProxyHandler对象,也就是具体的方法中的super.h,本代理中会直接调用之前我们重写的invoke方法
  4. 实现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】代处理?代理模式 - 静态代理,动态代理的更多相关文章

  1. Spring代理模式(jdk动态代理模式)

    有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...

  2. 代理模式及jdk动态代理原理

    代理模式 :为其它对象提供代理,以控制对这个对象的访问. 代理模式的特征:代理类(proxyClass)与委托类(realClass)有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转 ...

  3. 代理模式之cglib动态代理

    上一篇博客说了实现InvocationHandler接口的jdk动态代理,还有一种实现动态代理的方式则是:通过继承的方式实现的cglib动态代理. 先在程序中导入cglib的包,cglib-nodep ...

  4. java 代理模式二:动态代理

    java动态代理: java动态代理类位于java.lang.reflect包下,一般主要涉及两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法:Objec ...

  5. Spring代理模式(CGLIB动态代理模式)

    jdk动态代理和CGLIB动态代理 没什么太大的区别,CGLIB动态代理不需要接口,但是需要导入jar包. 房东出租房子的方法: package com.bjsxt.proxy2; public cl ...

  6. Java代理模式/静态代理/动态代理

    代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...

  7. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  8. 代理模式 静态代理、JDK动态代理、Cglib动态代理

    1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...

  9. java静态和动态代理原理

    一.代理概念 为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代.代理类负责请求的预处理.过滤.将请求分派给委托类 ...

  10. 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理

    本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...

随机推荐

  1. stdout( 标准输出流)和 stderr( 标准输入流) 重定向问题

    我想把提示信息和错误信息列表都写入到同一个文件里, /* ** 设置流, 使输出与错误流都指向指定的文件 */ if( (output1 = freopen( PATH, "a", ...

  2. BizTalk 2013 Beta 新特性介绍

    BizTalk2013 Beta于2012年11月6日发布,下面来介绍一下BizTalk 2013 Beta的新特性. [核心更新功能] 1. 与云服务集成 BizTalk 2013 Server提供 ...

  3. php课程---文件操作及文件上传的代码总结

    php里面文件包含目录和文件两种 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "h ...

  4. Java程序设计 实验五

    实     验    报     告 课程:Java 班级: 1353    姓名:李海空   学号:20135329 成绩:              指导教师:娄嘉鹏   实验日期:2015.6. ...

  5. Array-基本功能

    <title>Array-基本功能</title></head> <body> <script type="text/javascrip ...

  6. 【iCore3 双核心板】例程十五:USB_CDC实验——高速数据传输

    实验指导书及代码包下载: http://pan.baidu.com/s/1gemYjz9 iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  7. bootstrap插件学习

    转自http://v3.bootcss.com/javascript/ bootstrap javascript插件学习 模态框 打开模态框 <button type="button& ...

  8. git -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree fetch origin

    git -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree fetch origi ...

  9. 对于指定区块div,如何区分区块内的点击 和 区块外的点击?

    需求:对于区块div内点击事件, 需要展示区块内的附属操作区块,对于区块外的点击, 需要将前面说的附属操作区块隐藏掉. 分析:对于一般的HTML控件,有标准的js事件接口, focus和blur,来实 ...

  10. 创建XMLHttpRequest对象方法

    ~~~ //创建XMLHttpRequest对象 function creatXmlHttpRequest() { var xmlHttp; try{ //非IE浏览器使用 xmlHttp = new ...