动态代理proxy与CGLib的区别
- 什么是代理?
- 静态代理与动态代理
- 静态代理实例
- JDK动态代理实例
- CGLib 简介
- CGLib 与JDK动态代理的区别
代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogate,代理的本意是一个人代表另一个人,或者一个机构代表另一个机构,采取行动,因而,代理和现实生活中的中介有很大的类似,你买房子、卖房子,可以自己去操作,但是需要了解和买卖房产无关的细节,如契税等,而找一个中介,则不用关心这些与买卖房产无直接关系的中间细节,只关心业务本身。
因而,我们可以把代理模式的适用,总结为:代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等
代理模式的用途,可以分为如下几种:
1)远程代理(Remote ):为远程对象的invoke提供代理,典型的例子如RMI或者EJB,都会在本地生成一个远程对象的stub。
2)虚拟代理(Virtual):如果需要创建一个资源消耗大的对象,则可以首先使用一个代理,使这个对象在需要时才真正创建。
3)Copy-On-Write 代理:把复制延迟到客户端需要时才采取行动
4)保护(Protect)
5)Cache代理
2.静态代理与动态代理
代理分为静态代理和动态代理
按照代理创建的时期,可以分为动态代理和静态代理:
静态代理:由程序员或者自动生成工具生成代理类,然后进行代理类的编译和运行。在代理类、委托类运行之前,代理类已经以.class的格式存在。
静态代理:在程序运行时,由反射机制动态创建而成。
3.静态代理实例
静态代理实例:
首先需要一个接口
- package net.battier.dao;
- /**
- * 定义一个账户接口
- *
- * @author Administrator
- *
- */
- public interface Count {
- // 查看账户方法
- public void queryCount();
- // 修改账户方法
- public void updateCount();
- }
然后是委托类,也就是接口的真正实现类,内涵主要的业务逻辑:
- package net.battier.dao.impl;
- import net.battier.dao.Count;
- /**
- * 委托类(包含业务逻辑)
- *
- * @author Administrator
- *
- */
- public class CountImpl implements Count {
- @Override
- public void queryCount() {
- System.out.println("查看账户方法...");
- }
- @Override
- public void updateCount() {
- System.out.println("修改账户方法...");
- }
- }
最后是代理类:
- CountProxy.java
- package net.battier.dao.impl;
- import net.battier.dao.Count;
- /**
- * 这是一个代理类(增强CountImpl实现类)
- *
- * @author Administrator
- *
- */
- public class CountProxy implements Count {
- private CountImpl countImpl;
- /**
- * 覆盖默认构造器
- *
- * @param countImpl
- */
- public CountProxy(CountImpl countImpl) {
- this.countImpl = countImpl;
- }
- @Override
- public void queryCount() {
- System.out.println("事务处理之前");
- // 调用委托类的方法;
- countImpl.queryCount();
- System.out.println("事务处理之后");
- }
- @Override
- public void updateCount() {
- System.out.println("事务处理之前");
- // 调用委托类的方法;
- countImpl.updateCount();
- System.out.println("事务处理之后");
- }
- }
运行:
- package com.mahoutchina.pattern.proxy;
- public class CountTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- CountImpl countImpl = new CountImpl();
- CountProxy countProxy = new CountProxy(countImpl);
- countProxy.updateCount();
- countProxy.queryCount();
- }
- }
从静态代理中可以看出:
1.接口:代理类需要实现一个接口,这个接口和委托类的接口是一样的,这样proxy才能和委托类行为表现一致
2. 方法(Method):由于接口限制,proxy类中也要有interface中的各个方法,这就造成了代码重复
4.JDK动态代理实例
动态代理类克服了proxy需要继承专一的interface接口,并且要实现相应的method的缺陷。从JDK 1.3以来,Java 语言通过java.lang.reflex库提供的三个类直接支持代理:
java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler 和Method.
Proxy类在运行时动态创建代理对象,这也是dynamic proxy的由来,下面是类图,其中最重要的是newProxyInstance,这个方法中,指明了将要代理的类的加载器,业务类接口,以及代理类要执行动作的调用处理器(InvokeHandler)
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
Ps:类加载器
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。
当系统有了一个代理对象之后,对原方法的调用会首先被分派到一个调用处理器(Invocation Handler).InvocationHandler 接口如下图所示:
代码:
接口:
- package com.mahoutchina.pattern.proxy.dynamicproxy;
- public interface BookFacade {
- public void addBook();
- public void deleteBook();
- }
实际业务类:
- package com.mahoutchina.pattern.proxy.dynamicproxy;
- public class BookFacadeImpl implements BookFacade {
- @Override
- public void addBook() {
- System.out.println("add book logic is running。。。");
- }
- @Override
- public void deleteBook() {
- System.out.println("delete book logic is running。。。");
- }
- }
动态代理类:
- package com.mahoutchina.pattern.proxy.dynamicproxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class BookFacadeProxy implements InvocationHandler {
- private Object target;
- /**
- *
- * @param target
- * @return
- */
- public Object bind(Object target) {
- this.target = target;
- // 取得代理对象
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object result=null;
- System.out.println("Proxy start...");
- System.out.println("method name:"+method.getName());
- result=method.invoke(target, args);
- System.out.println("Proxy end...");
- return result;
- }
- }
测试类:
- package com.mahoutchina.pattern.proxy.dynamicproxy;
- ublic class TestProxy {
- /**
- * @param args
- */
- public static void main(String[] args) {
- BookFacadeProxy proxy = new BookFacadeProxy();
- BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
- bookProxy.addBook();
- bookProxy.deleteBook();
- }
对于JDK 的Proxy,有以下几点:
1)Interface:对于JDK proxy,业务类是需要一个Interface的,这也是一个缺陷
2)Proxy,Proxy 类是动态产生的,这个类在调用Proxy.newProxyInstance(targetCls.getClassLoader, targetCls.getInterface,InvocationHander)之后,会产生一个Proxy类的实例。实际上这个Proxy类也是存在的,不仅仅是类的实例。这个Proxy类可以保存到硬盘上。
3) Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来
4) InvocationHandler: 这个类在业务委托类执行时,会先调用invoke方法。invoke方法再执行相应的代理操作,可以实现对业务方法的再包装
5 CGLib 简介
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
示例
业务类:
- package net.battier.dao;
- public interface BookFacade {
- public void addBook();
- }
- package net.battier.dao.impl;
- /**
- * 这个是没有实现接口的实现类
- *
- * @author student
- *
- */
- public class BookFacadeImpl1 {
- public void addBook() {
- System.out.println("增加图书的普通方法...");
- }
- }
代理:
- package net.battier.proxy;
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- /**
- * 使用cglib动态代理
- *
- * @author student
- *
- */
- public class BookFacadeCglib implements MethodInterceptor {
- private Object target;
- /**
- * 创建代理对象
- *
- * @param target
- * @return
- */
- public Object getInstance(Object target) {
- this.target = target;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.target.getClass());
- // 回调方法
- enhancer.setCallback(this);
- // 创建代理对象
- return enhancer.create();
- }
- @Override
- // 回调方法
- public Object intercept(Object obj, Method method, Object[] args,
- MethodProxy proxy) throws Throwable {
- System.out.println("事物开始");
- proxy.invokeSuper(obj, args);
- System.out.println("事物结束");
- return null;
- }
- }
测试;
- package net.battier.test;
- import net.battier.dao.impl.BookFacadeImpl1;
- import net.battier.proxy.BookFacadeCglib;
- public class TestCglib {
- public static void main(String[] args) {
- BookFacadeCglib cglib=new BookFacadeCglib();
- BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
- bookCglib.addBook();
- }
- }
动态代理proxy与CGLib的区别的更多相关文章
- 动态代理jdk和cglib的区别
学习来源贴:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类, ...
- 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance
浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- java动态代理--proxy&cglib
大纲 代理 proxy cglib 小结 一.代理 为什么要用代理?其实就是希望不修改对象的情况下,增强对象. 静态代理: 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法, ...
- Spring的两种代理JDK和CGLIB的区别浅谈
一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...
- 静态代理和动态代理(jdk/cglib)详解
1.静态代理模式 代理模式上,基本上有Subject角色,RealSubject角色,Proxy角色.其中:Subject角色负责定义RealSubject和Proxy角色应该实现的接口:RealSu ...
- 动态代理 Proxy InvocationHandler
前奏 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常 ...
- java中的动态代理Proxy
动态代理是java语言的一个神奇的地方,不是很好理解,下面来看看关键的地方. InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHa ...
随机推荐
- 什么是JAVAbean
JavaBean 是一种JAVA语言写成的可重用组件.为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器.JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性 ...
- Extjs 一些配置以及方法
1.例如想要实现以下功能,本来model中只有用户的firstname和lastname,但是在grid中展示还需要展示用户姓名,或者只展示用户姓名
- python for mysql
# -*- coding: utf-8 -*- '''python coded by written in 2016/8/31 Used for get win os log for each win ...
- Hadoop namenode无法启动
最近遇到了一个问题,执行start-all.sh的时候发现JPS一下namenode没有启动 每次开机都得重新格式化一下namenode才可以 其实问题就出在tmp文件,默 ...
- DELPHI XE5 与SQLITE
最近一次使用DELPHI做项目是使用DELPHI2009,为了访问本地数据库方便,使用ACCESS数据库,不需要安装驱动,(WINDOWS自带),但是ACCESS数据库的性能确实很糟糕,通过ADO连接 ...
- iis7 64位 操作excel的一系列问题(未完待续)
查了半天发现是IIS跑在64位环境下引起的.而64位下,是木有Access数据库的驱动的(包括Excel也不行). 解决办法是:在目标网站的应用程序池中选择高级设置,然后将启用32位应用程序设置为tr ...
- MI卡UID
卡号是根据第0扇区第0块的UID,高位和低位互换后转10进制后出的数字.一般读卡器都会在左边补0补足10位.
- lua定义一个简单的类
classA.lua: classA = { a = , b = , --__index = classA; }; classA.__index = classA; function classA:n ...
- java中常见的几种异常
算术异常类:ArithmeticExecption空指针异常类:NullPointerException类型强制转换异常:ClassCastException数组负下标异常:NegativeArray ...
- 调试必备---console
一.前言 可能很多前端er会习惯用 alert() 方法来进行调试,但我相信更多的人会使用 console.log(),因为它不用像 alert() 那样会有字符长度限制,也不会中止程序的进程,而且还 ...