Java反射机制(四):动态代理

简介:

一、静态代理

在开始去学习反射实现的动态代理前,我们先需要了解代理设计模式,那何为代理呢?

代理模式: 为其他对象提供一种代理,以控制对这个对象的访问。

先看一张代理模式的结构图:



简单的理解代理设计: 一个操作接口有两个子类,其中一个真实主题的实现类,另一个是代理类,代理实现类要完成比真实主题实现类更多的内容,而且本身还需要处理一些与具体业务有关的程序代码。

静态代理示例:

package org.chen.yuan.prox;


interface Subject
{
    public String say(String name, int age);
}


class RealSubject implements Subject
{
    @Override
    public String say(String name, int age)
    {
        return "姓名: " + name + ", 年龄: " + age;
    }
}


class ProxSubject implements Subject
{
    private Subject sub = null;


    public ProxSubject(Subject sub)
    {
        this.sub = sub;
    }


    @Override
    public String say(String name, int age)
    {
        this.preRequest(); // 在调用真实角色操作之前所附加的操作


        String info = sub.say(name, age); // 真实角色所完成的事情


        this.postRequest(); // 在真实角色操作之后所附加的操作


        return info;
    }


    // 自定义的方法,在真实方法执行之前调用的方法
    private void preRequest()
    {
        System.out.println("pre request");
    }


    // 自定义的方法,在真实方法执行之后调用的方法
    private void postRequest()
    {
        System.out.println("post request");
    }

}


public class DynaProxyDemo
{
    public static void main(String[] args)
    {
        Subject sub = new ProxSubject(new RealSubject());
        String info = sub.say("沉缘", 25);
        System.out.println(info);
    }
}

输出:

pre request
post request
姓名: 沉缘, 年龄: 25


说明:

在实现代理角色对象时,最重要的一点是要有一个真实对象的引用。通过这个引用在代理对象中去调用真实对象的方法,并且可以自定义一些其他的操作,比如本例子中的preRequest()和postRequest(),他们分别在之前和之后被调用。

大家看代码后可以发现,这里并没有用到反射的知识。而且对于上面的例子来说,真实角色对象是事先必须已经存在的,并且必须是作为代理对象的内部属性。
如果这样的话,有一个真实角色那就必须在代理角色中有一个他的引用,如果在不知道真实角色的情况下又需要怎么办?这就需要"动态代理"来解决了。


二、 动态代理(以下内容摘自博文http://blog.csdn.net/a396901990/article/details/26015977)

静态代理中,一个代理类只能为一个接口服务,那么如果现在有很多个接口的话,则肯定要写很多的代理类了,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,此时,肯定有很多重复的代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能或者说去动态的生成这个代理类,那么此时就必须使用动态代理完成。

动态代理实现所需要的API:

Java动态代理类位于java.lang.reflect包下,主要有以下一个接口和一个类:

1.  InvocationHandler接口:    该接口中仅有一个方法

public object invoke(Object proxy, Method method, Object[] args)

在实际使用时,proxy一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象的invoke方法在代理类中动态实现。


2.  Proxy类:  该类即为动态代理类,这里只介绍一下newProxyInstance()这个方法

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

ClassLoader loader: 类加载器

Class<?>[] interfaces: 得到全部的接口

InvocationHandler h: 得到InvocationHandler接口的子类实例

这个方法是最主要的方法,它会返回代理类的一个实例,返回后的代理类可以当做被代理类使用。
如果你要想得到一个加载器的对象,则肯定使用Class类完成。


实现动态代理需4步骤:

1)创建一个实现接口InvocationHandler的类,它必须实现invoke方法。

2)  通过Proxy的静态方法newProxyInstance创建一个代理

3)  创建被代理的类以及接口

4)  通过代理调用方法

下面看这个例子具体说明如何通过上面的4个步骤来建立一个动态代理:


步骤1和步骤2合并写在一个类中,命名为DynamicProxy

public class DynamicProxy implements InvocationHandler {

	// 需要被代理类的引用
	private Object object;

	// 通过构造方法传入引用
	public DynamicProxy(Object object) {
		this.object = object;
	}
	// 定义一个工厂类,去生成动态代理
	public Object getProxy() {
		// 通过Proxy类的newProxyInstance方法动态的生成一个动态代理,并返回它
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object
				.getClass().getInterfaces(), this);
	}
	// 重写的invoke方法,这里处理真正的方法调用
	@Override
	public Object invoke(Object obj, Method method, Object[] args)
			throws Throwable {
		
		beforeDoing();
		
		Object invoke = method.invoke(object, args);
		
		afterDoing();
		
		return invoke;
	}
	
	public void beforeDoing() {
		System.out.println("before ............");
	}
	
	public void afterDoing() {
		System.out.println("after ............."+"\n");
	}
}
该类实现了InvocationHandler接口,并且自定义了一个getProxy()方法去调用Proxy类的newProxyInstance()去生成一个动态代理。
步骤3:创建被代理的类以及接口

