文档章节

Java动态代理(Proxy+Cglib)详解

Yalong
 Yalong
发布于 2017/08/31 09:04
字数 1862
阅读 29
收藏 1
点赞 0
评论 0

对本片内容有不明白的地方请提问我,我如果也不懂会再去学习掌握然后再补充完善,谢谢。

        首先接触动态代理是在学习Struts框架还是Spring框架这个我已经记不清了。但在学习Spring框架的时候知道其AOP(面向切面编程)技术是依赖动态代理实现的。大致方式了解,但具体细节就不明白了。最近在看MyBatis源码,一开始就遇到了一个地方使用到了Java的动态代理机制。所以借此机会,对Java的动态代理机制进行探究和分析以及学习,一方面再遇到动态代理的问题时能够明白实现细节,再一个在日后编程时也能够借助动态代理来完成一些特定任务。

        此篇笔记是基于以下博客的内容,加以自己的分析理解来写的。自己写技术文章功力不行,如果看不明白可以参考原博客的内容,或许能更容易明白些。
http://www.cnblogs.com/xiaoluo501395377/p/3383130.html
 

我一般习惯于先了解相关类结构及关系,然后再看内部的方法和实现细节代码。要使用Java动态代理,必须使用接口InvocationHandler和类Proxy
先看下Proxy这个类,如果先看InvocationHandler接口的话不容易明白。Proxy类在JDK1.7的文档描述如下:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.(Proxy类提供了一些静态方法用来创建动态代理的类和它们的实例对象。Proxy同时还是所有通过这些静态方法创建出来的代理类的父类。)

文档中还提供了如何使用Proxy来创建动态代理类:

//To create a proxy for some interface Foo:
    //创建一个InvocationHandler接口的实例
     InvocationHandler handler = new MyInvocationHandler(...);
        //创建代理类的Class信息
     Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
        //根据Class信息实例化代理类的对象
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

//or more simply://另一种简易方式
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

        由上面代码可知,Proxy类可以先使用getProxyClass来获取代理类的Class,然后再根据Class信息实例化具体的代理类实例。也可以直接使用Proxy类的newProxyInstance方法来直接获取一个代理类对象。我们直接看newProxyInstance这个方法

public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[] interfaces,
   InvocationHandler h
)

该方法有三个参数:

ClassLoader loader:类加载器,声明使用哪个加载器来加载生成后的代理类对象。
Class<?>[] interfaces:一组接口的Class信息,声明我们要生成的代理类对象实现了这些接口,既然实现                                     了这些接口,那么就可以调用代理类对象中的这些接口方法。
InvocationHandler h:调用处理器。即在调用代理类的接口方法时,会关联到我们指定的调用处理器。我                                       们在调用处理器中就可以在方法执行前后执行我们要求的任务。

总的来说,ClassLoader决定了用哪个加载器加载生成的代理对象。interfaces决定了代理类能执行哪些方法。invocationHandler决定了在执行调用方法时,我们能在执行前后做什么。

然后我们再看下InvocationHandler这个接口信息,先看下JDK中如何描述:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

代理类实例的调用处理器实现了该接口。每一个代理类实例都关联了一个调用处理器。当代理类调用某个方法时,这个方法调用会被分发到我们关联的调用处理器中进行调用执行(大致意思就是这,英文高手见谅...)。

InvocationHandler接口只有一个invoke方法。也就是说我们实现的调用处理器都要实现invoke方法。

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

下面开始用实例来示范Java动态代理具体怎么使用。

动态代理是基于接口的,所以我们先声明一个接口:

package com.yalonglive.java.proxy;

import java.util.List;

/*
 * Jdbc服务接口,用来执行query,update,delete,insert等方法。
 */
public interface JdbcService {
	
	/**
	 * 查询单个对象
	 * @param id
	 */
	Object queryObject(String id);
	
	/**
	 * 查询对象列表
	 * @param entity
	 */
	List queryList(String entity);

}

然后声明接口的实现类,也就是我们要代理的对象:

package com.yalonglive.java.proxy;

import java.util.ArrayList;
import java.util.List;

public class JdbcServiceImp implements JdbcService {

