JAVA反射与代理

一,JAVA反射机制

动态地获得类的信息,动态地调用对象的方法。

反射机制的功能

①运行时构造类的对象,示例如下:

Object object = Class.forName("AClass").getConstructor(new Class[]{}).newInstance(new Object[]{});
//new Class[]{} 表示构造器的参数类型, new Object[]{}表示构造的参数的值,示例中二者均为空
AClass类中必须有无参的构造方法

②运行时判断类所具有的成员变量和方法,示例如下:

Class<String> type = String.class;
//Class<?> type2 = Class.forName("java.lang.String");
Method[] methods = type.getDeclaredMethods();
获取java.lang.String 类中定义的方法。

③生成动态代理,后面会详细介绍。


二,代理模式

RealSubject称委托类,Proxy称为代理类,它们实现同一个接口,故这两个类中有相同的方法。代理类关联着委托类,即它持有委托类的引用,代理类中方法的实现都是通过委托类的对象调用委托类的方法来实现的,也即方法的真正实现是在委托类中。参考代码如下:

public interface Subject {
	public void operation();
}

public class RealSubject implements Subject{
	@Override
	public void operation() {
		//do something
	}
}

public class Proxy implements Subject{

	RealSubject real;//持有委托类的引用
	
	public Proxy(RealSubject real) {
		this.real = real;
	}
	@Override
	public void operation() {
		real.operation();//委托类的对象调用委托类的方法
	}
}


三,静态代理与动态代理的区别

静态代理中的代理类是事先编写好的,获得静态代理类的对象通过 new 来获得。示例代码如下:

import java.util.Date;

public interface HelloService {
	public Date getTime();
}

//委托类
public class HelloServiceImpl implements HelloService{

	@Override
	public Date getTime() {
		return new Date();
	}
}

//代理类
public class HelloServiceProxy implements HelloService{

	private HelloService helloService;//委托类的引用
	public HelloServiceProxy(HelloService helloService) {
		this.helloService = helloService;
	}
	@Override
	public Date getTime() {
		return helloService.getTime();//调用委托类的getTime()方法
	}
}

public class Client1 {
	public static void main(String[] args) {
		HelloService helloService = new HelloServiceImpl();
		HelloService helloServiceProxy = new HelloServiceProxy(helloService);//创建静态代理类对象
		Date date = helloServiceProxy.getTime();
		System.out.println(date);
	}
}
1,从Client1中可以看出,静态代理类的对象是通过new关键字创建的,并将委托类对象作为构造函数的参数传递---前面已经提到,代理类需要持有委托类对象的引用。

2,对于客户端而言,它即需要与委托类(HelloServiceImpl)打交道,又需要与代理类打交道(HelloServiceProxy)。


而动态代理则是由JDK中java.lang.reflect包中的Proxy类和InvocationHandler接口在程序运行时动态生成。

Proxy类提供的创建动态代理类及其对象的静态方法如下:

public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces)throws IllegalArgumentException{
.....}
ClassLoader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的接口(上面已经提到,代理类和委托类实现相同的接口)

创建动态代理类的对象的方法如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
			throws IllegalArgumentException{
	....}
ClassLoader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的接口,参数handler指定与动态代理类关联的InvocationHandler对象。

注意,与静态代理类关联的是委托类的对象,而与动态代理类关联的是InvocationHandler对象。

动态代理类由JDK的类库提供了支持--java.lang.reflect包中的Proxy类,创建动态代理类的对象的两种方式如下:

方式一

InvocationHandler handler = new InvocationHandler() {
 ...           
}
//new Class[]{HelloService.class} 表示的是动态代理类需要实现的接口
Class classType = Proxy.getProxyClass(HelloService.class.getClassLoader(), new Class[]{HelloService.class});

//创建动态代理类对象,new Class[]{InvocationHandler.class}表示动态代理类所关联的类型
//new Object[]{handler} 表示提供给构造方法的参数--InvocationHandler类的对象
 HelloService dynamicProxy = (HelloService)classType.getConstructor(new Class[]{InvocationHandler.class})
.newInstance(new Object[]{handler});


 根据这种构造动态代理的方式可以看出,正如上面提到的: 
与静态代理类关联的是委托类的对象,而与动态代理类关联的是InvocationHandler对象。 

方式二

InvocationHandler handler = new InvocationHandler() {
.......
    };

HelloService dynamicProxy = (HelloService)Proxy.newProxyInstance(HelloService.class.getClassLoader(), new Class[]{HelloService.class},  handler);

使用动态代理类的示例代码如下

import java.util.Date;

public interface HelloService {
	public Date getTime();
}

//委托类
public class HelloServiceImpl implements HelloService{

	@Override
	public Date getTime() {
		return new Date();
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HelloServiceProxyFactory {
	public static HelloService getHelloServiceProxy(final HelloService helloService) throws Exception{
		InvocationHandler handler = new InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
                        //方法调用前的预处理
                        Object result = method.invoke(helloService, args);
                        //方法调用之后的后续处理
                        return result;
			}
		};
		
//		Class classType = Proxy.getProxyClass(HelloService.class.getClassLoader(), new Class[]{HelloService.class});
//		HelloService dynamicProxy = (HelloService)classType.getConstructor(new Class[]{InvocationHandler.class}).newInstance(new Object[]{handler});
		HelloService dynamicProxy = (HelloService)Proxy.newProxyInstance(HelloService.class.getClassLoader(), new Class[]{HelloService.class},  handler);
		return dynamicProxy;
	}
}

public class Client2 {
	public static void main(String[] args) throws Exception {
		HelloService helloService = new HelloServiceImpl();
		HelloService helloServiceProxy = HelloServiceProxyFactory.getHelloServiceProxy(helloService);
		
		Date date = helloServiceProxy.getTime();
		System.out.println(date);
	}
}
其中,有两点需要讨论:

一是invoke方法

@Override
public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
               //方法调用前的预处理
               Object result = method.invoke(helloService, args);
               //方法调用之后的后续处理
               return result;
     }
invoke方法是InvocationHandler接口中声明的方法。前面已经提到,动态代理类关联的是InvocationHandler类型的对象,也就是说,当在Client2.java中执行

Date date = helloServiceProxy.getTime();
将会委托给InvocationHandler接口中的invoke()方法来完成实际的调用。在invoke方法中,我们可以做一些方法调用前的预处理以及方法调用完成之后的清理工作。


第二点是Client2.java

在Client2.java中,代理类对象是通过反射机制创建的,而不是 new 关键字创建。方法的实际调用也是通过JAVA的反射机制来实现,这样的好处是:当有多种类型的Service时,只需要传递相应的Service实例给getHelloServiceProxy方法,实际调用的形式不变---还是invoke方法负责具体的调用。对于静态代理,对于每一个委托类,都需要手工编写静态代理类的源代码;而对于动态代理,只需要编写一个动态代理工厂类,只需将相应的Service实例传给工厂,它就能自动创建各种类型的动态代理类,简化了编程提高了可扩展性。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值