什么是代理模式?
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
可以说,生活中处处能见到代理模式,经纪人、中介、黄牛,甚至以前的媒婆都是代理模式。就拿经纪人来说吧,流量巨星蔡徐坤,就是那个喜欢唱、跳、rap和篮球的男人。他的经纪人葛福鸿(特地百度的)就是代理模式。现在来假设一下葛福鸿为蔡徐坤签下NBA中国形象大使的过程,以此来了解代理模式的原理:首先,蔡徐坤火肯定有她的道理,说明她很努力,那么她肯定背后下了很多功夫来提升自己的技能(唱、跳、rap和篮球),换句话说她很忙,没时间亲自去找商业合作,包括和谈、签订合同、收款等事宜。当然去往其他城市也需要定好行程,订好机票,酒店等一系列的事情。
从中我们可以看到这些事情都是蔡徐坤必须要做的,但是像找商业合作等事情,他肯定不擅长或者压根就不会做,而安排行程这些麻烦事,蔡徐坤显然懒得做,也没时间做。这不,经济人就出现了,并且很好的解决了这些问题。有了葛福鸿这个经纪人,蔡徐坤就可以心无旁骛的努力磨炼自己的技能,只在需要出场的时候表演一二就可以了。
还有,像蔡徐坤这种大牌明星,找他谈合作的人多如牛毛,要是他自己一一接谈,那肯定累的够呛。于是,所有的电话都会打给经纪人,经纪人会进行合理的筛选,最终选出符合蔡徐坤身份的代言活动,这或许就是NBA形象大使的由来了吧!
还有,我们不要小瞧了经纪人葛福鸿的能力,作为职业经纪人的他甚至可以同时作为多个明星的经纪人,就比方说他可以同时担任蔡徐坤和鹿晗的经纪人。
看了以上的例子我们就大概明白了代理模式的特点了,总结一下:
满足代理模式应用的三个必要条件:
1、两个角色:被代理对象,执行者
2、注重过程,必须要做,被代理对象不想做或没时间做
3、需要获取到被代理对象的信息
代理模式最大的优点就是在访问对象时引入一定程度的间接性,因这种间接性而附加各种用途。
代理模式结构图:
- 分类
- 静态代理(静态定义代理类)
- 动态代理 (动态生成代理类)
- JDK自己的动态代理
- javaassit字节码操作库实现
- CGLIB
- ASM(底层使用指令,可维护性差)
代码示例:
静态代理的代码示例
- 定义一个接口
public interface Tenement {
// 寻找房客
void findTenant();
// 带人看房
void surveyHourse();
// 签合同
void signContract(); // 只能和房主签o
// 收定金
void collectMoney();
// 交付钥匙
void giveKey();
}
- 真实的实现类
public class RealTenement implements Tenement {
@Override
public void findTenant() {
System.out.println("RealTenement.findTenant()");
}
@Override
public void surveyHourse() {
System.out.println("RealTenement.surveyHourse()");
}
@Override
public void signContract() {
System.out.println("RealTenement.signContract(房东亲自签的哦)");
}
@Override
public void collectMoney() {
System.out.println("RealTenement.collectMoney()");
}
@Override
public void giveKey() {
System.out.println("RealTenement.giveKey()");
}
}
- 代理类
public class ProxyTenement implements Tenement{
private Tenement tenement;
public ProxyTenement(Tenement tenement) {
super();
this.tenement = tenement;
}
@Override
public void findTenant() {
System.out.println("ProxyTenement.findTenant()");
}
@Override
public void surveyHourse() {
System.out.println("ProxyTenement.surveyHourse()");
}
@Override
public void signContract() {
// 调用真实对象的方法
tenement.signContract();
}
@Override
public void collectMoney() {
System.out.println("ProxyTenement.findTenant()");
}
@Override
public void giveKey() {
System.out.println("ProxyTenement.giveKey()");
}
}
- 客户端测试类
public class Client {
public static void main(String[] args) {
Tenement tenement = new RealTenement();
ProxyTenement proxyTenement = new ProxyTenement(tenement);
proxyTenement.findTenant();
proxyTenement.surveyHourse();
proxyTenement.signContract();
proxyTenement.collectMoney();
proxyTenement.giveKey();
}
}
总结:
代理类与真实类实现同一个接口,而代理类无法自行处理某些事件时,通过传递的真实类的对象引用调用真实类的方法;而客户只需要关注接口即可。
动态代理代码示例
一、JDK自带的动态代理实现
java.lang.reflect.Proxy
- 作用:动态生成代理类和对象
java.lang.reflect.InvocationHandler
- 可以通过invoke实现对真实角色的代理访问
- 每次通过proxy生成代理类对象时都要指定对应的处理器类
代码示例:
接口、真实角色类 与上相同
- Handler类
public class TenementHandler implements InvocationHandler {
Tenement realTenement;
public TenementHandler(Tenement realTenement) {
super();
this.realTenement = realTenement;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(realTenement,args);
return null;
}
}
- client类
public class Client {
public static void main(String[] args) {
// 创建真实对象
Tenement realTenement = new RealTenement();
// 创建Handler
TenementHandler handler = new TenementHandler(realTenement);
// 生成代理类和对象
Tenement proxy = (Tenement) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Tenement.class},handler);
// 通过返回的代理类对象 调用方法
proxy.findTenant();
proxy.surveyHourse();
proxy.signContract();
proxy.collectMoney();
proxy.giveKey();
}
}
动态代理的调用过程
深入分析jdk动态代理的实现原理
原理
1、拿到被代理对象的引用和接口
2、jdk代理重新生成一个类,实现代理对象实现的接口
3、拿到代理队形的引用
4、重新动态生成一个class字节码
5、直接加载
代码示例(实现JDK代理):
定义一个接口 Person
public interface Person {
void findLove();
// String getSex();
// String getName();
}
创建一个Zhangsan类实现 Person
public class Zhangsan implements Person{
private String sex = "女";
private String name = "隔壁老王";
public void findLove() {
System.out.println("我叫:"+this.name+"性别:"+this.sex+"我的要求:");
System.out.println("高富帅");
System.out.println("有房有车");
System.out.println("身高180cm以上,体重70KG");
}
}
自定义MyInvocatiionHandle
public interface MyInvocationHandle {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
自定义加载类 MyClassLoader
public class MyClassLoader extends ClassLoader{
private File baseDir;
public MyClassLoader(){
String basePath = MyClassLoader.class.getResource("").getPath();
this.baseDir = new File(basePath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName()+"."+name;
if(baseDir!=null){
File classFile = new File(baseDir,name.replaceAll("\\.","/")+".class");
if(classFile.exists()){
FileInputStream in =null;
ByteArrayOutputStream out =null;
try{
in = new FileInputStream(classFile);
out =new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ( (len= in.read(buff))!=-1){
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
}catch (Exception e){
e.printStackTrace();
}finally {
if(null!=in&&null!=out){
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
// 编译完以后删除
classFile.delete();
}
}
}
}
return null;
}
}
自定义生成代理类 MyProxy
public class MyProxy {
private static String ln ="\r\n";
public static Object newProxyInstance(MyClassLoader classLoader,
Class<?>[] interfaces,
MyInvocationHandle h)
throws IllegalArgumentException
{
// 1、生成源代码
String proxySrc = generateSrc(interfaces[0]);
// 2、将生成的源代码输出到磁盘,保存为.java文件
try {
String filePath = MyProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(proxySrc);
fw.flush();
fw.close();
// 3、编译源代码生成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
Iterable iterable = manager.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null,iterable);
task.call();
manager.close();
// 4、将class文件中的内容,动态加载到JVM中来
// 5、返回被代理后的代理对象
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c= proxyClass.getConstructor(MyInvocationHandle.class);
// 读完以后删除
f.delete();
return c.newInstance();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?> interfaces){
StringBuffer src = new StringBuffer();
src.append("package src.main.java.custom;"+ln);
src.append("import java.lang.reflect.Method;"+ln);
src.append("public class $Proxy0 implements " + interfaces.getName()+"{"+ln);
src.append("MyInvocationHandle h;"+ln);
src.append("public $Proxy0(MyInvocationHandle h){"+ln);
src.append("this.h=h;"+ln);
src.append("}"+ln);
for (Method m:interfaces.getMethods()){
src.append("public" + m.getReturnType()+ " " + m.getName() + "() throws Throwable {"+ln );
src.append("try {"+ln);
src.append("Method m = "+interfaces.getName()+".class.getMethod(\"" +m.getName()+"\",new Class[]{});" +ln );
src.append("this.h.invoke(this,m,null);"+ln);
src.append("}catch(Exception e){}"+ln);
src.append("}"+ln);
}
src.append("}");
return src.toString();
}
}
定义代理执行类实现自定义的MyInvocationHandle
public class MyMeipo implements MyInvocationHandle {
private Person target; //代理对象的引用作为成员变量保存下来
// 获取被代理的资料
public Object getInstance(Person target) throws Exception{
this.target = target;
Class clazz = target.getClass();
System.out.println("被代理队象的class是:"+clazz);
return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println("你的性别是:"+this.target.getSex()+"得给你找个异性才行");
System.out.println("开始你的筛选。。。");
System.out.println("----------------------------");
System.out.println("如果合适就弄事");
// this.target.findLove();
method.invoke(this.target,args); // 反射调用
return null;
}
}
测试类TestCustom
public class TestCustom {
public static void main(String[] args) {
try {
Person object = (Person)new Meipo().getInstance(new Zhangsan());
System.out.println("代理对象是:"+object);
object.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
二、cglib的动态代理实现原理
原理:
cglib代理与jdk代理的最大不同在于,cglib代理不需要实现一个接口,cglib通过创建一个类继承被代理类从而实现对代理对象的间接访问。
代码实例:
首先创建一个具体的类 Wangnima
public class Wangnima {
public void findLove(){
System.out.println("肤白貌美大长腿");
}
}
创建一个代理类 Meipo
public class Meipo implements MethodInterceptor {
//疑问
// 好像没有持有被代理对象的引用??
public Object getInstance(Class clazz) throws Exception {
Enhancer enhancer = new Enhancer();
// 设置父类
// 这一步就是告诉cglib生成的子类应该继承哪个类
enhancer.setSuperclass(clazz);
// 设置回调
enhancer.setCallback(this);
return enhancer.create();
}
/**
* @Author DevinLei
* @Description //实现原理:生成一个类继承被代理对象
**/
// 同样是做了字节码重组的事情
// 但是对java用户来说无感知
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("我是你妈");
System.out.println("开始你的筛选。。。");
System.out.println("----------------------------");
// 这个obj的引用是由cglib给new出来的
// cglib new出来的对象,是被代理对象的子类(继承了我们自己写的那个类)
// oop在new 子类之前,实际上是默认先调用了super()方法的
// 也就是说new 子类之前先new 父类,这就间接持有了父类的引用
// 子类重写了父类的所有方法
// 我们改变子类对象的某些属性时,是可以间接操作父类属性的
proxy.invokeSuper(obj,args); // 特别注意:这里如果是invoke()则会陷入死循环中
System.out.println("如果合适就弄事");
return null;
}
}
创建一个测试类 :TestCglibProxy
public class TestCglibProxy {
public static void main(String[] args) {
//jdkd的动态代理是通过接口来强制转换的
// 生成以后的对象,可以强制装换为接口
// cglib的动态代理是通过生成一个被代理对象的的子类,然后重写父类方法
// 生成以后的对象可以强制转换为被代理的对象(也就是自己写的类)
// 子类引用赋值给父类
try {
Wangnima obj = (Wangnima)new Meipo().getInstance(Wangnima.class);
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
代理模式的应用
- 安全代理:屏蔽真是角色的直接访问
- 远程加速:通过代理类处理远程方法调用
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
- spring AOP ,Mybatis中都使用了代理模式。