//真实角色对象,继承自抽象角色,重写定义的方法。
public class RealSubject implements Subject1,Subject2{

	//Subject1接口中的方法
	@Override
	public void request() {
		System.out.println("this is real subject");
	}

	//Subject1接口中的方法
	@Override
	public void ask() {
		System.out.println("this is real ask");
		
	}
	
	//Subject2接口中的方法
	@Override
	public void request2() {
		System.out.println("this is real subject2");
		
	}
}

这个类就是我们需要被代理的类,他继承了两个接口分别是Subject1,Subject2

interface Subject1 {
	
	public  void request();
	
	public void ask();
}
interface Subject2 {
	
	public  void request2();
}

4.通过代理调用方法

接下来在main方法中通过动态生成的代理来调用方法

public static void main(String[] args) {
		
		//需要被代理的类
		RealSubject realSubject = new RealSubject();
		//用于创建动态代理的类,将被代理类的引用传递进去
		DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
		
		//通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
		Subject1 s1 = (Subject1) dynamicProxy.getProxy();
		s1.request();
		s1.ask();
		
		//通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
		Subject2 s2 = (Subject2) dynamicProxy.getProxy();
		s2.request2();
	}

输出:

before ............
this is real subject
after .............

before ............
this is real ask
after .............

before ............
this is real subject2
after .............


简单介绍动态代理内部实现原理:

例子看完了,肯定有如下疑问:

动态代理在哪里应用了反射机制?仅仅通过一个InvocationHandler接口和一个Proxy类的newProxyInstance方法是如何动态的生成代理?

下面就来简单的分析一下InvocationHandler,和Proxy的newProxyInstance方法是如何在运行时动态的生成代理的:

先看newProxyInstance是如何定义的:

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

这里需要传入3个参数。先看第二个参数,传入一个接口类型的Class数组。

上面例子中传入的参数是object.getClass().getInterfaces()

object是被代理对象,这个参数就是通过反射拿到被代理对象的所有接口

在上面例子中就是我们定义的Subject1,Subject2接口了

有了接口数组,就可以通过类似下面的代码使用反射拿到接口中的所有方法:

for (interface infce : interfaces[]) {
	Method[] methods = infce.getMethods();
	for (Method m : method) {
		m.getName();
	}
}
在正常情况下,知道了被代理的接口和接口里面的方法就可以去生成代理类了。
大概就是下面这种的一个简单的实现:一个很固定的套路,只要知道实现接口和方法就仿照写出。
public class ProxySubject implements Subject{
	
        private RealSubject realSubject;	
    
	@Override
	public void request() {
		
		realSubject.request();  
	}
}
动态代理还会在代理的方法中做一些其他的操作,如添加日志,时间,权限等操作。这时候就要靠InvocationHandler接口中的invoke方法。看看例子中如何实现的。

@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
		
	beforeDoing();
		
	Object invoke = method.invoke(object, args);
		
	afterDoing();
		
	return invoke;
}

这些代码是我们自定义的,需要实现什么操作就写在里面。
这段代码存在于Invocationhandler对象中,这个对象会在调用Proxy的newProxyInstance的方法中传递进去。
这时候可以通过反射知道被调用方法的名字等信息,之后还是通过字符串的形式拼接处类似下面的动态代理类

public class ProxySubject implements Subject{
	
        private RealSubject realSubject;	
	@Override
	public void request() {
		Methond md = Subject.getMethod("methodName");
		handler.invoke(this, md);  
	}
}

这个大概就是根据传递的接口对象和InvocationHandler结合后应该生成的代理类。但现在的问题是如何去动态的生成上面这样的代理类。
答案是使用字符串拼接的方式。
从看上面的代码可以看出,除了接口和调用方法不同其他都相同。而且我们已经通过反射获得了方法和接口名字,这样就可以按着这个“套路”去用字符串拼接成这样的一类。
大概就是下面这种代码:

  String source = "package com.gxy.proxy;" + rt 
  
			    + "public class "+ClassName+"implements "+InterfaceName+ rt  
			    + "{" + rt  
			    + 		"private "+ ClassName + ClassName.toLowerCase()+" ; " + rt
			         
			    +       "@Override"
			    +		"public Void "+InterfaceName+ "()" + rt  + " {"
			    + 			"Method md = "+InterfaceName+".getMethod("+ methodName+");" +rt 
			    +           "hander.invoke(this, md);" + rt
			    +		"}" + rt 
			    + "}";
用反射生成的出来类名,接口名,方法名去动态的创建这样一个类的字符串。
之后就特定的方法去将这个字符串生成成类。在用反射把这个类取出来。这样就有了这个“动态”生成的代理类了。

