`

java动态代理小记

 
阅读更多

这两天没有什么工作任务,也不想去看所谓的业务,就利用闲来的时间复习一下java基础的东西吧。

下午复习了java动态代理,这里记下来。

java动态代理表面上并没有什么难的,难的是怎么去应用。

 

在讲动态代理之前,要问一个问题为什么要用动态代理?

考虑这样一个场景,在项目当中,我们通常有这样的需求,就是只有登陆的会员才具有下载,发表文章等权限,下载,发表文章等都对应着某些方法(如download()),在调用这些方法之前,我们都要先判断当前用户的是否有权去调用。最普通的做法是在download()中添加权限判断的代码:

download(){

    权限判断.....

    下载....

}

 

如果这样做,问题就来了,如果只有少数的几个操作需要权限判断那还行,只要挨个添加权限判断的代码就行了。但是一个项目中需要权限判断的地方海量去了,你一个个的去添加那还不累死?这个时候就要想办法减轻开发量,解决方法用两种,一种是通过过滤器(Filter),另外一种就是使用代理。

 

这里讲的是代理,代理又分为静态代理和动态代理,先说说静态代理:

我们提供一个代理类,该代理类拥有与目标类所要被调用的方法 相同的方法(方法签名),该代理类所生成的代理对象并含有目标对象的引用,调用代理对象的download方法时,会先判断权限,然后在调用目标对象的方法:

目标类:class TragetClass{

                   download(){

                            下载...... 

                    }

            }

代理类:class ProxyClass{

                   TragetClass target;

                    download(){

                            权限判断......

                            下载...... 

                    }

            }

静态代理和前面讲的常规方法有相同的弊端,就是代码量太大,有一万个目标方法,就要有一万个代理方法,太坑爹了。

 

不过幸好,java中利用reflect反射实现了动态代理,我们只需要写很少的代码就可以完成静态代理中一万个代理方法所实现的功能。

 

java动态代理用到了一个类(Proxy)和一个接口(InvocationHandler):

 

类Proxy.该类用于创建目标类的代理对象。

Proxy类常用的方法有:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) 该方法是一个static的方法,用于返回目标对象的代理对象。

参数loader就是加载目标类的classloader。

参数interfaces就是目标类所实现的接口。jdk动态代理是通过实现目标类所实现的接口来产生代理类并生成代理对象的。

参数handler,就是接口InvocationHandler实现类的对象,我们要自己去实现该接口,并实现其方法:

Object  invoke(Object proxy, Method method, Object[] args) 。InvocationHandler用官方话说就是:是代理实例的调用处理程序 实现的接口。用我的话来说就是,代理对象调用某个方法时,就会调用handler的invoke方法,该方法中拥有目标对象的引用,而且该方法中拥有权限判断等需要在目标方法被调用之前或之后进行的操作。在该方法中,会先进行权限判断,然后再真真正正的去调用目标对象对应的方法。

要实现动态代理,首要条件是目标类是面向接口的。比如说鸭子Duck类,它实现了两个接口Brid和Animal。

 

public interface Animal {
	public void eat();
	public void run();
}

 

public interface Bird {
	public void fly();
}

 

public class Duck implements Animal, Bird {

	@Override
	public void eat() {
		System.out.println("duck eat");
	}

	@Override
	public void run() {
		System.out.println("duck run");
	}

	@Override
	public void fly() {
		System.out.println("duck fly");
	}

}

  目标类已经实现了,下面是动态代理的实现部分:

首先是InvocationHandler的实现类:

public class MyInvocationHandler implements InvocationHandler{
	//target就是目标对象
	private Object target; 
	public void setObject(Object target) {
		this.target = target;
	}
	//proxy就是代理对象
	//method就是对应于在代理对象上调用的接口方法的 Method 实例,调用method.invoke()方法时才真真的去调用目标类的方法
	//args是要往目标方法中传入的参数,一般来源于代理对象调用方法时传入的参数
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//method.getName()可以返回要调用的目标方法的名称
		System.out.println("---------------目标方法:"+method.getName()+"执行之前do some thing---------------------");
		//调用目标类的目标方法,returnObj是目标方法返回的值
		Object returnObj = method.invoke(target, args);
		System.out.println("---------------目标方法:"+method.getName()+"执行之后do some thing---------------------");
		
		return returnObj;
	}
}

  下面是生成代理对象的方法类:

public class GetProxyObj  {
	private Object obj;
	private MyInvocationHandler handler;
	public GetProxyObj(Object obj,MyInvocationHandler handler){
		this.obj = obj;
		handler.setObject(obj);
		this.handler = handler;
	}
	//用于返回目标对象的代理类
	public Class getProxyClass(){
		//obj.getClass().getClassLoader() 返回目标对象的classloader
		// obj.getClass().getInterfaces()返回目标类所实现的接口数组
		Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces());
		return clazz;
	}
	//用于返回目标对象的代理对象
	//obj.getClass().getClassLoader() 返回目标对象的classloader
	// obj.getClass().getInterfaces()返回目标类所实现的接口数组
	//handler就是前面介绍的InvocationHandler实现类的实例对象
	public Object getProxyObject(){
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
	}
}

 测试类:

 

public class TestUtil {
	@Test
	public void testGetObject(){
		Duck duck = new Duck();
		MyInvocationHandler handler = new MyInvocationHandler();
		GetProxyObj obj = new GetProxyObj(duck,handler);
		System.out.println("代理类:"+obj.getProxyClass());
		//jdk动态代理是通过实现目标类所实现的接口来生成的,
		//所以Proxy.newProxyInstance生成的对象只能被转换为接口类型比如Animal或Bird,而不是目标类duck
		Animal animal = (Animal)obj.getProxyObject();
		animal.run();
		Bird bird = (Bird)obj.getProxyObject();
		bird.fly();
		
	}
}

 

输出信息:

代理类:class $Proxy4
---------------目标方法:run执行之前do some thing---------------------
duck run
---------------目标方法:run执行之后do some thing---------------------
---------------目标方法:fly执行之前do some thing---------------------
duck fly
---------------目标方法:fly执行之后do some thing---------------------

 

分享到:
评论

相关推荐

    java小记.rar

    这份"java小记.rar"压缩包很可能包含了作者多年从事Java Web开发的经验总结,可能包括代码示例、笔记、最佳实践等内容。 在Java Web开发中,Servlet是核心部分,它是一个Java类,用于扩展服务器的功能,处理HTTP...

    Java transient关键字使用小记

    在Java编程语言中,`transient`关键字是一个非常重要的概念,它与对象的序列化过程紧密相关。序列化是将一个对象的状态转换为字节流,以便存储或在网络中传输。当一个类实现了`Serializable`接口,该类的对象就可以...

    java 中时间和日期处理的小记

    时间和日期中常用到的几个类: java.util.Date, java.util.Calendar, java.util.GregorainCalendar, java.text.DateFormat, java.text.SimpleDateFormat

    linux+java+python3+numpy+stl的3d打印小记

    在本项目中,我们将探索如何利用Linux操作系统,Java编程语言,Python3,NumPy库以及STL文件格式来实现3D打印技术。这是一个跨学科的综合应用,涉及到计算机科学、软件开发以及制造技术。 首先,Linux是开源的操作...

    Linux boost库安装、编译问题小记

    环境: Linux s12084 2.6.9-67.ELsmp #1 SMP Wed ...小记一下。以备以后参考。  boost 库做得真好。在windows 平台, linux 平台下编译都很顺利。hp aCC 也宣称对 boost 1.35 完全支持 。  全部编译是很痛苦的过程

    C++编程小记,经典收藏

    ### C++11 新特性详解 #### 一、Auto 关键字的新用途 - **旧用途**:在C++11之前,`auto`关键字主要用于指定变量的存储期。 - **新用途**:C++11引入了类型推断的概念,`auto`变成了一个类型占位符,用于告诉...

    android开发小记

    在“android开发小记”这个主题中,我们可以深入探讨Android应用程序开发的相关知识,特别是通过查看提供的压缩文件中的代码示例。这些文件名如lesson_8_code.zip至lesson_20_codel.zip,以及不同的Mp3Player版本,...

    随笔小记.doc

    随笔小记.doc

    GeoStudio学习小记.pdf

    GeoStudio学习小记

    Flex整合J2EE开发小记+源码下载

    标题中的“Flex整合J2EE开发小记+源码下载”揭示了本文的主题,即如何将Adobe Flex技术与Java EE(J2EE)平台相结合,进行Web应用的开发。Flex是一种用于构建富互联网应用程序(RIA)的开源框架,它允许开发者创建...

    xstream使用小记

    《Xstream使用小记》 Xstream是一款Java库,它提供了简单且强大的方式来序列化和反序列化Java对象到XML,以及从XML回转换为Java对象。在本文中,我们将深入探讨Xstream的核心概念、使用场景以及如何通过实例进行...

    【咬人草小记,阅读附答案】 咬人草小记阅读答案.docx

    这篇文章是关于一种名为“咬人草”的植物的描述和作者对其独特特性的体验与思考。咬人草在新疆地区生长,是一种看似普通的草本植物,却有着不寻常的防御机制——它的叶瓣上有尖锐的小刺,一旦触碰,就会像被毒蜂蜇...

    python进行爬虫小记

    另外,网站可能会有反爬机制,如验证码、IP限制或User-Agent检查,这时需要合理设置headers,甚至使用代理IP。对于使用iframe的页面,需要切换到对应的frame才能进行操作。 总的来说,Python爬虫涉及网络请求、HTML...

    刘重威将军小记.docx

    刘重威将军小记.docx

    每日小记,记录账单和日记

    "每日小记"就是这样一款应用,它结合了记账和日记的功能,为用户提供了一个全面的生活记录平台。 首先,我们要了解记账的重要性。在个人财务管理中,记账是一个基础但至关重要的步骤。通过"每日小记",用户可以轻松...

    基于Java与SpringBoot的微信小程序开源记账账本设计源码

    本项目是一款基于Java和SpringBoot框架的微信小程序开源记账账本设计源码,包含705个文件,涵盖128个JavaScript文件、111个Java源文件、110个wxss样式文件、107个json配置文件、103个wxml模板文件、68个TypeScript...

    随笔小记文档及若干代码

    随笔小记的文档,自己记录的一些文档,仅供参考

    spring 小记

    这篇小记将深入探讨Spring的主要组件和关键特性,带你一步步走进Spring的世界。 首先,我们要理解Spring的核心——依赖注入。在传统的Java程序中,对象之间的依赖关系通常是硬编码的,这使得代码难以测试和维护。...

Global site tag (gtag.js) - Google Analytics