时间记录:2019-4-13
学习代理proxy
首先同样的什么是代理?代理怎么用?代理在什么样子的情况下使用?代理怎么实现呢?代理的优势在哪里呢?
一:什么是代理
代理从字面上看去就是代替处理的意思,就是代替某种东西去做某事。比如我们上学时候的交学费,不用自己去交,总是银行直接把你的钱勾走了,就不用自己去交。实际上是银行代理了你进行交学费,银行再把钱给相关机构。这里我们发现了一个点学生和银行都具有交学费的功能,故银行代理了学生交学费。
这样的方式存在:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加 自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
二:使用代理及实现
代理分为动态代理和静态代理,这里的动态代理我们介绍下proxy和cglib下的使用和比较。
1:静态代理
定义抽象的角色
package com.huo.proxy;
/**
* 代理的抽象角色
* @author huoruilin
*/
public interface AbstractStudent
{
/**
* 交学费的抽象行为
*/
public void tuitionFee();
}
定义真实角色
package com.huo.proxy;
/**
* 真实的角色
* @author huoruilin
*/
public class ActualStudent implements AbstractStudent
{
@Override
public void tuitionFee()
{
System.out.println("交学费");
}
}
定义代理类
package com.huo.proxy;
/**
* 代理对象
* @author huoruilin
*/
public class ProxyBank implements AbstractStudent
{
/**
* 被代理的对象
*/
private AbstractStudent abstractStudent;
/**
* cons
*/
public ProxyBank(AbstractStudent abstractStudent)
{
this.abstractStudent = abstractStudent;
}
@Override
public void tuitionFee()
{
long time1 = System.currentTimeMillis();
System.out.println("Time: "+time1);
abstractStudent.tuitionFee();
long time2 = System.currentTimeMillis();
System.out.println("Time: "+time2);
System.out.println("消耗时间:"+(time2-time1));
}
}
测试
package com.huo.proxy;
import java.lang.reflect.Proxy;
/**
* test for proxy
* @author huoruilin
*/
public class TestProxy
{
public static void main(String[] args)
{
AbstractStudent abstractStudent = new ActualStudent();
ProxyBank proxyBank = new ProxyBank(abstractStudent);
proxyBank.tuitionFee();
}
}
输出结果
Time: 1555142890645
交学费
Time: 1555142890645
消耗时间:0
思考:这种的静态代理是将实际的角色的操作转交给特定的处理器,那么此处理器的处理类就是将抽象接口实现。那么我们在处理器里面可以在具体的实现内容上面添加自己一些特定操作,比如说计时的操作,aop中的一些日志系统等。
2:动态代理
ActualStudent AbstractStudent 不动
jdk中的proxy代理
package com.huo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 代理对象
* @author huoruilin
*/
public class ProxyBank implements InvocationHandler
{
/**
* 被代理的对象
*/
private AbstractStudent abstractStudent;
/**
* cons
*/
public ProxyBank(AbstractStudent abstractStudent)
{
this.abstractStudent = abstractStudent;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
long time1 = System.currentTimeMillis();
System.out.println("Time: "+time1);
Object object = method.invoke(abstractStudent, args);
long time2 = System.currentTimeMillis();
System.out.println("Time: "+time2);
System.out.println("消耗时间:"+(time2-time1));
return object;
}
}
package com.huo.proxy;
import java.lang.reflect.Proxy;
/**
* test for proxy
* @author huoruilin
*/
public class TestProxy
{
public static void main(String[] args)
{
AbstractStudent abstractStudent = (AbstractStudent) Proxy.newProxyInstance(
AbstractStudent.class.getClassLoader(),
new Class[] {AbstractStudent.class}, new ProxyBank(new ActualStudent()));
abstractStudent.tuitionFee();
}
}
输出结果
Time: 1555232826429
交学费
Time: 1555232826430
消耗时间:1
思考:这种的动态代理需要的是相同的接口和接口的实现,然后通过反射的invoke执行对应的方法,不需要写太多的代码对比静态代理来说,那么我们可想而至的是这种的代理实现必须有相同的接口内容,那么我们如果是单一的类呢 没有什么固定的接口 那么我们如何去实现代理呢,如何对执行的方法进行拦截呢?我们想到了是编译时期的code注入,即修改编译得到的class执行文件,目前我知道的是asm的框架,在cglib中使用。
cglib中使用的代理
cglib项目链接,具体的内容可以学习
cglib文档,个人觉得挺好的
package com.huo.proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyBank implements MethodInterceptor
{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
{
long time1 = System.currentTimeMillis();
System.out.println("Time: "+time1);
Object object = proxy.invokeSuper(obj, args);
long time2 = System.currentTimeMillis();
System.out.println("Time: "+time2);
System.out.println("消耗时间:"+(time2-time1));
return object;
}
}
package com.huo.proxy.cglib;
import com.huo.proxy.ActualStudent;
import net.sf.cglib.proxy.Enhancer;
public class TestProxy
{
public static void main(String[] args)
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ActualStudent.class);
enhancer.setCallback(new ProxyBank());
ActualStudent actualStudent = (ActualStudent) enhancer.create();
actualStudent.tuitionFee();
}
}
输出结果
Time: 1555851882266
交学费
Time: 1555851882285
消耗时间:19
注意:这里使用版本为 cglib-3.2.10 、asm7.0 具体版本可以自己从cglib-parent的pom中查看
对intercept方法调用传入的实例obj具体是什么呢?进行debug可得到如下
com.huo.proxy.ActualStudent$$EnhancerByCGLIB$$c33d6600@5ae9a829
那么我们如何查看这个类呢?是不是自行进行编译时期的修改,参考proxy的代理类?那么这个是不是代理类呢?利用提供的DebuggingClassWriter.
package com.huo.proxy.cglib;
import com.huo.proxy.ActualStudent;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class TestProxy
{
public static void main(String[] args)
{
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, TestProxy.class.getResource(".").getPath());
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ActualStudent.class);
enhancer.setCallback(new ProxyBank());
ActualStudent actualStudent = (ActualStudent) enhancer.create();
actualStudent.tuitionFee();
}
}
输出结果
CGLIB debugging enabled, writing to '/E:/study/Test-study/bin/com/huo/proxy/cglib/'
Time: 1555852621014
交学费
Time: 1555852621096
消耗时间:82
我们去文件夹下查看发现多了几个文件 我们打开obj对应的名称的class文件如下
建议使用Luyten反编译查看,当然也可以通过反编译命令查看
package com.huo.proxy;
import java.lang.reflect.*;
import net.sf.cglib.proxy.*;
import net.sf.cglib.core.*;
public class ActualStudent$$EnhancerByCGLIB$$c33d6600 extends ActualStudent implements Factory
{
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$tuitionFee$0$Method;
private static final MethodProxy CGLIB$tuitionFee$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("com.huo.proxy.ActualStudent$$EnhancerByCGLIB$$c33d6600");
final Class<?> forName2;
CGLIB$tuitionFee$0$Method = ReflectUtils.findMethods(new String[] { "tuitionFee", "()V" }, (forName2 = Class.forName("com.huo.proxy.ActualStudent")).getDeclaredMethods())[0];
CGLIB$tuitionFee$0$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()V", "tuitionFee", "CGLIB$tuitionFee$0");
final Class<?> forName3;
final Method[] methods = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName3 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = methods[0];
CGLIB$equals$1$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = methods[1];
CGLIB$toString$2$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = methods[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = methods[3];
CGLIB$clone$4$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
}
final void CGLIB$tuitionFee$0() {
super.tuitionFee();
}
public final void tuitionFee() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$tuitionFee$0$Method, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$emptyArgs, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$tuitionFee$0$Proxy);
return;
}
super.tuitionFee();
}
final boolean CGLIB$equals$1(final Object o) {
return super.equals(o);
}
public final boolean equals(final Object o) {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
final Object intercept = cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$equals$1$Method, new Object[] { o }, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$equals$1$Proxy);
return intercept != null && (boolean)intercept;
}
return super.equals(o);
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
return (String)cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$toString$2$Method, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$emptyArgs, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$toString$2$Proxy);
}
return super.toString();
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
final Object intercept = cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$hashCode$3$Method, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$emptyArgs, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$hashCode$3$Proxy);
return (intercept == null) ? 0 : ((Number)intercept).intValue();
}
return super.hashCode();
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
return cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$clone$4$Method, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$emptyArgs, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$clone$4$Proxy);
}
return super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(final Signature signature) {
final String string = signature.toString();
switch (string.hashCode()) {
case -508378822: {
if (string.equals("clone()Ljava/lang/Object;")) {
return ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$clone$4$Proxy;
}
break;
}
case 1826985398: {
if (string.equals("equals(Ljava/lang/Object;)Z")) {
return ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$equals$1$Proxy;
}
break;
}
case 1836487275: {
if (string.equals("tuitionFee()V")) {
return ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$tuitionFee$0$Proxy;
}
break;
}
case 1913648695: {
if (string.equals("toString()Ljava/lang/String;")) {
return ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$toString$2$Proxy;
}
break;
}
case 1984935277: {
if (string.equals("hashCode()I")) {
return ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$hashCode$3$Proxy;
}
break;
}
}
return null;
}
public ActualStudent$$EnhancerByCGLIB$$c33d6600() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(final Callback[] array) {
ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$THREAD_CALLBACKS.set(array);
}
public static void CGLIB$SET_STATIC_CALLBACKS(final Callback[] cglib$STATIC_CALLBACKS) {
CGLIB$STATIC_CALLBACKS = cglib$STATIC_CALLBACKS;
}
private static final void CGLIB$BIND_CALLBACKS(final Object o) {
final ActualStudent$$EnhancerByCGLIB$$c33d6600 actualStudent$$EnhancerByCGLIB$$c33d6600 = (ActualStudent$$EnhancerByCGLIB$$c33d6600)o;
if (!actualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$BOUND) {
actualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$BOUND = true;
Object o2;
if ((o2 = ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$STATIC_CALLBACKS) != null) {
actualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
}
}
}
public Object newInstance(final Callback[] array) {
CGLIB$SET_THREAD_CALLBACKS(array);
final ActualStudent$$EnhancerByCGLIB$$c33d6600 actualStudent$$EnhancerByCGLIB$$c33d6600 = new ActualStudent$$EnhancerByCGLIB$$c33d6600();
CGLIB$SET_THREAD_CALLBACKS(null);
return actualStudent$$EnhancerByCGLIB$$c33d6600;
}
public Object newInstance(final Callback callback) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[] { callback });
final ActualStudent$$EnhancerByCGLIB$$c33d6600 actualStudent$$EnhancerByCGLIB$$c33d6600 = new ActualStudent$$EnhancerByCGLIB$$c33d6600();
CGLIB$SET_THREAD_CALLBACKS(null);
return actualStudent$$EnhancerByCGLIB$$c33d6600;
}
public Object newInstance(final Class[] array, final Object[] array2, final Callback[] array3) {
CGLIB$SET_THREAD_CALLBACKS(array3);
switch (array.length) {
case 0: {
final ActualStudent$$EnhancerByCGLIB$$c33d6600 actualStudent$$EnhancerByCGLIB$$c33d6600 = new ActualStudent$$EnhancerByCGLIB$$c33d6600();
CGLIB$SET_THREAD_CALLBACKS(null);
return actualStudent$$EnhancerByCGLIB$$c33d6600;
}
default: {
throw new IllegalArgumentException("Constructor not found");
}
}
}
public Callback getCallback(final int n) {
CGLIB$BIND_CALLBACKS(this);
Object cglib$CALLBACK_0 = null;
switch (n) {
case 0: {
cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0;
break;
}
default: {
cglib$CALLBACK_0 = null;
break;
}
}
return (Callback)cglib$CALLBACK_0;
}
public void setCallback(final int n, final Callback callback) {
switch (n) {
case 0: {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
break;
}
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[] { (Callback)this.CGLIB$CALLBACK_0 };
}
public void setCallbacks(final Callback[] array) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
}
static {
CGLIB$STATICHOOK1();
}
}
最开始执行的是 **CGLIB$STATICHOOK1();**我么可以发现
final Class<?> forName = Class.forName("com.huo.proxy.ActualStudent$$EnhancerByCGLIB$$c33d6600");
final Class<?> forName2;
CGLIB$tuitionFee$0$Method = ReflectUtils.findMethods(new String[] { "tuitionFee", "()V" }, (forName2 = Class.forName("com.huo.proxy.ActualStudent")).getDeclaredMethods())[0];
CGLIB$tuitionFee$0$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()V", "tuitionFee", "CGLIB$tuitionFee$0");
public final void tuitionFee() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$tuitionFee$0$Method, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$emptyArgs, ActualStudent$$EnhancerByCGLIB$$c33d6600.CGLIB$tuitionFee$0$Proxy);
return;
}
super.tuitionFee();
}
通过这种方式将对应的实体传递到intercept中进行用户的自行操作。
思考这种方式和jdk的动态代理有什么不一样呢?其实大致差不多 只是表现形式不一样,将一些的操作隐藏起来,减少了使用。
三:代理的使用场景
代理主要用在方法的拦截上面,进行aop的编程,具体的使用看自己的需求,不限制。
四:代理与注解的使用
代理可以进行方法的拦截那么,对方法上面的注解如何操作呢
package com.huo.proxy.cglib;
import java.lang.reflect.Method;
import com.huo.annotation.Face;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyBank implements MethodInterceptor
{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
{
System.out.println("注解:"+method.getAnnotation(Face.class));
Face face = method.getAnnotation(Face.class);
if(face != null)
{
System.out.println("Face::time() "+face.time());
}
long time1 = System.currentTimeMillis();
System.out.println("Time: "+time1);
Object object = proxy.invokeSuper(obj, args);
long time2 = System.currentTimeMillis();
System.out.println("Time: "+time2);
System.out.println("消耗时间:"+(time2-time1));
return object;
}
}
输出内容
CGLIB debugging enabled, writing to '/E:/study/Test-study/bin/com/huo/proxy/cglib/'
注解:@com.huo.annotation.Face(time=1)
Face::time() 1
Time: 1555855313145
交学费
Time: 1555855313166
消耗时间:21
package com.huo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Face
{
public int time() default 1;
}
这样就可以执行自己的注解定义的特殊操作了,传递一些类的名称,进行特殊的加载等,具体的使用还得自己去实践。
时间记录:2019-4-21