您的位置:首页 > 编程语言 > Java开发

动态代理及反射在工厂模式上的应用

2017-10-01 14:57 375 查看

前言

之前为读者讲解过代理机制的操作,但是讲解的代理设计属于静态代理,因为每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理。最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。

在Java中要想实现动态代理机制,则需要java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类的支持。

InvocationHandler接口的定义如下:

public interface InvocationHandler{
public Object invoke(Object proxy,Method method,Onject[] args)throws Throwable
}


在此接口中只定义了一个invoke()方法,此方法中有三个参数,其参数的意义如下。

Object proxy :被代理的对象

Method method :要调用的方法

object args[]: 方法调用时所需要的参数。

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类。Proxy类提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?> [] interface,invocationHandler h)throws IllegalArgumentException


通过newProxyInstance()方法可以动态地生成实现类.

关于类加载器

在Proxy类的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有以下3种类加载器。

Bootstrap ClassLoader: 此加载器采用C++编写,一般开发中是看不到的

Extension ClassLoader: 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类

AppClassLoader: 加载classpath指定的类,是最常使用的一种加载器。

如果要完成动态代理,首先定义一个InvocationHandler接口的子类,已完成代理的具体操作。

定义MyInvocationHandler的类

package org.ljz.dynaproxydemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler{
private Object obj;  //真实主题
public Object bind(Object obj){   //绑定真实操作主题
this.obj=obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass.getInstance(),this); //取得代理对象
}
public Object invoke(Object proxy,Method method, Object[] args)throws Throwable{  //动态调用方法
Object temp=method.invoke(this.obj,args); //调用方法,传入真实主题和参数
return temp; //返回方法的返回信息
}
}


在MyInvocationHandler类的bind()方法中接受被代理对象的真实主题实现,之后覆写InvocationHandler接口中的invoke()方法,完成具体的方法调用。

定义接口

package org.ljz.dynaproxydemo;
public interface Subject{  //定义Subject接口
public String say(String name,int age);  //定义抽象方法say


定义真实主题实现类

package org.ljz.dynaproxydemo;
public class ReaSubject implements Subject{ //真实实现类
public String say(String name,int age){  //覆写say()方法
return "姓名:"+name+",年龄:"+age; //返回信息
}
}


以上定义了接口及真实主题类,这样在操作时直接将真实主题类的对象传入到MyInvocationHandler类的bind()方法中即可。

测试动态代理

package org.ljz.demo.dynaproxydemo;
public class DynaProxyDemo{
public static void main(String[] args){
MyInvocationHandler handler=new MyInvocationHandler();//实例化代理操作类
Subject sub=(Subject)handler.bind(new RealSubject());//绑定对象
String info=sub.say("ljz",30);  //通过动态代理调用方法
System.out.println(info);  //信息输出
}
}


程序运行结果:

姓名: ljz, 年龄: 23

从程序运行的结果来看,完成的功能与之前的静态代理操作没有什么不同,所以在一般的开发中很少用到这种动态代理机制,但是在编写一般的底层代码或使用一些框架(如Spring Framework)时这种动态代理模式就比较正常了。

工厂设计模式

将反射用用在工厂设计模式上

工厂设计模式在实际开发中使用的非常多,之前读者已经学习过简单的工厂模式,通过简单的工厂设计模式可以达到类的解耦目的,但是之前的工厂设计模式依然存在问题,那就是在增加一个子类时都需要修改工厂类,这样肯定是很麻烦的。学习完反射机制之后,实际上,此时就可以通过反射机制来改善工厂类,让其在增加子类时可以不用做任何的修改,就可以到达功能的扩充,代码如下所示:

使用反射完成工厂设计

package org.ljz.factorydemo01;
interface Fruit{  //水果接口
public void eat();   //吃水果
}

class Apple implements Fruit{     //定义苹果
public void eat(){      //覆写抽象方法
System.out.println("吃苹果");
}
}

class Orange implements Fruit{  //定义橘子
public void eat(){   //覆写抽象方法
System.out.println("吃橘子");
}
}

class Factory{
public static Fruit getInstance(String className){ //取得接口实例
Fruit fruit=null;  //定义接口对象
try{
fruit=(Fruit)Class.forName(ClassName).newInstance(); //实例化对象
}catch(Exception e){
e.printStackTrace();
}
return fruit;
}
}

public class FactoryDemo01{
public static void main(String[] args){
//通过工厂类取得接口实例,传入完整的包.类名称
Fruit f=Factory.getInstance("org.ljz.demo.factorydemo01.Apple");
if(f!=null){  //判断是否取得接口实例
f.eat();
}
}
}


程序的运行结果:

** 吃苹果

以上的工厂操作类中使用了反射操作取得Fruit实例,这样无论增加多少个子类,工厂类都不用做任何的修改

结合属性文件的工厂模式

以上程序虽然可以通过反射取得接口的实例,但是在操作时需要传入完整的包.类名称,而且用户也无法知道一个接口有多少个可以使用的子类,所以此时可以通过属性文件的形式配置所有的子类信息。

属性文件fruit.properties

apple=org.ljz.factorydemo02.Apple

orange=org.ljz.factorydemo02.Orange

在属性文件中使用apple和orange表示完整的包.类 名称,这样在使用就可以直接通过属性名称即可。

属性操作类

class Init{    //定义初始化操作类
public static Properties getPro(){
Properties pro=new Properties();  //实例化属性类
File f=new File("d:\\fruit.properties");  //找到属性文件
try{
if(f.exists()){    //属性文件中存在
pro.load(new FileInputStream(f));   //读取属性
}else{
pro.setProperty("apple","org.ljz.factorydemo02.Apple");
pro.setProperty("Orange","org.ljz.factorydemo02.Orange");
pro.store(new FileOutputStream(f),"FRUIT CLASS");
}
}catch(Exception e){
e.printStackTrace();
}
return pro;
}
}


此类中的主要功能是取得属性文件中的配置信息,如果属性文件爱你不存在,则创建一个新的,并设置默认值。

测试程序

public class FactoryDemo02{
public static void main(String[] args){
Properties pro=Init.getPro();  //初始化属性类
//通过工厂类取得接口实例,通过属性的key传入完整的包.类名称
Fruit f=Factory.getInstance(pro.getProperty("apple"));
if(f!=null){   //判断是否取得接口实例
f.est();   //调用方法
}
}
}


在通过工厂类取得接口实例时,直接输入属性的key就可以找到完整的包.类名称,已达到对象的实例化功能。

在本程序中可以发现,程序很好地实现了代码与配置的分离。通过配置文件配置要使用的类,之后通过程序读取配置文件,已完成具体的功能,如图所示,当然程序完成的前提是基于接口,所以接口在实际开发中的用处是最大的。

总结

至此,关于Java反射相关的基础部分已经介绍完毕,还有许多反射更深层次的应用,欢迎大家一起学习交流!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息