码迷,mamicode.com
首页 > 编程语言 > 详细

JAVA 反射

时间:2016-06-03 19:38:15      阅读:361      评论:0      收藏:0      [点我收藏+]

标签:

1、Class类的使用
2、方法的反射
3、成员变量的反射
4、构造函数的反射
5、Java类记载机制

Class类
1)在面向对象的世界里,万事万物皆对象
类是对象,类是java.lang.Class类的实例对象

反射的基本操作原理
在整个反射的开发模式之中,有一个最为重要的组成就是java.lang.Class<T>类。但是如果要想取到这个类的实例对象,有三种方式可以完成:
1、Object类之中存在有一个getClass()方法:public final Class<?> getClass();
     -此方法不能被子类所覆写,而且所有类的实例化对象都可以调用;
2、利用“包.类.classs"的形式实例化Class类对象:
     -例如:java.util.Date.class ,在一些开源框架之中会大量使用到,例如:Hibernate、MyBatis;
3、利用Class类之中提供的forName()方法
    -主要用于工厂类上,实际上JDBC的驱动加载使用的就是此模式。
范例:
验证第一种模式
String str="Hello";
Class<?> cls= str.getClass();
System.out.println(cls);
以上的方式是不会使用到的,在所有的开发环境中这种方式可以使用的几率是很低的。
验证第二种模式:
Class<?> cls = java.lang.String.class;
System.out.println(cls);
此操作方式不需要得到指定操作类的实例化对象,而直接通过类的名称就可以完成了。少了一个对象的空间。但是以上的方式虽然严谨,却属于一个明确的结构,即:定义的时候类必须存在。
验证第三种模式:
Class<?> cls = Class.forName("java.lang.String");
Sysout.out.println(cls);
那么使用此类方式操作的最大特点:程序没有很强的严谨性,操作的类可以不存在,只要程序不运行,那么就不会出现任何的语法错误。如果要运行,那么就把指定的类设置上。(更适合开发)

那么取得Class类对象后,最为直观的操作就在于,对象的实例化操作的方式可以发生改变了。在Class类提供有一个实例化对象的操作方法:
public T newInstance() throws IntantiationException,IllegalAccessException
->第一个异常是:没有无参数构造,类名错误
-> 第二个是构造方法私有化
范例:
Class Book{
    public Book(){
    System.out.println("Book类的参构造方法");
}
    @Override
    public String toString(){
        return "这是一本书";    
    }
}
public class TestDemo{
    public static void mian(String[] args) throw Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Book");
        Object obj = (Book) cls.newInstance();//表示实例化对象
        System.out.println(obj); //输出对象调用toString
}
}
使用"newInstance()"方法只能够调用类中的无参构造方法,这个时候的功能相当于使用关键字new进行对象的实例化操作。

面试题:你来解释一下,使用关键字new实例化对象以及使用反射实例化对象有什么区别?
如果只是一个单纯的类进行对象的实例化,那么两者的区别不大;如果非要强调一个区别,那么就是使用反射实例化对象,它的灵活度更高,因为只需要传入“包.类”名称的字符串就可以取得实例化对象,毕竟要比严谨性的new要宽松许多。
如果说现在是一个子类需要为父接口进行对象实例化,那么如果使用了关键字new会造成接口对象的耦合性增加的问题,因为一个接口在使用中就与固定的一个子类进行绑定了,而最早的解耦合是利用工厂设计模式,但是为了让一个工厂类可以使用所有接口子类的扩展要求,则可以利用反射完成。
范例:传统方式取得接口对象
package cn.mldn.demo;

//这种接口叫做消费型接口
interface Message{
    public void print(String str);
}

class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Message msg =new News();//直接实例化对象,会造成接口与子类的耦合
        msg.print("今天不下雨");

    }
}

现在所造成的问题是,客户端代码需要负责子类操作。

范例:利用工厂类解决客户端的接口对象的耦合问题
package cn.mldn.demo;

//这种接口叫做消费型接口
interface Message{
    public void print(String str);
}

class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}

class Factory{
    public static Message getInstance(String className){
        if("news".equalsIgnoreCase(className)){
            return new News();
        }else if("email".equalsIgnoreCase(className)){
            return new Email();
        }else{
            return null;
        }
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
//        Message msg =new News();//直接实例化对象,会造成接口与子类的耦合
        Message msg = Factory.getInstance("news");
        msg.print("今天不下雨");

    }
}

虽然这个时候实现了工厂设计模式,但是本代码却具备一个非常大的缺点(根据子类扩充来决定的):每当接口扩充新的子类的时候,都需要修改工厂类,这对于一个代码根本就属于一个灾难性的折腾。最好的编码:一个类写完之后,可以适用于所有情况的变化,所以这种严谨性的代码,那么就不适合于实际的编写。
范例:利用反射来解决工厂类的设计问题
package cn.mldn.demo;

//这种接口叫做消费型接口
interface Message{
    public void print(String str);
}

class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}