	@Override
	public Object queryObject(String id) {
		System.out.println("执行queryObject方法");
		return new Object();
	}

	@Override
	public List queryList(String entity) {
		System.out.println("执行queryList方法");
		return new ArrayList<Object>(0);
	}

}

在接口中,我们定义了JdbcService可以执行的两个方法queryObject和queryList。以及对应的实现类,接下来我们开始创建代理对象,我这里按照JDK文档中的方法创建:

/**
	 * 测试代理类:先创建代理类信息
	 * @throws Exception
	 */
	public static void testProxy() throws Exception {
		
		//声明一个要被代理的对象
		final JdbcService jdbcService = new JdbcServiceImp();
		
		//创建调用处理器,将被代理的对象和调用处理器进行关联
		InvocationHandler handle = new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				
				System.out.println("方法调用前执行代码段");
				
				//代理类在调用任何注册过的接口方法时都会通过其调用处理器来执行对应的方法(通过反射),那么在此处就可以定义一些自定义操作了
				method.invoke(jdbcService, args);
				
				System.out.println("方法调用后执行代码段");
				
				return null;
			}
		};
		
		//创建代理类Class信息
		Class proxyClass = Proxy.getProxyClass(JdbcService.class.getClassLoader(),new Class[] {JdbcService.class});
		
		//生成代理类对象
		JdbcService proxyObject = (JdbcService)proxyClass.getConstructor(new Class[]{InvocationHandler.class}).newInstance(handle);
		
		//执行代理类的方法
		proxyObject.queryObject("1");
		
		//输出:
		//方法调用前执行代码段
		//执行queryObject方法
		//方法调用后执行代码段
	}

        其实在写上面的Demo之前,我在想代理对象,调用处理器,被代理对象之间的关系是什么,他们是如何进行交互的,为什么执行代理类的方法就会执行被代理类的方法。
        从上面的Demo可以看出。实际上是:代理类<------>调用处理器<------>被代理类。也就是说,创建代理类的时候引用了调用处理器,而在创建调用处理器对象的时候引用了被代理类对象。所以代理类对象就可以调用被代理类的方法了,调用处理器就是中间的“桥梁”,同时也是我们编写自定义逻辑代码的地方。

        到这里就能知道Java动态代理的实现方法了,改如何使用。大家如果有不明白的地方或者想知道的内容可以留言我,我会继续完善,后面会继续补充Proxy这个类内部的一些源码分析。

通过上面的讲解,我们只要能获取到对象的接口信息,就能创建一个该对象的代理类来对其进行代理操作,但是还有的时候我们的操作类并没有实现相关接口,但我们还有代理该对象的需求,此时,使用Proxy类的方式就不行了,需要使用其他办法。

 

© 著作权归作者所有

