一.代理模式简介:
1)概念:为对象提供一个替身,以控制对这个象的访 问。 即通过代理 对象 访问目标.这样做的 好处是 :可以在目标对象实现的基础上 ,增强额外的 功能操作 ,即扩展目标对象的功能。
2) 被代理的对象可以是远程对象 、创建开销大的对象或需要安全控制 的对象
3) 分类:代理模式 有不同的形式 , 主要有三种 静态代理 、动态代理 (JDK (JDK代 理、接口理)和 Cglib Cglib 代理 (可以在内存动态的创建对象, 而不需要实现接口他是属于 动态代理的范畴 )。
二、静态代理
1.简介:静态代理在使用时 ,需要定义接口或者父类 ,被代理对象(即目标对象 )与代理对象一 起实现相同的接口或者是继承父类
2.静态代理优 缺点
- 优点:在 不修改目标对象的功能前提下 , 能通过代理对象目标功能扩展
- 缺点:因 为代理对象需要与目标实现一样的接口 ,所以会有很多代理 类;一旦接口增加方法 ,目标对象与代理象都要维护
3.代码举例:
public interface Player {
//玩游戏的方法
public void paly();
}
//普通玩家类:
public class CommonPlayer implements Player{
public void paly() {
System.out.println("该游戏账号正在使用,游戏中。。。");
}
}
//代理类:
public class ProxyPlayer implements Player {
//代练玩家其实也是个普通玩家
private CommonPlayer commonPlayer;
public ProxyPlayer(CommonPlayer commonPlayer) {
this.commonPlayer = commonPlayer;
}
public void before(){
System.out.println("接受委托,开始代练账号。。。");
}
public void after(){
System.out.println("完成任务。。。");
}
public void paly() {
before();
commonPlayer.paly();
after();
}
}
public class Client {
public static void main(String[] args) {
CommonPlayer commonPlayer = new CommonPlayer();
ProxyPlayer proxyPlayer = new ProxyPlayer(commonPlayer);
proxyPlayer.paly();
}
}
执行结果:
三、jdk动态代理
1.jdk动态代理模式的基本介绍
- 代理对象 ,不需要实现接 口, 但是目标对 象要 实现接 口,否则不能用动态代理
- 代理对象的生成,是利用 JDK 的API ,动态的在内存中构建代理对 象
3)JDK 动态代理也叫 做:JDK 代理、接口代理
2.jdkProxy代码:
package JdkProxy;
public interface Producer{
void sale(Float price);//销售
String order(Integer id);//下订单
}
package JdkProxy;
import java.util.UUID;
public class MyProducer implements Producer {
public void sale(Float price) {
System.out.println("卖出商品,价格是:"+price*0.95);
}
public String order(Integer id) {
return UUID.randomUUID().toString()+id;
}
}
package JdkProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProducerProxy implements InvocationHandler {
private Object target = null;
public ProducerProxy(Object obj) {
this.target = obj;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法的参数
Object arg = args[0];
Object res = null;
if("sale".equals(method.getName())){
res = method.invoke(this.target,arg);
}else if("order".equals(method.getName())){
res = method.invoke(this.target,arg);
}
return res;
}
public static void main(String[] args) {
MyProducer myProducer = new MyProducer();
ProducerProxy producerProxy = new ProducerProxy(myProducer);
Producer myProxy = (Producer) producerProxy.getProxyInstance();
String orderId = myProxy.order(9999);
System.out.println(orderId);
myProxy.sale(1000.0f);
}
}
三、Cglib 代理
1.Cglib 代理基本介绍:
- 静态代 理和 JDK 代理模式 都要 求目标对象是实现一个接口,但是有时候目标对象只 是一个单独的对象 ,并没有实现任何的接口 ,这个时候可使用目标对象子类来实现代理这就是 Cglib代理
- Cglib 代理也 叫作子类代理 ,它是在内存中构建一个子类对象从而实现目标功 能扩展 , 有些 书也将 Cglib 代理 归属到动态。
- Cglib 是一个强大的高性能代码生成包 ,它可以在运行期扩展 java类与实现 java接 口.它广泛的被许多 AOP 的框架使用 ,例如 Spring AOP ,实现 方法拦截
- 在AOP 编程 中如何选择代理模式:
a. 目标对 象需要实 现接 口,用JDK 代理
b. 目标对 象不需要实 现接 口,用Cglib代理 - Cglib Cglib包的底层是通过使用字节码处理框架 ASM 来
2.cglib Proxy代码:
package cglibProxy;
import java.util.UUID;
public class Producer {
public void sale(Float price) {
System.out.println("卖出商品,价格是:"+price*0.95);
}
public String order(Integer id) {
return UUID.randomUUID().toString()+id;
}
}
package cglibProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target = null;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object arg = objects[0];
Object res = null;
if("sale".equals(method.getName())){
res = method.invoke(target,arg);
}else if("order".equals(method.getName())){
res = method.invoke(target,arg);
}
return res;
}
public static void main(String[] args) {
Producer producer = new Producer();
CglibProxy cglibProxy = new CglibProxy(producer);
Producer producerProxy = (Producer) cglibProxy.getProxyInstance();
String id = producerProxy.order(1111);
System.out.println(id);
producerProxy.sale(2000.0f);
}
}
四、常见面试题
1.JDK动态代理个CgLib动态代理的区别?
(1)要求不同:JDK动态代理只能代理实现了接口的类,没有实现接口的类不能实现JDK的动态代理;
而Cglib动态代理是针对类实现代理的,如果被代理类被final关键字所修饰,那么抱歉会失败
(2)底层:JDK 是通过 Java的反射机制实现的,Cglib 是通过ASM字节码框架实现的
(3)效率:
1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
2)但是在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。
3)jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。