class Factory{
    public static Message getInstance(String className){

        try {
            return (Message)Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
//        Message msg =new News();//直接实例化对象,会造成接口与子类的耦合
        Message msg = Factory.getInstance("cn.mldn.demo.News");
        msg.print("今天不下雨");

    }
}
这个时候虽然解决了工厂类的固化问题,但是有一个新的问题产生了,我的客户端代码还是固定的,客户端必须要明确的写出要操作的类名称,这样一来代码就会造成新的耦合问题。
最好的设计方法永远不是点对点直达(最快),通过key找到value。如果说一个普通用户,那么他一定不能去修改程序的逻辑代码,但是它可以修改文件的内容,所以此时如果有想要解决新的耦合问题,可以采用属性文件的方式完成。
在java.util包中有一个Properties类,它是Hashtable的子类,这个类可以定义“key=value”的文件,在这个类中提供了以下的两个操作方法:
1、通过输入流读取属性内容:public void load(InputStream inStream)throw IOException
2、通过输出流输出属性内容:public void store(OutputStream out, String comments)throws IOException
范例:观察这个类的使用
package cn.mldn.demo;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
public class TestProperties {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();
        pro.setProperty("messsage.classname", "cn.mldn.demo.News");
        pro.setProperty("city", "北京");
        pro.store(new FileOutputStream(new File("D:"+File.separator+"info.properties")), "This Message File");
    }
}
在我们以后的任何开发之中,只要是属性文件其后缀必须为“*.properties"(国际化程序中见过此类文件)
通过Properties进行的内容输出之后发现其结构都按照"key=value"的形式保存,所有的中文自动转换编码,如果在MyEclipse之中自己定义*.properties文件,会帮助用户自己转换编码)
范例:读取文件
public class TestProperties {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();
        pro.load(new FileInputStream(new File("D:"+File.separator+"info.properties")));
        System.out.println(pro.getProperty("messsage.classname"));
    }
}
现在通过程序发现,Properties类可以直接具备IO流的程序操作能力。

范例:将属性的操作与工厂设计模式的客户端结合
package cn.mldn.demo;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

//这种接口叫做消费型接口
interface Message{
    public void print(String str);
}

class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}

class Factory{
    public static Message getInstance(String className){

        try {
            return (Message)Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
//        Message msg =new News();//直接实例化对象,会造成接口与子类的耦合
        Properties pro = new Properties();
        pro.load(new FileInputStream(new File("D:"+File.separator+"info.properties")));
        Message msg = Factory.getInstance(pro.getProperty("messsage.classname"));
        msg.print("今天不下雨");
    }
}
现在在程序里面没有明确的去定义一个使用的子类,只是读取了属性文件,这个时候的工厂设计模式,我们就认为它已经得到了一个改善。
但是给出的*.properties文件路径不应该设置在磁盘上,应该将其定义在CLASSPATH之中。如果说现在是一个web项目,那么CLASSPATH在WEB-INF/classes目录下。
但是发现直接利用Properties进行访问的时候根本就找不到指定的文件,因为这个类如果在进行读取的时候,使用load()方法需要绝对路径。
package cn.mldn.demo;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

public class TestProperties {
    public static void main(String[] args) throws Exception {
        String dir = System.getProperties().getProperty("user.dir");
        String path = dir + File.separator+"WebContent"+File.separator+"WEB-INF"+File.separator +"classes"+File.separator+"info.properties";
        System.out.println(path);
        Properties pro = new Properties();
        pro.load(new FileInputStream(new File(path)));
        System.out.println(pro.getProperty("messsage.classname"));
    }
}    我写的有问题
这种代码的开发体验并不好,而且可以说很烂,这个时候不能够直接使用Properties。还有一个类可以读取*.properties文件,那么就是在国际化程序中使用过的:java.util.ResourceBundle类,这个类可以直接读取classpath下文件。
范例:利用ResourceBundle读取
package cn.mldn.demo;
import java.util.ResourceBundle;
public class TestResourceBundle {
    public static void main(String[] args) {
        ResourceBundle rb = ResourceBundle.getBundle("info");
        System.out.println(rb.getString("messsage.classname"));
    }
}
这个时候,感觉到如果要读取CLASSPATH中的资源文件,还是使用ResourceBundle要比直接使用Properties类简单。如果你需要生成*.properties文件的时候,还是要使用Properties类比较好。
范例:改进代码
package cn.mldn.demo;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
import java.util.ResourceBundle;

//这种接口叫做消费型接口
interface Message{
    public void print(String str);
}

class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}

class Factory{
    public static Message getInstance(String className){

        try {
            return (Message)Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
//        Message msg =new News();//直接实例化对象,会造成接口与子类的耦合
        ResourceBundle rb = ResourceBundle.getBundle("info");
        Message msg = Factory.getInstance(rb.getString("messsage.classname"));
        msg.print("今天不下雨");
    }
}
所以在日后的开发之中,资源文件的读取都会使用ResourceBundle类,而且以上的代码可以总结一个特点,只要是定义的时候存在有"包.类”字符串,就可以使用反射处理了。


Annotation操作原理
对于之前的程序实际上的模式是“程序类+配置文件”,只不过这个配置文件稍微小一点而已,但是通过Servlet的开发过程我们可以感受到此时的代码实际上并不是这么的完美,不完美度就在于用户需要手工维护配置文件(*.properties).
所以现在必须想一种方式(前提:实际上把配置文件给用户只是理想状态,用户不会去修改),这种方式可以有效的将配置写回程序之中,那么又可以和进行有效的分离,于是就可以参考Servlet开发经验,使用Annotation来实现配置。
实际上现在已经接触了几种Annotation,有的是Java定义的,有的是Servlet定义,下面首先来分析Annotation是如何操作的。当然,所有的Annotation可以定义在类或者是方法上,那么首先要做的是取得这些定义的Annotation。在class类中有取得去不Annotation的操作方法:public Annotation[] getAnnotations(),这个方法取回的是Annotation 接口对象数组。
范例:取得全部的Annotation
package cn.mldn.demo;
import java.io.Serializable;
import java.lang.annotation.Annotation;
@SuppressWarnings("serial")
@Deprecated
class Student implements Serializable {
}
public class TestAnnotation {
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        Annotation an [] = cls.getAnnotations();//取得全部的Annotation
        for(int x=0;x<an.length;x++){
            System.out.println(an[x]);
        }
    }
}
输出结果:@java.lang.Deprecated()
最终发现只取得一个Annotation,因为在Java设计的过程之中,针对于Annotation的作用范围是有定义的,只有"@Deprecated"是在程序运行的时候起作用的Annotation。如果要想知道Annotation的全部范围,那么必须查询一个枚举类:java.lang.annotation.RetentionPolicy,在这个类中定义了三种Annotation的范围:
1、CLASS:保存在类之中
2、RUNTIME:程序运行时起作用
3、SOURCER:在源代码中起作用
如果我们要想编写的程序可以在运行的时候,使用RUNTIME类型。下面来定义一个属于自己的Annotation。使用“@interface"定义。
范例:定义属于自己的Annotation
package cn.mldn.demo;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
@interface MyFactory{//这是一个属于自己的Annotation
    public String name() default "mldn";
    public String val();
}
@MyFactory(val="hello")
@SuppressWarnings("serial")
@Deprecated
class Student implements Serializable {
}
public class TestAnnotation {
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        Annotation an [] = cls.getAnnotations();//取得全部的Annotation
        for(int x=0;x<an.length;x++){
            System.out.println(an[x]);
        }
    }
}
不过以上的代码只是取得了Annotation,但是不要忘记了,在Annotation使用的时候里面设置了内容,现在有两个参数内容,一个是name,另一个是val。这个时候只能够取得一个指定的Annotation,而后调用里面的方法,在Class类里面定义了取得Annotation的方法:public <A extend Annotation> A getAnnotation(Class<A>  annotation)
范例:取得指定的Annotation
package cn.mldn.demo;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(value=RetentionPolicy.RUNTIME)
@interface MyFactory{//这是一个属于自己的Annotation
    public String name() default "mldn";
    public String val();
}

