分享

深度解析反射机制

 太极混元天尊 2018-04-21


前言:



我们通过一个实际的例子来演示反射在编程中的应用,学一个技术,一定是要应用的,可能之前大家对反射的学习,仅仅是停留在概念层面,不知道反射究竟应用在哪,所以是一头雾水。相信通过这篇教程,会让你对反射有一个更深层次的认知。


首先简单介绍反射的概念:

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。


如何理解反射?简单的一句话解释,将传统的开发思路反向逆转。


传统的方式是通过类创建对象:类 ---> 对象。

反射就是将这个过程逆转,通过对象得到类:对象 ---> 类。


通过对象得到的这个类该如何表示?

使用Class类来表示,此类是Java反射的源头,是用来描述其他类的类,Class类的每一个实例化对象就是对其他类的描述。


在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()。

也就是说每一个类,都可以调用getClass()方法获取对应的Class对象,用来描述目标类,我们将这个Class类叫做目标类的运行时类。


有了Class对象,能做什么?


调用Class对象的newInstance()方法,可以动态创建目标类的对象。

要求:

1)目标类必须有无参数构造方法。

2)外部方法有足够的权限访问目标类的构造方法。


除了动态创建目标类的对象,反射也可以动态调用对象的各种方法,访问成员变量。


Java反射机制提供的功能


在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的成员变量和方法。


反射相关的主要API


java.lang.Class:描述一个类。

java.lang.reflect.Method:描述类的方法。

java.lang.reflect.Field:描述类的成员变量。

java.lang.reflect.Constructor:描述类的构造方法。


Class用来描述目标类的结构,叫做目标类的运行时类。

Class的常用方法:

public Class[] getInterfaces()   

返回运行时类实现的全部接口。


public Class getSuperclass()

返回运行时类的父类。


public Constructor[] getConstructors()

返回运行时类的public构造方法。


public Constructor[] getDeclaredConstructors()

返回运行时类的全部构造方法。


public Method[] getMethods()  

返回运行时类的public方法。


public Method[] getDeclaredMethods()

返回运行时类的全部方法。


public Field[] getFields() 

返回运行时类的public成员变量。


public Field[] getDeclaredFields() 

返回运行时类的全部成员变量。


Method用来描述运行时类的方法。

Method的常用方法:

public Class getReturnType() 

返回方法的返回值类型。


public Class[] getParameterTypes() 

返回方法的参数列表。


public int getModifiers() 

返回方法的访问权限修饰符。


public String getName(); 

返回方法名。


public Class[] getExceptionTypes() 

返回方法的异常信息。


Field用来描述运行时类的成员变量。

Field的常用方法:

public int getModifiers()  

返回成员变量的访问权限修饰符。


public Class getType()  

返回成员变量的数据类型。


public String getName()  

返回成员变量的名称。


Constructor用来描述运行时类的构造方法。

Constructor的常用方法:

public int getModifiers()

返回构造方法的访问权限修饰符。


public String getName()

返回构造方法名。


public Class[] getParameterTypes()

返回构造方法参数列表。


反射在实际中的应用主要是动态创建对象,动态调用对象的方法。


1.创建对象:

调用Class类的newInstance()方法创建对象。


2.调用指定方法:

(1)通过Class类的getMethod(String name,Class…parameterTypes)方法获取一个Method对象,并设置此方法操作时所需要的参数类型。

(2)调用Object invoke(Object obj, Object[] args)方法,并向方法中传递目标obj对象的参数信息。


代码:



需求:

创建一个查询数据库的工具类,自动将SQL语句查询出的结果集,封装成不同的对象返回,一个简化版的MyBatis工具。


思路:

工具类查询方法的参数列表:Connection对象,SQL语句,目标运行时类对象clazz,数据表的id值。


1.通过Connection对象,SQL语句,id值查询出对应的结果集。

2.利用反射机制调用clazz的无参构造方法创建目标对象。

3.获取clazz的Filed,即目标类的所有成员变量。

4.找到与成员变量名相同的结果集字段,并获取字段值。

5.通过成员变量名找到对应的setter方法。

6.利用反射机制调用setter方法完成赋值。


实现步骤:

1.导入mysql驱动,c3p0数据源相关jar包。

2.创建c3p0-config.xml。



c3p0-config>

   named-config name='testc3p0'>

       
       property name='user'>rootproperty>
       property name='password'>rootproperty>
       property name='driverClass'>com.mysql.jdbc.Driverproperty>
       property name='jdbcUrl'>jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=UTF-8property>

       
       property name='acquireIncrement'>5property>
       
       property name='initialPoolSize'>5property>
       
       property name='minPoolSize'>5property>
       
       property name='maxPoolSize'>10property>

       
       property name='maxStatements'>20property>
       
       property name='maxStatementsPerConnection'>5property>

   named-config>

c3p0-config>


3.创建数据表student,user。


DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(11) DEFAULT NULL,
 `address` varchar(11) DEFAULT NULL,
 `tel` varchar(255) DEFAULT NULL,
 `score` double(11,1) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 `age` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


4.创建实体类Student,User。


package com.southwind.entity;

