说起代理模式,相信很多人早已经很理解,但对于我这个菜鸟,理解的可能还是不到位,写一次博客加深印象。

什么是代理模式呢?代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类和委托类之间通常会存在关联关系,

一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不是真正实现服务,而是通过调用委托类对象的相关

的方法来提供特定的服务。

按照代理的创建时期,一般分为两类:

静态代理:由程序员或特定工具自动生成的源代码,再对其编译,在程序运行之前,代理的类编译生成的.class文件就已经存在了

动态代理:在程序运行时,通过反射机制动态创建而成。

重点:

动态代理主要实现使用的是聚合,这种方法更有利于继承

作用:

(1)方法前后加日志,记录时间(方法运行的时间)

(2)方法前后加事务,事务的提交或者回滚(AOP)

(3)方法前后加判断调用者是否拥有权限,用于权限的控制

1、静态代理:

由程序员创建或工具生成代理类的源码,再编译代理类。所谓的静态代理,就是在程序运行前就已经存在的代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

代码如下:

接口Moveable.java

package designMode.proxy;
/*** * @Description * @author CCQ* @date 2017年6月22日 上午11:19:30**/
public interface Moveable {void move();}

Tank.java

package designMode.proxy;import java.util.Random;
/*** * @Description * @author CCQ* @date 2017年6月22日 上午11:19:52**/
public class Tank implements Moveable{@Overridepublic void move() {System.out.println("Tank Moving...");try {Thread.sleep(new Random().nextInt(10000));} catch (InterruptedException e) {e.printStackTrace();}}}

日志代理类TankLogProxy.java

package designMode.proxy;public class TankLogProxy implements Moveable {public TankLogProxy(Moveable t) {super();this.t = t;}Moveable t;@Overridepublic void move() {System.out.println("log Start ...");t.move();System.out.println("log end ...");}}

时间代理类TankTimeProxy.java

package designMode.proxy;public class TankTimeProxy implements Moveable {public TankTimeProxy(Moveable t) {super();this.t = t;}Moveable t;@Overridepublic void move() {long start = System.currentTimeMillis();t.move();long  end = System.currentTimeMillis();System.out.println("运行时间:"+(end-start)/1000+"秒");}}

测试类TestProxy.java

package designMode.proxy;public class TestProxy {public static void main(String[] args) {Tank t = new Tank();TankTimeProxy ttp = new TankTimeProxy(t);TankLogProxy tlp = new TankLogProxy(ttp);Moveable m = tlp;m.move();}}

测试结果

2、动态代理

Spring主要的两大思想,一个是IOC,一个是AOP,对于IOC,就是依赖注入,而对于AOP来说,我们不但要知道怎

么通过AOP来满足我们的功能,更需要知道底层是怎样的原理,而AOP的原理就是Java的动态代理机制。

jdk动态代理和cglib动态代理。两种方法的存在,各有各自的优势。jdk动态代理是由java内部的反射机制来实现的,

cglib动态代理底层则是借助asm来实现。

总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm

生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标

类基于同一的接口。如果没有上述的前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,

cglib这种第三方类库实现的代理应用更加的广泛,且在效率更有优势。

(1)jdk动态代理

在java的动态代理机制中,有两个重要的接口和类。一个是接口InvoactionHandler,一个是类Proxy,这一个类和一个

接口是实现动态代理所必须用到的。

每一个动态代理类都必须实现InvocationHandler这个接口,并且每一个代理类的实例都关联到一个Handler,当我们

通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来调用。

观察InvocationHandler接口的唯一一个方法。

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
proxy:代理的真实对象
method:代理的真实对象的某个方法的Method对象

args:调用真实方法对象传递的参数

源码模拟实现:

UserDao.java 接口

package designMode.proxy.proxyJDK.dao;import java.util.Date;/*** * @Description * @author CCQ* @date 2017年6月22日 上午9:28:42**/
public interface UserDao {void save();void delete(int id);void update(int id,String name,String pwd,Date date);
}UserDaoMysqlImpl.java package designMode.proxy.proxyJDK.dao.impl;import java.util.Date;import designMode.proxy.proxyJDK.dao.UserDao;/*** * @Description * @author CCQ* @date 2017年6月22日 下午1:16:41**/
public class UserDaoMysqlImpl implements UserDao {@Overridepublic void save() {System.out.println("Mysql执行保存...");}@Overridepublic void delete(int id) {System.out.println("Mysql执行删除..."+id);}@Overridepublic void update(int id, String name, String pwd, Date date) {System.out.println("Mysql执行修改...id:"+id+"name:"+name+"pwd:"+pwd+"date:"+date);}}UserDaoOracleImpl.javapackage designMode.proxy.proxyJDK.dao.impl;import java.util.Date;import designMode.proxy.proxyJDK.dao.UserDao;public class UserDaoOracleImpl implements UserDao {@Overridepublic void save() {System.out.println("Oracle执行保存...");}@Overridepublic void delete(int id) {System.out.println("Oracle执行删除..."+id);}@Overridepublic void update(int id, String name, String pwd, Date date) {System.out.println("Oracle执行修改...id:"+id+"name:"+name+"pwd:"+pwd+"date:"+date);}}