@MyFactory(val="hello")
@SuppressWarnings("serial")
@Deprecated
class Student implements Serializable {
}
public class TestAnnotation {
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        //取得一个指定的Annotation类型
        MyFactory an = cls.getAnnotation(MyFactory.class);
        System.out.println(an.name());
        System.out.println(an.val());
    }
}
现在发现可以通过程序取得Annotation的设置内容,那么下面将利用Annotation来进行工厂设计模式的修改,在使用的客户端上定义Annotation.
范例:利用Annotation改善工厂设计
package cn.mldn.demo;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(value=RetentionPolicy.RUNTIME)
@interface MyFactoryClass{
    public String className(); //需要设置Class的包.类名称
}
interface Message{
    public void print(String str);
}
class News implements Message{
    public void print(String str){
        System.out.println("新闻消息,内容:"+str);
    }
}

class Email implements Message{
    public void print(String str){
        System.out.println("邮件消息,内容:"+str);
    }
}
@MyFactoryClass(className="cn.mldn.demo.Email")
public class TestDemo {
    public static Message get() throws Exception{
        Class<?> cls = TestDemo.class;
        //取得在此处配置的annotation
        MyFactoryClass an = cls.getAnnotation(MyFactoryClass.class);
        String className = an.className();
        Class<?> ins = Class.forName(className);
        Object obj = ins.newInstance();
        Message msg = (Message) obj;
        return msg;
    }

    public static void main(String[] args)throws Exception{
        Message msg = get();
        msg.print("今天要下雨");

    }
}

总结:Annotation好用,而且利用Annotation编写的代码非常简洁,但是它的开发太麻烦了,可是在现阶段的开发中,会出现"程序+配置文件"、”程序+Annotation“共存的状态。

3.3 利用反射调用类中的其他结构(重点)
Class 是反射之中最为重要的类,也是所有反射的操作源头。而对于每一个结构(构造、方法、成员),每一个都可以用Class找到,所以下面将利用反射操作类的其它结构。
3.3.1 操作类中的构造方法
回顾开发原则:每一个简单Java类都要求提供有无参构造方法。
利用Class类对象的newInstance()方法可以进行对象的实例化操作,但是这样的操作必须有一个前提,类之中必须存在有无参构造方法。
范例:错误的代码
package cn.mldn.demo;

class Book{
    private String title;
    private Double price;
    public Book(String title){
        this.title = title;
    }
    public Book(String title,Double price){
        this.title = title;
        this.price = price;
    }
    @Override
    public String toString() {
        return "书名" + this.title + ", 价格:"+this.price;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Book");
        cls.newInstance();

    }
}