public class Student {
   private int id;
   private String name;
   private String address;
   private String tel;
   private double score;
   public int getId() {
       return id;
   }
   public void setId(int id) {
       this.id = id;
   }
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public String getAddress() {
       return address;
   }
   public void setAddress(String address) {
       this.address = address;
   }
   public String getTel() {
       return tel;
   }
   public void setTel(String tel) {
       this.tel = tel;
   }
   public double getScore() {
       return score;
   }
   public void setScore(double score) {
       this.score = score;
   }
   @Override
   public String toString() {
       return 'Student [id=' + id + ', name=' + name + ', address=' + address
               + ', tel=' + tel + ', score=' + score + ']';
   }

}


package com.southwind.entity;

public class User {
   private int id;
   private String name;
   private int age;
   public int getId() {
       return id;
   }
   public void setId(int id) {
       this.id = id;
   }
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public int getAge() {
       return age;
   }
   public void setAge(int age) {
       this.age = age;
   }
   @Override
   public String toString() {
       return 'User [id=' + id + ', name=' + name + ', age=' + age + ']';
   }

}


5.创建JDBCTools工具类。


package com.southwind.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCTools {
   private static ComboPooledDataSource dataSource;

   static{
       dataSource = new ComboPooledDataSource('testc3p0');
   }

   /**
    * 获取Connection
    * @return
    */

   public static Connection getConnection(){
       try {
           return dataSource.getConnection();
       } catch (SQLException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return null;
   }

   /**
    * 释放资源
    * @param conn
    * @param stmt
    * @param rs
    */

   public static void release(Connection conn,Statement stmt,ResultSet rs){
       if(conn != null){
           try {
               conn.close();
           } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }
       if(stmt != null){
           try {
               stmt.close();
           } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }
       if(rs != null){
           try {
               rs.close();
           } catch (SQLException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }
   }
}


6.创建数据库查询工具类MyQueryRunner,核心代码。


package com.southwind.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
* 通用工具类
* @author southwind
*
*/

public class MyQueryRunner {

   /**
    * 将结果集动态封装成对象
    * @param conn
    * @param sql
    * @param clazz
    * @param id
    * @return
    */

   public Object query(Connection conn,String sql,Class clazz,int id){
       PreparedStatement pstmt = null;
       ResultSet rs = null;
       Object obj = null;
       try {
           pstmt = conn.prepareStatement(sql);
           pstmt.setInt(1, id);
           rs = pstmt.executeQuery();
           obj = clazz.newInstance();
           if(rs.next()){
               //遍历实体类属性集合,依次将结果集中的值赋给属性
               Field[] fields = clazz.getDeclaredFields();
               //获取ResultSet数据
               ResultSetMetaData rsmd = rs.getMetaData();
               for(int i = 0; i < fields.length;="">
                   Object value = setFieldValueByResultSet(fields[i],rsmd,rs);
                   //通过属性名找到对应的setter方法
                   String name = fields[i].getName();
                   name = name.substring(0, 1).toUpperCase() + name.substring(1);
                   String MethodName = 'set'+name;
                   Method methodObj = clazz.getMethod(MethodName,fields[i].getType());
                   //调用setter方法完成赋值
                   methodObj.invoke(obj, value);
               }
           }
       } catch (Exception e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return obj;
   }

   /**
    * 根据将结果集中的值赋给对应的属性
    * @param field
    * @param rsmd
    * @param rs
    * @return
    */

   public Object setFieldValueByResultSet(Field field,ResultSetMetaData rsmd,ResultSet rs){
       Object result = null;
       try {
           int count = rsmd.getColumnCount();
           for(int i=1;i<>
               //找到与属性名相同的结果集字段
               if(field.getName().equals(rsmd.getColumnName(i))){
                   //获取属性的数据类型
                   String type = field.getType().getName();
                   switch (type) {
                       case 'int':
                           result = rs.getInt(field.getName());
                           break;
                       case 'java.lang.String':
                           result = rs.getString(field.getName());
                           break;
                       case 'double':
                           result = rs.getDouble(field.getName());
                           break;
                   }
               }
           }
       } catch (SQLException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return result;
   }
}


7.测试

通过id查询student表,调用工具方法,直接返回Student对象。

package com.southwind.test;

import java.sql.Connection;
import com.southwind.entity.Student;
import com.southwind.util.MyQueryRunner;
import com.southwind.util.JDBCTools;

public class Test {
   public static void main(String[] args) {
       Connection conn = JDBCTools.getConnection();
       String sql = 'select * from student where id = ?';
       MyQueryRunner myQueryRunner = new MyQueryRunner();
       Student student = (Student) myQueryRunner.query(conn, sql, Student.class, 1);
       System.out.println(student);
   }
}



通过id查询user表,调用工具方法,直接返回User对象。


package com.southwind.test;

import java.sql.Connection;
import com.southwind.entity.User;
import com.southwind.util.MyQueryRunner;
import com.southwind.util.JDBCTools;

public class Test {
   public static void main(String[] args) {
       Connection conn = JDBCTools.getConnection();
       String sql = 'select * from users where id = ?';
       MyQueryRunner myQueryRunner = new MyQueryRunner();
       User user = (User) myQueryRunner.query(conn, sql, User.class, 30);
       System.out.println(user);
   }
}



源码:


github

https://github.com/southwind9801/ReflectDemo.git







【框架合集】SpringMVC教程汇总


【框架合集】Hibernate教程汇总


【框架合集】MyBatis教程汇总


【框架合集】Spring教程汇总



专业 热爱 专注

致力于最高效的Java学习

Java大联盟



扫描下方二维码,加入Java大联盟



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多