相关文章
|
7月前
|
设计模式 人工智能 安全
AQS:Java 中悲观锁的底层实现机制
AQS(AbstractQueuedSynchronizer)是Java并发包中实现同步组件的基础工具,支持锁(如ReentrantLock、ReadWriteLock)和线程同步工具类(如CountDownLatch、Semaphore)等。Doug Lea设计AQS旨在抽象基础同步操作,简化同步组件构建。 使用AQS需实现`tryAcquire(int arg)`和`tryRelease(int arg)`方法以获取和释放资源,共享模式还需实现`tryAcquireShared(int arg)`和`tryReleaseShared(int arg)`。
408 32
AQS:Java 中悲观锁的底层实现机制
|
5月前
|
人工智能 缓存 安全
Java中的反射机制:深入探索与应用
Java反射机制是程序运行时动态获取类信息并操作类成员的特性,具备高度灵活性,但也伴随性能与安全风险。本文详解反射的基本用法、高级应用及最佳实践,助你掌握这一强大工具的正确使用方式。
164 0
|
7月前
|
人工智能 Java 关系型数据库
Java——SPI机制详解
SPI(Service Provider Interface)是JDK内置的服务提供发现机制,主要用于框架扩展和组件替换。通过在`META-INF/services/`目录下定义接口实现类文件,Java程序可利用`ServiceLoader`动态加载服务实现。SPI核心思想是解耦,允许不同厂商为同一接口提供多种实现,如`java.sql.Driver`的MySQL与PostgreSQL实现。然而,SPI存在缺陷:需遍历所有实现并实例化,可能造成资源浪费;获取实现类方式不够灵活;多线程使用时存在安全问题。尽管如此,SPI仍是Java生态系统中实现插件化和模块化设计的重要工具。
275 0
|
5月前
|
人工智能 前端开发 安全
Java开发不可不知的秘密:类加载器实现机制
类加载器是Java中负责动态加载类到JVM的组件,理解其工作原理对开发复杂应用至关重要。本文详解类加载过程、双亲委派模型及常见类加载器,并介绍自定义类加载器的实现与应用场景。
262 4
|
5月前
|
人工智能 安全 Java
掌握Java反射:在项目中高效应用反射机制
Java反射是一种强大功能,允许程序在运行时动态获取类信息、创建对象、调用方法和访问字段,提升程序灵活性。它在框架开发、动态代理、注解处理等场景中广泛应用,如Spring和Hibernate。但反射也存在性能开销、安全风险和代码复杂性,应谨慎使用。
134 0
|
6月前
|
人工智能 Java
Java中的反射机制:深入探索与应用
本文介绍了Java反射机制的基本概念、用途及其实现方式。反射机制允许程序在运行时动态获取类的属性和方法,并调用它们,适用于处理私有成员或权限受限的情况。文章详细讲解了`Class`类的功能,包括获取类的方法、属性、注解、构造器等信息,以及通过四种方式获取`Class`对象的示例代码。此外,还探讨了类加载器、继承关系判断、动态代理等高级内容,展示了如何在运行时创建接口实例并处理方法调用。文末提供了完整的代码示例以加深理解。
156 0
Java中的反射机制:深入探索与应用
|
7月前
|
Java 区块链 网络架构
酷阿鲸森林农场:Java 区块链系统中的 P2P 区块同步与节点自动加入机制
本文介绍了基于 Java 的去中心化区块链电商系统设计与实现,重点探讨了 P2P 网络在酷阿鲸森林农场项目中的应用。通过节点自动发现、区块广播同步及链校验功能,系统实现了无需中心服务器的点对点网络架构。文章详细解析了核心代码逻辑,包括 P2P 服务端监听、客户端广播新区块及节点列表自动获取等环节,并提出了消息签名验证、WebSocket 替代 Socket 等优化方向。该系统不仅适用于农业电商,还可扩展至教育、物流等领域,构建可信数据链条。
|
9月前
|
缓存 Dubbo Java
理解的Java中SPI机制
本文深入解析了JDK提供的Java SPI(Service Provider Interface)机制,这是一种基于接口编程、策略模式与配置文件组合实现的动态加载机制,核心在于解耦。文章通过具体示例介绍了SPI的使用方法,包括定义接口、创建配置文件及加载实现类的过程,并分析了其原理与优缺点。SPI适用于框架扩展或替换场景,如JDBC驱动加载、SLF4J日志实现等,但存在加载效率低和线程安全问题。
441 7
理解的Java中SPI机制
|
7月前
|
人工智能 JavaScript Java
Java反射机制及原理
本文介绍了Java反射机制的基本概念、使用方法及其原理。反射在实际项目中比代理更常用,掌握它可以提升编程能力并理解框架设计原理。文章详细讲解了获取Class对象的四种方式:对象.getClass()、类.class、Class.forName()和类加载器.loadClass(),并分析了Class.forName()与ClassLoader的区别。此外,还探讨了通过Class对象进行实例化、获取方法和字段等操作的具体实现。最后从JVM类加载机制角度解析了Class对象的本质及其与类和实例的关系,帮助读者深入理解Java反射的工作原理。
197 0
|
8月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
823 1