设计模式——代理模式(静态代理和JDK、CGLib动态代理)
简介
什么是代理模式?
代理模式就是多一个代理类出来,代替原对象进行一些操作。比如说租房的中介、打官司的律师、旅行社,他们可以代替我们做一些事情,这就是代理。
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1. 修改原有的方法来做到改进。但这样违反了“对扩展开放,对修改关闭”的原则。
2. 采用一个代理类调用原有的方法,且对产生的结果进行控制。这就是代理模式。
代理模式的分类:
静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。再程序运行前代理类的.class文件就已经存在了。
动态代理:在程序运行时用反射机制,动态创建代理类。
静态代理
所谓静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public interface Hello {
public void sayHello(String way);
}
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class HelloImpl implements Hello{
public void sayHello(String way) {
System.out.println("Hello by " + way);
}
}
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class HelloProxy implements Hello {
private Hello hello;
public HelloProxy() {
hello = new HelloImpl();
}
private void sayBefore() { System.out.println("Before.."); }
private void sayAfter() { System.out.println("After.."); }
public void sayHello(String way) {
sayBefore();
hello.sayHello(way);
sayAfter();
}
}
代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。HelloProxy 类实现了 Hello 接口,在构造方法中 new 出一个 HelloImpl 类的实例。在 sayHello()方法中调用 HelloImpl 的 sayHello()方法,同时在调用的前后加上 before 与 after 方法。
测试类:
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class Test {
public static void main(String[] args) {
Hello proxy = new HelloProxy();
proxy.sayHello("normal");
}
}
输出结果:
Before..
Hello by normal
After..
以上就是静态代理,下面分析其带来的优缺点:
优点:
客户端不必知道实现类(委托类)如何如何,只需要调用代理类即可。
缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。但这样出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也要实现这个方法。这显然增加了代码的复杂度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象,那就要对每种对象都进行代理。静态代理子啊程序规模稍大是就无法胜任了。
JDK代理——接口级别代理
package Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//Created by zhengbinMac on 2017/2/19.
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target) { this.target = target; }
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
sayBefore();
Object result = method.invoke(target, args);
sayAfter();
return result;
}
private void sayBefore() { System.out.println("before..."); }
private void sayAfter() { System.out.println("after..."); }
}
JDK 提供的 Proxy 类的工厂方法 newProxyInstance 去动态地创建一个 Hello 接口的代理类。
Proxy.newProxyInstance:
参数:
- loader - 定义代理类的类加载器
- interfaces - 代理类要实现的接口列表
- h - 指派方法调用的调用处理程序(每个代理实例都具有一个关联的调用处理程序,调用代理实例的方法时,将对方法的调用指派到它的调用处理程序的 invoke 方法)
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定类加载器定义,并实现指定的接口。
测试类:
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class Test {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello hello = dynamicProxy.getProxy();
hello.sayHello("JDK");
}
}
动态代理相比静态代理,接口变了,动态代理类不需要改变,而静态代理类不仅需要改变实现类,代理类也需要修改。
但如果要代理一个没有接口的类,JDK 动态代理就用不上了,这就引出了 CGLib 代理。
CGLib代理——方法级别代理
Spring、Hibernate 框架都是用了它,它是一个在运行期间动态生成字节码的工具,也就是动态生成代理类。
package Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//Created by zhengbinMac on 2017/2/19.
public class CGLibProxy implements MethodInterceptor {
// 单例模式
private static CGLibProxy instance = new CGLibProxy();
private CGLibProxy() {}
public static CGLibProxy getInstance () { return instance; }
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this);
}
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
sayBefore();
Object result = methodProxy.invokeSuper(obj, objects);
sayAfter();
return result;
}
private void sayBefore() { System.out.println("before..."); }
private void sayAfter() { System.out.println("after..."); }
}
CGLib 给我们提供的是方法级别的代理,也可以理解为对方法的拦截。通过直接调用 proxy 的 invokeSuper 方法,将被代理的对象 obj 以及方法的参数 objects 传入其中即可。
测试类:
package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class Test {
public static void main(String[] args) {
Hello helloCGLib = CGLibProxy.getInstance().getProxy(HelloImpl.class);
helloCGLib.sayHello("CGLib");
}
}
设计模式——代理模式(静态代理和JDK、CGLib动态代理)的更多相关文章
- java代理(静态代理和jdk动态代理以及cglib代理)
版权声明:本文为Fighter168原创文章,未经允许不得转载. 目录(?)[+] 说到代理,脑袋中浮现一大堆代理相关的名词,代理模式,静态代理,jdk代理,cglib代理等等. 记忆特别深刻 ...
- 代理模式详解:静态代理+JDK/CGLIB 动态代理实战
1. 代理模式 代理模式是一种比较好的理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...
- jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)
代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...
- Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景
我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...
- 静态代理和jdk动态代理
要说动态代理,必须先聊聊静态代理. 静态代理 假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志. 你如何在不修改已有代码的前提下,完成这个需求? 我首先想到的是静态代理.具体做法是: 1 ...
- 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理
本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...
- 基于SpringBoot实现AOP+jdk/CGlib动态代理详解
动态代理是一种设计模式.在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理. JDK动态代理 首先定义一个人的接口: public interface Person { ...
- java静态代理和JDK动态代理
静态代理 编译阶段就生产了对应的代理类 public interface IBussiness { void execute(); } public class BussinessImpl imple ...
- Spring Boot @Trasactionl 失效, JDK,CGLIB动态代理
来自: https://www.cnblogs.com/sweetchildomine/p/6978037.html?utm_source=itdadao&utm_medium=referra ...
随机推荐
- root目录空间不够的问题
今天导入mysql表的时候,提示write file error /tmp/xxx 原因是表太大,创建临时表的时候,tmp目录不够空间了. 找到一个解决方法: 使用 mount --bind moun ...
- 懒得说IE6了,写个js插件不能写注释,原因如下
变态的ie6将注释当代码解释 ie6宽松的安全环境对于开发人员是开心的,比如运行速度快(对于ie7/8/9).支持部份文件操作等.但也有很多烦忧,比如对数组.对象的检测比较机械,这还不算什么,这两天让 ...
- Fiddler高级用法-抓取手机app数据包
在上一篇中介绍了Fiddler的基本使用方法.通过上一篇的操作我们可以直接抓取浏览器的数据包.但在APP测试中,我们需要抓取手机APP上的数据包,应该怎么操作呢? Andriod配置方法 1)确保手机 ...
- debian8最小化安装,字符界面的中文配置
一.现象: debian8最小化安装以后,字符界面的中文显示乱码. 二.解决 1. 安装locales apt-get install locales 2. 配置locales dpkg-reconf ...
- svm工具箱快速入手简易教程
首先svm是用来做分类的,是一种有监督的分类器. 什么是有监督的呢?就是说在你给我一个数据集让我做分类之前.我已经有一些经验数据了.即要先进行学习,再进行分类. 这里就有了训练集和测试集.先用训练集来 ...
- 找不到dubbo:annotaion错误
dubbo 2.8.4 出现找不到dubbo:annotation的错误,其实这个不会影响程序正确的运行,但是看到有红叉心里肯定非常不爽: 解决办法是,将dubbo-2.8.4.jar包,后缀改成.z ...
- RocketMQ最佳实践(一)4.0版本/概念介绍/安装调试/客户端demo
为什么选择RocketMQ 我们来看看官方回答: “我们研究发现,对于ActiveMQ而言,随着越来越多的使用queues和topics,其IO成为了瓶颈.某些情况下,消费者缓慢(消费能力不足)还会拖 ...
- 用bundler安装jeklly
为什么要写这篇文章呢?因为官方的安装文档里,ruby的很多库没有说明怎么安装.所以需要重点说明一下.1.我的安装环境是vultr的16.04版的ubuntu.2.因为ruby的扩展库好多都是Gcc编译 ...
- [svc][op]磁盘(结构)容量计算
磁盘结构和容量计算 fdisk -l显示信息详解 [root@www.linuxidc.com ~]# fdisk -l Disk /dev/sda: bytes heads, sectors/tra ...
- Win7下使用无线网卡共享上网的4种方式
我尝试了第一种直接上网了 一.Win7自带无线承载网络功能 1.查看网卡是否支持承载网络功能运行“命令提示符” 输入命令:netsh wlan show drivers图中红框“支持的承载 ...