这个时候类之中由于没有无参构造方法,所以使用newInstance()操作一定会出现异常信息,所以我们需要明确指定参数的构造方法,并且在实例化对象的时候,传入所需要的参数内容。
在Class类里面提供有以下的操作方法:
1、取得全部构造:public Constructor<?>[] getConstructors() throws SecurityException
2、取得指定参数类型的构造:public Constructor<T> getConstructor(Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException
在反射的过程中,是一个只认类型不认具体对象的工具类,包括早进行方法重载的时候,认的也只是方法名称和参数类型。以上两个方法返回的是java.lang.reflect.Constructor对象。这个类有如下的方法:
1、取得构造方法的名称:public String getName()
2、取得构造方法的修饰符:public int getmodifiers()
3、取得参数类型:public Clsss<?>[] getParameterTypes()
4、取得构造方法上抛出的全部异常:public Class<?>[] getExceptionTypes(); 
但是所有的修饰符都是通过数字编号取得的,也就是说如果要想取得相应的内容必须将相应的数字变回可以读懂的关键字。所以可以使用“java.lang.reflect.Modifier"类完成。于是在这个类里面提供了一个简单的方法,可以实现数字到关键字的还原。
public 1
static   8
fianal  7
abstract
范例:取得全部构造方法的信息
package cn.mldn.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

class Book{
    private String title;
    private Double price;
    public Book(String title) throws Exception,NumberFormatException{
        this.title = title;
    }
    public Book(String title,Double price){
        this.title = title;
        this.price = price;
    }
    @Override
    public String toString() {
        return "书名" + this.title + ", 价格:"+this.price;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Book");
        Constructor<?> cons[] = cls.getConstructors();//取得全部的构造函数
        for(int x = 0;x<cons.length;x++){
            System.out.print(Modifier.toString(cons[x].getModifiers()));
            System.out.print(" "+cons[x].getName() +"(");
            Class<?>params []  = cons[x].getParameterTypes();
            for(int y = 0;y<params.length;y++){
                System.out.print(params[y].getSimpleName()+" arg"+y);
                if(y<params.length-1){
                    System.out.print(", ");
                }
            }
            System.out.print(")");
            Class<?> exp[] = cons[x].getExceptionTypes();//取得全部异常
            if(exp.length>0){//表示现在有异常抛出
                System.out.println(" throws");
                for(int y=0;y<exp.length;y++){
                    System.out.println(exp[x].getSimpleName());
                    if(y<exp.length-1){
                        System.out.println(",");
                    }
                }
            }
            System.out.println();
        }
    }
}
在实际的开发环境中,所有的开发工具实际上都具有这样的操作过程,但是如果采用这样的方式实现了随笔提示,这个代码的执行速度那是慢的。
但是在Constructor类里面提供了一个专门负责实例化的方法,这个方法可以传递指定参数的具体内容
1、实例化对象:public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,  IlleagalArgumentException, InvovationTargetExcetpion
范例:调用指定构造实例化对象  //要么无参,要么全参
package cn.mldn.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

class Book{
    private String title;
    private Double price;
    public Book(String title,Double price){
        this.title = title;
        this.price = price;
    }
    @Override
    public String toString() {
        return "书名" + this.title + ", 价格:"+this.price;
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Book");
        Constructor<?> cons = cls.getConstructor(String.class,Double.class);
        Book book = (Book) cons.newInstance("java开发",79.8);
        System.out.println(book);
    }
}
通过以上的一段代码,必须明确的是,类之中只有提供无参构造方法的时候才是最容易的一种操作,以上的代码,如果是有参的构造,那么在编写的代码的麻烦程度是相当高的,而解决的办法也需要用户处理麻烦的逻辑。
最好的做法就是本着简单Java类的标准开发原则进行的。

3.3.2 操作类中的方法
清楚了构造方法的调用之后,下面继续看普通方法调用,在Class类里面提供有两组普通方法信息的取得:
1、第一组方法取得本类定义的方法:
                            public Method[] getDeclaredMethods() throws SecurityException
                            public Method getDeclaredMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
2、第二组方法取得类的所有方法:
                            public Method[] getMethods() throws SecurityException
                            public Method getMethod(String name,Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException
范例:编写程序验证区别
public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.MessageImpl");
        Method met[] = cls.getMethods();
        for(int x = 0;x < met.length; x++){
            System.out.println(met[x]);
        }
    }
}
使用getMethods()方法可以取得一个类之中所有的方法,包括自己定义的以及继承的方法。
范例:验证另外一个方法
package cn.mldn.demo;
import java.lang.reflect.Method;
interface Message{
    public void print();
}
abstract class Info{
    public abstract void get();
}
class MessageImpl extends Info implements Message{
    @Override
    public void print() {//覆写接口的方法
    }
    @Override
    public void get() {//覆写抽象类的方法
    }
    public void set(){//定义自己的方法
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.MessageImpl");
        Method met[] = cls.getDeclaredMethods();
        for(int x = 0;x < met.length; x++){
            System.out.println(met[x]);
        }
    }
}
现在取得是本类之中定义的所有操作方法,与继承的无关。
以上的代码是利用Method类之中的toString()方法取得了每一个方法的信息,那么现在要求可以自己定义方法输出。
1、取得方法返回值类型:public Class<?> getReturnType();
范例:输出全部方法(包括异常)
package cn.mldn.demo;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

interface Message{
    public void print();
}
abstract class Info{
    public abstract void get();
}
class MessageImpl extends Info implements Message{

    @Override
    public void print() {//覆写接口的方法
    }
    @Override
    public void get() {//覆写抽象类的方法
    }
    public void set(){//定义自己的方法
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.MessageImpl");
        Method met[] = cls.getMethods();
        for(int x = 0;x < met.length; x++){
            System.out.print(Modifier.toString(met[x].getModifiers()) + " ");
            System.out.print(met[x].getReturnType().getSimpleName() + " ");
            System.out.print(met[x].getName() + "(");
            Class<?> params[] = met[x].getParameterTypes();
            for(int y = 0; y < params.length; y++){
                System.out.print(params[y].getSimpleName() + " arg " + y);
                if(y < params.length -1){
                    System.out.print(", ");
                }
            }
            System.out.println(")");
            Class<?> exp[] = met[x].getExceptionTypes();
            if(exp.length>0){
                System.out.print(" throws ");
                for(int y = 0; y < exp.length; y++){
                    System.out.print(exp[y].getSimpleName());
                    if(y<exp.length - 1){
                        System.out.print(", ");
                    }
                }
                System.out.println();
            }
            }
    }
}
技术分享
在之前取得了Constructor类的对象是为了明确调用参数类之中的指定参数的构造方法,而取得了Method类对象不是让你进行以上输出操作的。在Method类里面提供了以下一个重要的方法:
1、调用本类方法:public Object invoke(Object obj, Object... args) throws 略去
在使用以上的invoke方法()操作的时候,一定要保证已经存在了本类的实例化对象,而方便的是这种实例化对象可以直接利用Object代替。可以利用Class类反射实例化对象,而后通过Object类对象操作,就没有必要向下转型了。
范例:利用反射调用方法
package cn.mldn.demo;
import java.lang.reflect.Method;
class Message{
    public void print(String str){//没有返回值类型,但是有参数
        System.out.println(str);
    }
}
public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Message");
        Object obj = cls.newInstance();
        //已经明确的找到了Method(public void print(String str))
        Method met = cls.getMethod("print", String.class);
        met.invoke(obj, "hello world");

    }
}
范例:调用有返回值、有参数的方法
package cn.mldn.demo;

import java.lang.reflect.Method;

class Message{
    public String echo(String str){
        return "ECHO:" + str;//返回值
    }
}

