静态代理与JDK动态代理
目录
1.静态代理
静态代理就是已知的代理类,对其进行代理。
首先创建普通的项目
1.1.创建Service
//创建Pen服务类
//这里指代的是买钢笔的服务类
public interface PenService {
public float sellPen(int num);
}
1.2.创建对象的实现类
package edu.wan.service.serviceImpl;
import edu.wan.service.PenService;
//晨光铅笔的供应商,不为个人提供钢笔的买卖信息,直接为代理提供
public class CgPenServiceImpl implements PenService {
@Override
public float sellPen(int num) {
return 5.0f;
}
}
package edu.wan.service.serviceImpl;
import edu.wan.service.PenService;
//英雄钢笔的服务类
//这个服务也不对外进行开放,只能通过代理的形式才能进行购买
public class HeroPenServiceImpl implements PenService {
@Override
public float sellPen(int num) {
return 25.0f;
}
}
1.3.创建代理类
package edu.wan.service.agentImpl;
import edu.wan.service.PenService;
import edu.wan.service.serviceImpl.CgPenServiceImpl;
import edu.wan.service.serviceImpl.HeroPenServiceImpl;
//中间的代理商,用户可以从这里进行购物
//个人理解,这个代理的本质就是我是没有Pen的实物的,我只是代理其他公司的东西,
// 但是我通过代理可以增加我的实现逻辑
public class TaobaoPenService implements PenService {
private CgPenServiceImpl cgPenServiceImpl = new CgPenServiceImpl();
private HeroPenServiceImpl heroPenServiceImpl = new HeroPenServiceImpl();
//该方法是静态代理的方法,首先我们还是代用、具体实现的方法
// 具体实现方法就是 cgPenServiceImpl.sellPen 、 heroPenServiceImpl.sellPen
// 而代理做的就是从具体实现的方法之后,进行行为的增强
// ps:加收额为的费用,增加运费等功能的增强
@Override
public float sellPen(int num) {
Float aFloat = cgPenServiceImpl.sellPen(1);
aFloat += aFloat +2.0f;
System.out.println("如果购买晨光的钢笔总共花费:"+aFloat+"而且我们是面运费的!");
Float aFloat1 = heroPenServiceImpl.sellPen(1);
aFloat1 += aFloat1 +1.5f;
System.out.println("如果购买晨光的钢笔总共花费:"+aFloat1 +"但是还需要加收0.5的运费!");
return aFloat+aFloat1;
}
}
1.4.测试类
package edu.wan.service;
import edu.wan.service.agentImpl.TaobaoPenService;
public class Test {
public static void main(String[] args) {
TaobaoPenService taobaoPenService = new TaobaoPenService();
Float aFloat = taobaoPenService.sellPen(1);
System.out.println("购买钢笔总共花费: "+aFloat);
}
}
1.5.测试结果
2.JDK动态代理
想要了解JDK的动态代理,需要了解Method 、InvocationHandler 、Proxy
验证需要创建一个普通项目。
2.1.创建一个Service
package edu.wan.jdk.service;
public interface PenService {
public float sellPen(int num);
}
2.2.创建实现类
package edu.wan.jdk.serviceImpl;
import edu.wan.jdk.service.PenService;
//晨光铅笔的供应商,不为个人提供钢笔的买卖信息,直接为代理提供
public class CgPenServiceImpl implements PenService {
@Override
public float sellPen(int num) {
return 5.0f;
}
}
package edu.wan.jdk.serviceImpl;
import edu.wan.jdk.service.PenService;
//英雄钢笔的服务类
//这个服务也不对外进行开放,只能通过代理的形式才能进行购买
public class HeroPenServiceImpl implements PenService {
@Override
public float sellPen(int num) {
return 25.0f;
}
}
2.3.验证Method
// Method 方法
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
PenService penService = new HeroPenServiceImpl();
PenService penService2 = new CgPenServiceImpl();
// 获取方法,通过方法的名字和方法的参数列表
// 委托模式支持penService2的调用,代理模式就不支持penService2的调用
// Method method = PenService.class.getMethod("sellPen", int.class);//委托模式
Method method = penService.getClass().getMethod("sellPen", int.class);//代理模式
/** method.invoke 参数讲解: public Object invoke(Object obj, Object... args)
1) Object obj :指的是要被代理的对象
2) Object... args:指的是代用method方法所需要的参数 因为参数是未知的所以用 ...表示
*/
//动态的调用HeroPenServiceImpl的sellpen的方法
//本质上method只是对应的sellPen方法的对象,需要传入代理的类,用于实现代理类的sellPen的实现
Float aFloat = (float) method.invoke(penService2, 1);
System.out.println("购买一支英雄钢笔的价格:"+aFloat);
}
在这里出现一个问题,在我使用代理模式的时候,我会用(float) method.invoke(penService2, 1)进行动态代理的时候会抛出一个问题。
问题如下:
经过源码查看:
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
远程在验证完之后判断是否有MethodAccessor,如果没有的话就调用acquireMethodAccessor
在acquireMethodAccessor类中有三个方法分别是:
acquireMethodAccessor()
setMethodAccessor()
copy()
Method实例对象维护了一个root引用,当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象。
Method被多次拷贝后,调用一次setMethodAccessor()方法,就会将root引用所指向的Method的methodAccessor变量同样赋值。
ps:a-> b -> c -> d
当c对象调用setMethodAccessor,b和a也会传播赋值methodAccessor,但是d的methodAccessor还是null
当d调用setMethodAccessor,d就会和abc一样。
所以这样就导致了使用代理模式的时候,调用method.invoke(penService2, 1)报错,
但是我使用委托模式的时候,就没有问题。
注:这块是我自己的理解和网上的参考,如有不对的地方,请帮忙指出,感谢!!!!
2.4.实现InvocationHandler
package edu.wan.jdk.Handle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//必须实现InvocationHandler接口,该实现类实现了两个功能
// 1.调用了目标的方法
// 2.功能增强
public class MyInvocationHandle implements InvocationHandler {
private Object targer = null;
//targer指代是要被代理的对象
public MyInvocationHandle(Object targer){
this.targer = targer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(targer, args); // 执行代理类的目标方法
// 以下属于功能增强的方法
System.out.println("我的地盘听我的");
System.out.println("调用过来了来 撒花.........");
return invoke;
}
}
2.5.Proxy代理对象的创建
//public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
//newProxyInstance 主要的作用就是不用使用new 就可以创建对象,
// 创建了接口的代理对象
/**
loader :类加载器
interfaces:接口
h: 自己定义的 Handler 实现功能的曾想
*/
public static void main(String[] args) {
PenService penService = new HeroPenServiceImpl();
PenService penService2 = new CgPenServiceImpl();
InvocationHandler myInvocationHandle = new MyInvocationHandle(penService2);
PenService penService1 = (PenService) Proxy.newProxyInstance(penService.getClass().getClassLoader(),
penService.getClass().getInterfaces(), myInvocationHandle);
System.out.println(penService1.sellPen(1));
2.6.验证结果
源码地址:https://gitee.com/Wanbogo/csdnblogs.git 中的agent文件夹。
动态代理在mybatis、Spring中都有所应用。