共有 人打赏支持
Yalong
粉丝 1
博文 5
码字总数 6148
作品 0
西安
【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank
06/21
0
0
动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码。当前有很多开源框架可以完成这些功能,如ASM,Javassist。 动态代理机制详解(JDK 和CGLIB,Javassist,ASM...

素雷
2017/10/19
0
0
Instrumentation 实践详解

利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,有了这样的功能,开发者就可以实现更为灵活的和 了,这样的特性实际上提供了,使得开发者无需对 ...

陶邦仁
2014/12/30
0
0
Spring AOP切点表达式详解

简介 面向对象编程,也称为OOP(即Object Oriented Programming)最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的。通过面向对象编程,不同的模板可以相互组装,从而实现更为...

张旭峰
06/05
0
2
深度探讨Java字节代码的操纵方法

本文为IBM工程师成富编写的《Java深度历险》的第一部分Java字节代码的操纵,像这样Java语言的深度理解和运用还没有很多文章,我们把他奉献给读者,希望读者们喜欢。 51CTO编者按:我们曾给大...

mj4738
2011/11/02
0
0
Java深度理解——Java字节代码的操纵

导读:Java作为业界应用最为广泛的语言之一,深得众多软件厂商和开发者的推崇,更是被包括Oracle在内的众多JCP成员积极地推动发展。但是对于 Java语言的深度理解和运用,毕竟是很少会有人涉及...

刘学炜
2012/01/13
0
0
Java开发者不会这些永远都只能是三流程序员,细数一下你是不是?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

美的让人心动
04/16
0
0
JAVA代理模式与动态代理模式

1、代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介...

lee123lee
2013/08/30
0
0
Java代理-Javassist

代理 (agent) 是在你的main方法前的一个拦截器 (interceptor),也就是在main方法执行之前,执行agent的代码。agent的代码与你的main方法在同一个JVM中运行,并被同一个system classloader装载...

ksfzhaohui
2014/09/02
0
0
Java类动态加载(二)——动态加载class文件

想要在jvm启动后,动态的加载class类文件,我们首先需要了解Instrumentation、Attach、Agent、VirtualMachine、ClassFileTransformer这几个类的用法和他们之间的关系。 Java的com.sun.tools...

2k10
2015/03/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

python上传文件

//注意 <form action="/login/" method="post" enctype="multipart/form-data"> f=request.FILES.get('fafa') ff=open(f.name,mode='wb') for i in f.chunks(): ff.write(i) ff.close()......

南桥北木
4分钟前
0
0
CISCO VPN Client Reason 442 WIN8/10错误解决方案

http://jdkleo.iteye.com/blog/2163493 引用 http://my.oschina.net/cloudcoder/blog/220391?p={{currentPage 1}} 在使用cisco VPN 客户端登录时,产生Reason 442:Failedto enable Virtual......

chenfj_fer
8分钟前
0
0
信号量有没有容量限制?

之前一直误以为信号量初始化的时候那个初始化的值是信号量的“容量”,昨天同事指出了我的错误,最初我是不相信的,经过以下代码实践,证明了我的错误: Java版: import java.util.concurr...

锟斤拷烫烫烫
11分钟前
0
0
【RocketMQ】Message存储笔记

概述 消息中间件存储分为三种,一是保存在内存中,速度快但会因为系统宕机等因素造成消息丢失;二是保存在内存中,同时定时将消息写入DB中,好处是持久化消息,如何读写DB是MQ的瓶颈;三是内...

SaintTinyBoy
22分钟前
0
0
Android应用Context详解及源码解析

Android应用Context详解及源码解析 本文定位:优质文章收集 本文转载 1 背景 今天突然想起之前在上家公司(做TV与BOX盒子)时有好几个人问过我关于Android的Context到底是啥的问题,所以就马...

lichuangnk
54分钟前
0
0
PostgreSQL的昨天今天和明天

PostgreSQL 是一种非常复杂的对象-关系型数据库管理系统(ORDBMS), 也是目前功能最强大,特性最丰富和最复杂的自由软件数据库系统。有些特性甚至连商业数据库都不具备。 这个起源于伯克利(...

闻术苑
59分钟前
1
0
Mysql对自增主键ID进行重新排序

1,删除原有主键: ALTER TABLE `table_name` DROP `id`; 2,添加新主键字段: ALTER TABLE `table_name` ADD `id` MEDIUMINT( 8 ) NOT NULL FIRST; 3,设置新主键: ALTER TABLE `table_nam......

niithub
今天
0
0
福利篇:免费csdn vip账号分享

分享一个发布免费csdn vip账号的网站:啰嗦vip www.lostvip.com , 各种软件开发类的视频教程:慕课网、动脑学院、黑马各大培训机构VIP视频教程,非常不错!

在水一方发盐人
今天
0
0
Nginx+Tomcat搭建高性能负载均衡集群

一、 工具   nginx-1.8.0   apache-tomcat-6.0.33 二、 目标   实现高性能负载均衡的Tomcat集群:    三、 步骤   1、首先下载Nginx,要下载稳定版:      2、然后解压两个Tom...

码代码的小司机
今天
0
0
Centos7编译安装ntp-4.2.8p11

Centos7编译安装ntp-4.2.8p11 背景 因公司做等保评级,在进行安全漏洞检测时发现ntp需要升级到ntp-4.2.7p25以上版本,经过一番搜索,没有该版本及新版本ntp的yum安装包,所以只能编译安装了,...

阿dai
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部