public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Message");
        Object obj = cls.newInstance();
        //已经明确的找到了Method(public void print(String str))
        Method met = cls.getMethod("echo", String.class);
        Object val = met.invoke(obj, "hello world");
        System.out.println(val);
    }
}
以上只是演示了Method类的执行操作,但是从实际的开发来讲,反射结合简单Java类的情况是最多的,一直强调的原则:简单Java类中的属性一定要进行封装,封装后的属性要按照指定的要求编写setter和getter.
范例:利用反射实行setter、getter调用
import java.lang.reflect.Method;
class Dept{
    private String dname;

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }
}
如果需要通过反射设置dname属性内容,一般都一定会提供以下的内容:
1、要操作的类名称,提供的目的是取得Class类对象
2、要操作的类中的属性名称
如果要想找到setter、getter方法,那么必须将属性的单词字母首字母变为大写,这个类JDK一直没有提供,所以应该编写一个自己的工具类,来实现这样的操作。
范例:定义自己的工具类
package cn.mldn.util;
/**
 * 字符串操作工具类,用于实现字符串处理数据
 * @author Administrator
 *
 */
public class StringUtils {
    /**
     * 实现字符串首字母大写功能,其他字母保持大小写不变
     * @param str  要转换的字符串数据
     * @return 将字符串的首字母大写后返回
     */
    public static String initcap(String str){
        if(str == null){
            return null;
        }
        return str.substring(0,1).toUpperCase() + str.substring(1);
    }
}
这个类以后就直接使用了,那么只要调用方法,就可以实现字符串首字母大写。
范例:实现方法调用
package cn.mldn.demo;
import java.lang.reflect.Method;
import cn.mldn.util.StringUtils;

class Dept{
    private String dname;

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }
}
public class TestDemo {
    public static void main(String[] args)throws Exception{
        //默认一定会提供以下内容
        String className = "cn.mldn.demo.Dept"; //类名称
        String property = "dname";//类属性
        String value = "财务部";
        //后面的代码一定是由反射机制进行处理
        Class<?> cls = Class.forName(className); //找到指定类型
        Method setMet = cls.getMethod("set"+StringUtils.initcap(property), String.class);
        Method getMet = cls.getMethod("get"+StringUtils.initcap(property));
        Object obj = cls.newInstance();
        setMet.invoke(obj, value);//调用setDname()方法
        System.out.println(getMet.invoke(obj)); //调用getDname()方法
    }
}
除了小小的遗憾,需要输入固定类型,发现整个代码里面的确通过反射进行的设置,而且反射操作的一定是有前提条件:类名称、属性名称、内容。

3.3.3 调用成员
学习到现在可以发现一个类之中可以定义的成员:全局常量、全局变量、普通常量、普通变量,这所有的概念都称为Field(成员),在Class类里面提供了两组取得成员的方法:
1、第一组:得到全部成员,包括继承而来的成员,但是无法取得私有;
    取得全部成员:putblic Field[] getFields() throws SecurityException
    取得单个成员:public Field getField(String name)throws NoSuchFieldException 省略
2、第二组:取得本类的属性。
    取得全部成员:public Field[] getDeclaredFields() throw SecurityException
    取得单个成员:public Field getDeclaredFiled(String name) throws 省略
package cn.mldn.demo;
import java.lang.reflect.Field;
interface Message{
    public static final String MSG ="HELLO WORLD!";
}
abstract class Info{
    public static String  city = "北京";
    public String id = "101216545315353";
}
class Dept extends Info implements Message{
    public String dname;

}
public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Dept");
        {
            Field f[] = cls.getFields();
            for(int x = 0;x<f.length; x++){
                System.out.println(f[x]);
            }
        }
        System.out.println("*************************************************");
        {
            Field f[] = cls.getDeclaredFields();
            for(int x = 0;x<f.length; x++){
                System.out.println(f[x]);
            }
        }
    }
}
在Field类里面有以下几个方法很重要:
1、得到属性类型:public Class<?>getType();
2、得到属性内容:public Object getXxx(Object object) throws 省略
3、设置属性内容:public void set(Object obj, Object value) throws省略
在java.lang.reflect包之中最为核心的类一共有三个:Constructor、Method、Field,而这三个类存在有共同的继承方式的,它们都是java.lang.reflect.AccessibleObject子类,而在AccessibleObject类定义有如下方法:
1、取得全部的Annotation:public Annotation[] getAnnotation();
2、设置是否可以访问:public void setAccessible(boolean flag) throws 省略

范例:反射调用属性
package cn.mldn.demo;
import java.lang.reflect.Field;
class Dept{
    private String dname;

}
public class TestDemo {
    public static void main(String[] args)throws Exception{
        Class<?> cls = Class.forName("cn.mldn.demo.Dept");
        Field dnameField = cls.getDeclaredField("dname");
        Object obj = cls.newInstance();
        dnameField.setAccessible(true);   //取消封装
        dnameField.set(obj, "财务部");
        System.out.println(dnameField.get(obj));
    }
}
虽然可以通过反射进行属性的直接调用,但是千万要记住,不要直接通过反射进行属性的内容操作,所有的操作都必须通过setter、getter方法。
范例:利用Fileld来解决Method定义方法的问题
package cn.mldn.demo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import cn.mldn.util.StringUtils;

class Dept{
    private Integer deptno;

    public Integer getDeptno() {
        return deptno;
    }

    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }
}
public class TestDemo {
    public static void main(String[] args)throws Exception{
        //默认一定会提供以下内容
        String className = "cn.mldn.demo.Dept"; //类名称
        String property = "deptno";//类属性
        Integer value = 20;
        //后面的代码一定是由反射机制进行处理
        Class<?> cls = Class.forName(className); //找到指定类型
        Field dnameField = cls.getDeclaredField(property); //取得成员对象
        Method setMet = cls.getMethod("set"+StringUtils.initcap(property), dnameField.getType());
        Method getMet = cls.getMethod("get"+StringUtils.initcap(property));
        Object obj = cls.newInstance();
        setMet.invoke(obj, value);//调用setDname()方法
        System.out.println(getMet.invoke(obj)); //调用getDname()方法
    }
}
如果要想编写出程序代码可以被所有的类都使用到,那么必须使用Contructor、Field、Method三个类共同完成。