实现jdk代理第一种形式,使用ProxyFactory工厂的模式生成代理对象
分别有增加事务(getTransactionProxyInstance)和日志(getLogProxyInstance)的代理:

package designMode.proxy.proxyJDK;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** * @Description 创键代理对象工厂* @author CCQ* @date 2017年6月22日 上午9:54:44**/
public class ProxyFactory {private Object obj;public ProxyFactory(Object obj) {super();this.obj = obj;}public Object getTransactionProxyInstance(){Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {/*** 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开启事务..."); //执行核心业务之前执行的内容method.invoke(obj, args);      //执行目标对象方法,即核心业务System.out.println("关闭事务..."); //执行核心业务之后执行的内容return proxy;}});return proxy;}public Object getLogProxyInstace(){Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {/*** 三个参数:1、代理对象,2、目标对象的方法,3、目标对象的参数值列表*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开启日志..."); //执行核心业务之前执行的内容method.invoke(obj, args);      //执行目标对象方法,即核心业务System.out.println("关闭日志..."); //执行核心业务之后执行的内容return proxy;}});return proxy;}
}

第二种是继承InvocationHandler接口,重写invoke方法,提供getInstance方法实现
LogHander.java,增加日志handler

package designMode.proxy.proxyJDK;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class LogHandler implements InvocationHandler {Object target = null;public LogHandler(Object target) {super();this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开启日志..."); //执行核心业务之前执行的内容method.invoke(target, args);      //执行目标对象方法,即核心业务System.out.println("关闭日志..."); //执行核心业务之后执行的内容return proxy;}public Object getInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , this);}}

测试类 TestJDKProxy.java

package designMode.proxy.proxyJDK;import java.util.Date;import org.junit.Test;import designMode.proxy.proxyJDK.dao.UserDao;
import designMode.proxy.proxyJDK.dao.impl.UserDaoMysqlImpl;
import designMode.proxy.proxyJDK.dao.impl.UserDaoOracleImpl;/*** * @Description * @author CCQ* @date 2017年6月22日 上午10:02:45**/
public class TestJDKProxy {@Testpublic void test1(){UserDao userDao = new UserDaoMysqlImpl();UserDao userDaoProxy = (UserDao) new ProxyFactory(userDao).getTransactionProxyInstance();userDaoProxy.save();userDaoProxy.update(1, "张三", "123456", new Date());System.out.println("目标对象类型:"+userDao.getClass());System.out.println("代理对象类型:"+userDaoProxy.getClass());}@Testpublic void test2(){UserDao userDao = new UserDaoOracleImpl();UserDao userDaoProxy = (UserDao) new LogHandler(userDao).getInstance();userDaoProxy.save();System.out.println("目标对象类型:"+userDao.getClass());System.out.println("代理对象类型:"+userDaoProxy.getClass());}
}

测试结果:

(2)cglib代理

JDK动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。cglib就是针对类来实现代理的,它

的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进

行代理。

使用cglib实现动态代理,完全不受代理类必须实现接口的限制,而且cglib底层采用ASM字节码生成框架,使用字节码

技术生成代理类,比使用java反射效率要高。

需要引入两个jar包:cglib.jar,asm.jar

定义了一个拦截器,在调用目标方法之前,cglib回调MethodInterceptor接口方法拦截,来实现自己的业务逻辑,类似

于JDK中的InvocationHandler接口。

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable ;

proxy:为cglib动态生成的代理实例
method:为上文中实体类所调用的被代理的方法调用

args:为method参数数值列表

methodProxy:为生成代理类对方法的代理引用

返回:从代理实例方法调用返回的值

其中,methodProxy.invokeSuper(obj,arg):

调用代理类实例上的proxy方法的父类方法

UserDaoImpl.java

package designMode.proxy.proxyCglib;/*** * @Description * @author CCQ* @date 2017年6月22日 下午1:16:41**/
public class UserDaoImpl {public void save() {System.out.println("Mysql执行保存...");}}

CglibProxyFactory.java

package designMode.proxy.proxyCglib;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;/*** * @Description * @author CCQ* @date 2017年6月22日 下午1:50:09**/
public class CglibProxyFactory {private Object obj;public CglibProxyFactory(Object obj) {super();this.obj = obj;}public Object getProxyFactory(){//Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展Enhancer enhancer = new Enhancer();enhancer.setSuperclass(obj.getClass());//将目标对象所在的类作为Enhaner类的父类enhancer.setCallback(new MethodInterceptor() {//通过实现MethodInterceptor实现方法回调@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("事务开启...");method.invoke(obj, args);System.out.println("事务结束...");return proxy;}});return enhancer.create();//生成目标对象并返回}
}

测试类

package designMode.proxy.proxyCglib;import org.junit.Test;/*** * @Description * @author CCQ* @date 2017年6月22日 上午10:02:45**/
public class TestCglibProxy {@Testpublic void test1(){UserDaoImpl userDao = new UserDaoImpl();UserDaoImpl userDaoProxy = (UserDaoImpl) new CglibProxyFactory(userDao).getProxyFactory();userDaoProxy.save();System.out.println("目标对象类型:"+userDao.getClass());System.out.println("代理对象类型:"+userDaoProxy.getClass());}}

设计模式-代理模式(jdk代理和cglib代理详解)相关推荐