3.5、利用反射操作简单Java类(核心
通过JSP的标签可以观察到可用反射可以自动设置请求参数的数据到VO类,但是JSP之中的标签只适合单数据类型。而且在JSP标签只能留给页面去使用,那么为了将这一功能继续传承下来,所以利用反射实现数据的设置,考虑到学习的层次,本次的操作是属于半模拟状态。
需要实现的功能:
1、可以实现任意层次结构的类对象属性设置
2、可以实现String、Integer(int)、Double(double)、Date、String[]、Integer[](int[]))、Double[](double[])几种数据类型的自动转换
这些功能最后可以形成一个BeanOperate的操作类,供开发使用。
现在假设有如下的几个关系层次的类:一个雇员属于一个部门,一个部门属于一个公司
范例:定义Company类
package cn.mldn.vo;
import java.util.Date;
public class Company {
    private String name;
    private String address;
    private Date createDate;
}
定义:部门类
package cn.mldn.vo;

public class Dept {
    private Integer deptno;
    private String dname;
    private String loc;
    private Company company= new Company();
    public Integer getDeptno() {
        return deptno;
    }
    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }
    public String getDname() {
        return dname;
    }
    public void setDname(String dname) {
        this.dname = dname;
    }
    public String getLoc() {
        return loc;
    }
    public void setLoc(String loc) {
        this.loc = loc;
    }
    public Company getCompany() {
        return company;
    }
    public void setCompany(Company company) {
        this.company = company;
    }
}
范例:定义雇员:
package cn.mldn.vo;
import java.util.Date;
public class Emp {
    private Integer empno;
    private Double sal;
    private Date hiredate;
    private String ename;
    private String msg[];
    private Integer iid[];
    private Dept dept = new Dept(); //必须new
}

如果要想实现层次的属性操作,那么只要是其他VO的类型,在VO类里面都需要进行对象的实例化。
那么现在会给出用户两个字符串:
1、第一个字符串:“实例化对象.属性.属性.属性.....”
2、第二个字符串:属性的内容,可以是任意类型,常见的就是String、Integer、Double
分析现在的问题:
1、如果说现在的字符串的组成为:”对象.属性“,那么就表示最后一个一定是属性;
如果说给出的是"emp.ename”,那么实际上就是:emp.setEname(内容):
现在必须要找到emp对象:
2、如果字符串组成为:“对象.对象.对象.属性",实际上最后一个也是属性。
如果说现在给出的是”emp.dept.empany.name",那么实际上就是:emp.getDept().getCompany().setName(内容)
现在必须要找到company对象

现在的程序已经实现了对象与属性分离以及设置基本属性内容的操作,但是在Emp类里面存在数组,在开发之中数组的情况一定会有,例如:复选框。
范例:工具类:
package cn.mldn.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
/**
 * 本类的功能是根据属性的名称与属性设置传递的内容
 * 
 * @author Administrator
 *
 */
public class BeanOperate {
    private Object obj; // 操作对象
    private String property; // 操作属性
    private Object value;// 数据内容
    private Field field; // 要操作的属性
    private String fieldName; //保存成员的名称
    private Object currentObject; // 要操作的属性的对象

    /**
     * 本类里面是加上只需要一些基本信息
     * 
     * @param obj
     * @param property
     * @param value
     */
    public BeanOperate(Object obj, String property, Object value) {
        this.obj = obj;
        this.property = property;
        this.value = value;
        this.handleString(); //直接进行数据处理
        this.serFieldValue(); //设置成员的内容
    }

    /**
     * 此操作的核心就是要处理字符串,从里面区分出要操作的属性以及对象
     */
    private void handleString() {
        String result[] = this.property.split("\\.");// 按照.拆分
        this.currentObject = this.obj;// 设置当前的操作对象
        try{
            if (result.length == 2) { //只有一个点
                this.field = this.currentObject.getClass().getDeclaredField(result[1]);
                this.fieldName = result[1];
            } else {
                for (int x = 1; x < result.length; x++) {// 利用循环操作,从1开始

                    // getter方法是没有参数的,找到getter方法
                    Method met = this.currentObject.getClass().getMethod("get" + StringUtils.initcap(result[x]));
                    this.field = this.currentObject.getClass().getDeclaredField(result[x]);
                    this.fieldName = result[x];//保存成员名称
                    if(x<result.length-1){ //后面还有对象
                        this.currentObject = met.invoke(this.currentObject);
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 设置指定属性的内容,所有的操作一定要调用setter方法
     */
    private void serFieldValue(){
        try{
            Method setMet = this.currentObject.getClass().getDeclaredMethod(
                    "set" + StringUtils.initcap(this.fieldName) , 
                    this.field.getType());//找到设置的setter方法
            String type = this.field.getType().getSimpleName();
            String valueType = this.value.getClass().getSimpleName();
            if("string".equalsIgnoreCase(valueType)){
                String val = null;//保存处理后的数据
                val = this.value.toString();
                if("int".equalsIgnoreCase(type)||"Integer".equalsIgnoreCase(type)){//为数字型
                    if(val.matches("\\d+")){
                        setMet.invoke(this.currentObject, Integer.parseInt(val));
                    }
                }else if("doublie".equalsIgnoreCase(type)||"Double".equalsIgnoreCase(type)){
                    if(val.matches("\\d+(\\.\\d+)")){
                        setMet.invoke(this.currentObject, Double.parseDouble(val));
                    }
                }else if("string".equalsIgnoreCase(type)){
                    setMet.invoke(this.currentObject, val);
                }else if("date".equalsIgnoreCase(type)){
                    if(val.matches("\\d{4}-\\d{2}-\\d{2}")){
                        setMet.invoke(this.currentObject,new SimpleDateFormat("yyyy-MM-dd").parse(val));
                    }
                }
            }else{//传入的内容为String字符串
                String val [] = (String[]) this.value; //将Object变为数组
                this.field.setAccessible(true);
                if("string[]".equalsIgnoreCase(type)){//如果是数组
                    //setMet.invoke(this.currentObject, val); //字符串数组
                    this.field.set(this.currentObject, val);
                }else if("int[]".equalsIgnoreCase(type)||"Integer[]".equalsIgnoreCase(type)){
                    Integer data[] = new Integer[val.length];
                    for(int x = 0;x<val.length;x++){
                        data[x] = Integer.parseInt(val[x]);
                    }
                    //setMet.invoke(this.currentObject, data);
                    this.field.set(this.currentObject, data);
                }else if("double[]".equalsIgnoreCase(type)){
                    Double data[] = new Double[val.length];
                    for(int x = 0;x<val.length;x++){
                        data[x] = Double.parseDouble(val[x]);
                    }
                    //setMet.invoke(this.currentObject, data);
                    this.field.set(this.currentObject, data);
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public Object getCurrentObject() {
        return currentObject;
    }

    public Field getField() {
        return field;
    }
}

范例:测试

首先在实际的开发之中,这个类的自动设置属性内容的功能,在开发之中随处可见,也就是说有了这个类的帮助,再结合getParameterNames()方法,整个程序的代码将变得非常简单,但是这只是一个开始。

3.6 开发DispatcherServlet工具类(重点
清楚了反射的所有概念之后,那么下面需要利用反射来解决之前MVC开发之中出现的种种问题,回顾一下存在哪些问题?
1、如果一个Servlet要处理多个业务操作,那么需要传递一个status参数,觉得这样参数传的有点多;
2、每一个Servlet处理分页的过程几乎是一样的,代码相当重复;
3、所有接收的请求参数都要将其设置到VO类对象里面,这样如果提交的参数过多,那么用户需要重复设置,并且需要进行转型操作;
4、那该死的上传,彻底打乱了我们对传统接收的参数方式变更;
5、每一次操作的时候都需要根据参数来判断调用的功能,如果没有判断,功能就无法使用。
所有的问题都集中在Servlet程序上,因为这是整个程序的控制核心,但是一直以来我们都只是使用它,并没有对它进行一个好的设计。如果要为它进行类的结构设计,那么肯定使用继承关系是最合适的,首先所有的Servlet都要继承HttpServlet(HttpServlet是一个抽象类,另外一点这个类之中的service方法负责请求的分类处理,而具体的处理交给子类完成),那么可以继续沿用这样的设计思路,继续增加新的工具类,这个工具类存在于HttpServlet与具体的Servlet之间。
技术分享
3.6.1 解决status参数的问题
由于每一个Servlet需要负责处理多个业务操作,所以这样就为程序的开发增加了负担,因为每一次都需要传递一个状态码,而后通过if else 判断(switch也支持String判断),但是这样的判断模式绝对是不好的,因为不具备灵活的扩展性,而这样的代码必须解决。
范例:定义DispatcherServlet基本结构
package cn.mldn.util.action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 定义一个负责处理所有用户请求的公共Servlet
 * 这个Servlet负责操作分发、负责数据验证、分页实现
 * 这个类必须有具体的子类操作
 * @author Administrator
 *
 */
@SuppressWarnings("serial")
public abstract class DispatcherServlet extends HttpServlet {
    /**
     * 此方法留给一些程序初始化使用,因为在整个程序里面存在有资源文件,
     * 将通过此部分读取资源文件
     */
    @Override
    public void init() throws ServletException {
        // TODO Auto-generated method stub
        super.init();
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.doGet(request, response);
    }
}
范例:定义子类 --EmpServlet
package cn.mldn.servlet.back;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.mldn.util.action.DispatcherServlet;
@SuppressWarnings("serial")
@WebServlet(urlPatterns="/pages/back/admin/emp/EmpServlet")
public class EmpServlet extends DispatcherServlet {
    public String list(HttpServletRequest request, HttpServletResponse response){
        return null;
    }
}
在EmpServlet之中由于只是处理核心业务,那么它的核心功能就是进行业务方法的定义,没有任何的doGet和doPost处理,这些处理都交给DispatcherServlet处理。
那么下面需要解决传递参数的问题,怎么解决呢?
在最早的时候,如果有了一个参数,那么servlet只要接受status参数,利用判断语句就可以知道我们要进行哪些操作,但是后来我们发现,如果真传递参数,整个代码修改的实在太多了,而且也不好,那么换一种形式;
1、增加雇员操作:EmpServlet/insert
2、雇员列表操作:EmpServlet/list
使用此类方式不再费心的去区分到底表单是否进行了封装,因为不是参数的处理,而是路径的处理。
也就是说现在把所有的操作类型都当作一个地址传递。如果要想实现这一操作,那么需要进行如下的修改:

1、在我们的EmpServlet上就证明它后面可以是任意的符号,所以映射路径修改;
@WebServlet(urlPatterns="/pages/back/admin/emp/EmpServlet/*")
2、针对于"*"的内容在HttpServletRequest接口里面定义了一个方法:public String getRequestURI()
范例:修改DispatcherServlet类
@Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //得到的路径:/TestReflect/pages/back/admin/emp/EmpServlet/list
        String uri = request.getRequestURI(); //取得用户的相对路径
        //处理后的路径:list
        String status = uri.substring(uri.lastIndexOf("/") + 1);
        System.out.println("************************uri="+ uri);
        System.out.println(status);
    }
这个操作还不受到上传文件的影响,那么既然取得了操作类型,后面的任务就是要利用反射来调用传入的方法。如果要通过反射调用方法,那么必须有一个前提:有指定类的对象,并且要知道调用的方法的名称,以及参数(所有参数基本上只会保持两个);
范例:反射调用
package cn.mldn.util.action;
import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 定义一个负责处理所有用户请求的公共Servlet
 * 这个Servlet负责操作分发、负责数据验证、分页实现
 * 这个类必须有具体的子类操作
 * @author Administrator
 *
 */
@SuppressWarnings("serial")
public abstract class DispatcherServlet extends HttpServlet {
    /**
     * 此方法留给一些程序初始化使用,因为在整个程序里面存在有资源文件,
     * 将通过此部分读取资源文件
     */
    @Override
    public void init() throws ServletException {
        // TODO Auto-generated method stub
        super.init();
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = "/pages/errors.jsp";//默认的跳转路径
        //得到的路径:/TestReflect/pages/back/admin/emp/EmpServlet/list
        String uri = request.getRequestURI(); //取得用户的相对路径
        //处理后的路径:list
        String methodName = uri.substring(uri.lastIndexOf("/") + 1);
        //根据指定的方法名称反射调用指定的方法
        try {
            Method met = this.getClass().getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
            path =(String) met.invoke(this, request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        request.getRequestDispatcher(path).forward(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doPost(req, resp);
    }
}
现在整个代码不存在参数的传递情况,所有的操作都通过了路径传递了,这样的方式就是利用反射实现方法调用。当然整个过程之中,异常的输出意义不大,因为如果出错的话直接跳转到error.jsp就可以了。

3.6.1 解决路径&提示信息问题
所有的Servlet最终都一定要跳转到一个JSP页面,但是在编写跳转路径的时候页面地址太长了,维护很不方便,因为如果要想修改页面路径,那么必须进行程序的修改,这样实在太麻烦了。另外,我们在所有的Servlet一定会有消息进行提示(所有的消息都应该在forward.jsp上显示),那么消息也可以由用户灵活配置。
于是,现在觉得应该利用资源文件进行读取,那么所有的资源文件应该在指定的CLASSPATH之中(假设资源文件保存在WEB-INF/classes下,src下保存的内容直接生成到WEB-INF/classes目录下)。把所有页面的跳转路径都定义在Pages.properties文件里面,而所有的提示信息都保存在Messages.properties里面。
但是如果真的是资源文件,那么久必须考虑国际化程序问题。而幸运的是在ResourceBundle类里面支持了国际化的操作,同时利用Local取得当前的环境属性。
范例:定义Pages.properties文件
forward.page=/pages/forward.jsp
errors.jsp=/pages/errors.jsp
emp.list.page=/pages/back/admin/emp/emp_list.jsp
emp.list.servlet=/pages/back/admin/emp/EmpServlet/list
emp.insert.page=/pages/back/admin/emp/emp_insert.jsp
emp.update.page=/pages/back/admin/emp/emp_update.jsp
范例:定义Messages.properties文件
admin.login.success=管理员登录成功,欢迎光临!
admin.login.failure=管理员登录失败,错误的用户名或密码!

vo.insert.success={0}数据添加成功!
vo.insert.failure={0}数据添加失败!
vo.update.success={0}数据修改成功!
vo.update.failure={0}数据修改失败!
vo.delete.success={0}数据删除成功!
vo.delete.failure={0}数据删除失败!
如果要想进行资源的文件读取,现在很明显不能够使用Properties,原因有二:
1、原因一:此时的文件保存在了CLASSPATH,所以Properties不方便读取;
2、原因二:因为此时的数据存在有占位数据,Properties不能处理。
范例:修改DispatcherServlet
//准备进行Messages.properties文件读取的资源类
    private ResourceBundle messagesResource;
    //准备进行Pages.properties文件读取的资源类
    private ResourceBundle pagesResource;

private static final String PAGESBASENAME = "Pages";
    private static final String MESSAGESBASENAME = "Messages";
    /**
     * 此方法留给一些程序初始化使用,因为在整个程序里面存在有资源文件,
     * 将通过此部分读取资源文件
     */
    @Override
    public void init() throws ServletException {
        //所有的资源文件都会在初始化的时候准备好
        //代码必须考虑到日后扩充的国际化应用,应该利用Locale类取得本地语言编码
        Locale loc = Locale.getDefault();//根据当前语言环境取得语言编码
        this.pagesResource = ResourceBundle.getBundle(PAGESBASENAME, loc);
        this.messagesResource = ResourceBundle.getBundle(MESSAGESBASENAME, loc);
    }
这个时候EmpServlet(DispatcherServlet子类)方法返回的应该是Pages.properties文件里面定义的key值,该操作指的是由Servlet跳转到JSP页面。
public String list(HttpServletRequest request, HttpServletResponse response){
        System.out.println("-------------执行数据列表操作-----------------");
        //return "/pages/back/admin/emp/emp_list.jsp";
        return "emp.list.page";
    }
而后在DispatcherServlet里面进行跳转的时候要使用pagesResource类读取指定的key。
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = "errors.page";//默认的跳转路径
        //得到的路径:/TestReflect/pages/back/admin/emp/EmpServlet/list
        String uri = request.getRequestURI(); //取得用户的相对路径
        //处理后的路径:list
        String methodName = uri.substring(uri.lastIndexOf("/") + 1);
        //根据指定的方法名称反射调用指定的方法
        try {
            Method met = this.getClass().getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
            path =(String) met.invoke(this, request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        request.getRequestDispatcher(this.pagesResource.getString(path)).forward(request, response);
    }
如果现在执行的是增加操作,那么由Servlet应该跳转到forward.jsp,而后需要设置msg和path属性,那么干脆在DispatcherServlet里面增加一个操作方法,而这个操作方法专门负责设置msg与path的内容的key,而后这个内容通过ResourceBundle取得。


JAVA 反射

标签:

原文地址:http://blog.csdn.net/timchen525/article/details/51570955

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!