  1. 代理模式——静态代理,动态代理(JDK代理和CGLib代理)

    概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问. 这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. Java中的代理按照代理类生成时机不同又分为 ...

  2. Spring AOP中的JDK代理和Cglib代理

    JDK动态代理是java JDK自身提供的基于接口的代理,代理类的生成速度快,而代理类的运行速度慢,适合于prototype类型 Cglib代理是基于之类继承的方式的代理,能代理非基于接口的类,适合于 ...

  3. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)

    第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...

  4. 动态代理(JDK动态代理和CGLIB代理)

    一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 二 Jav ...

  5. 动态代理:JDK动态代理和CGLIB代理的区别

    代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...

  6. 静态代理,JDK动态代理和CGLIB代理入门学习

    之前面试时面试官问我:"你知道spring中有哪几种代理吗?" 啊?代理?啥子代理?VPN代理吗?嘿嘿,面试官你要种子直说啊......被刷下来了.好吧,入门学习下代理. 为什么需 ...

  7. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  8. JDK动态代理和CGLIB代理的区别

    https://www.cnblogs.com/waves-a/p/8036857.html 转载于:https://www.cnblogs.com/wangjing666/p/11357689.ht ...

  9. 动态代理模式newProxyInstance及invoke方法参数详解

    动态代理概述: Java提供的动态代理类Proxy: Proxy provides static methods for creating dynamic proxy classes and inst ...

最新文章

  1. 重新标注128万张ImageNet图片:多标签,全面提升模型性能
  2. linux mysql详解,Linux 下mysql安装使用详解
  3. kettle、Oozie、camus、gobblin
  4. go获取项目内所有proto_gRPC学习之三:初试GO版gRPC开发
  5. 信息学奥赛一本通 1040:输出绝对值 | OpenJudge NOI 1.4 02
  6. JavaScript的调用栈、回调队列和事件循环
  7. Linux编程(7)_gdb
  8. InfluxDB读写性能测试
  9. KEIL5打开KEIL4工程的方法
  10. webstorm下载安装教程
  11. 小技巧(10):使用Python绘制激活函数(Sigmoid、Tanh、ReLU、PReLU)图像
  12. idea中用java不能自动导包的解决办法
  13. WEB安全之DIV CSS基础(二):文字和文本的属性、列表样式和伪类超链接
  14. 互联网上要创业 选好域名很重要
  15. 2013 province java c-2 组素数
  16. Linux记录-sysctl.conf优化方案
  17. 美食家(2)- 家常红烧肉制作方法
  18. Angular JS (一)
  19. 用js实现时钟的实时运动(简单版)
  20. 探索式测试--第四章(全局探索式测试法)--读书笔记

热门文章

  1. 西祠胡同再遭出售,谁来拯救BBS?
  2. 从《学书津梁》看隶书学书轨程
  3. No mapping for the Unicode charactor exists in the target multi-byte code page解决办法
  4. ipa sigh resign最新版重签名步骤(亲测有效)
  5. lp0606分类信息网站,诚信商家联盟
  6. MapInfo 里程计算
  7. php反序列化—POP 链的构造利用
  8. 【网络基础】RIP基础概念
  9. python语法分析器(parser)源码阅读(三)
  10. 基于微信小程序的公务员考试刷题系统设计与实现