(十七)Core Java 反射的使用(框架的开发原理) (115)

 
目录 :           1 ) . 框架的概念及用反射技术开发框架的原理

2 ) .  用类加载器的方式管理资源和配置文件

3 ) .   由内省引出JavaBean的讲解

4 ) . 对JavaBean的简单内省操作

5 ) . 对javaBean的复杂内省操作

6 ) . 使用BeanUtils工具包操作JavaBean

 
 


     一 .   框架的概念及用反射技术开发框架的原理

1 ) . 实现框架的核心原理 : 

1.1 往常 :  需要什么 ,造什么  ,   拿什么 

1.2  反射:  提前做好, 需要什么 , 拿什么

2 ) . 调用方法的两种思路 : 

1. 第一种是  我用它 ,因此 我调用他

2. 第二种是  我用它,因此他调用我

3 ) .  框架与工具的区别 : 

3.1 框架如同房子,工具如同锁 ,房子是通用型的,锁是功能型的

3.2 框架使用是是 他调用别人,  而 工具 使用是  是 别人 调用他 

4 ) . Demo:

package cn.ittext.day01;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
public class ReflectApplication07
{
       
        //实现一个框架原理
       
       
       
        public static void main(String[] args ) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
       {
                    
             
              /*
              * 原始实现:
              *
              * 需求 : 向一个集合放一些元素并迭代输出
              *
              * 第一步:创建集合
              *
              * 第二步:放入元素
              *
              * 第三步:迭代输出
              *
              */
             
              Collection<String> collections = new LinkedList<String>();
             
             
                     collections .add( "A" );
                     collections .add( "B" );
                     collections .add( "C" );
                     collections .add( "D" );
                           
              for (Iterator<String> it = collections .iterator() ; it .hasNext();)
             {
                    
                     sop ( it .next());
       
             }
             
             
             
             
              /*
              * 反射框架实现:
              *
              *
              * 第一步 :创建一个资源文件,里边写入相关配置 ; className=java.util.LinkedList
              *
              * 第二步 :引入资源文件,并将资源文件放入资源类中,因为资源类是键值对的好取值
              *
              * 第三步 : 关闭系统资源 ;  关乎java程序的资源可不管,因为有GC,但关乎调用系统资源的必须得关,否则会泄露
              *
              * 第四步 : 获取资源中的值,也就是对象地址
              *
              * 第五步 : 通过class反射进行实例化
              *
              * 第六步 : 添加数据
              *
              * 第七步 : 迭代数据
              */
             
             
              /*
              *
              * 资源文件须知 :
              *
              * 【1】 地址是可找到的,通过鼠标放到相关对象上就可看到地址
              *
              * 【2】 地址是不可用“” 标识的,否则找不到
              *
              * 【3】 键与值之间必须是一个=号关联的
              *
              * 【4】结束标记一定不可以写;否则会出现找不到该类
              *
              * 【5】配置文件放的地址应该是与src目录在同一级
              */
             
             InputStream fis   = new FileInputStream( "config.properties" );
             
             Properties ps = new Properties();
             
              ps .load( fis );
             
              fis .close();
             
             String className = ps .getProperty( "className" );
             
              sop ( className );
             
             
             Collection<String> collection (Collection) Class. forName ( className ).newInstance() ;
             
              collection .add( "A" );
              collection .add( "B" );
              collection .add( "C" );
              collection .add( "D" );
                    
              for (Iterator<String> it = collection .iterator() ; it .hasNext();)
             {
                    
                     sop ( it .next());
       
             }
 
 
 
       }
       
        public static void sop(Object obj )
       {
             System. out .println( obj );
       }
       
       
       
}
5 ). config.properties

className= java.util.LinkedList

小结 :  

           1.关闭资源关闭的是与本地操作系统相关联数据的资源,关闭的是物理资源;  而逻辑资源,也就是java源程序产生的垃圾则由GC进行处理
        
    
 

       二. 用类加载器的方式管理资源和配置文件


1 ) . 资源配置文件路径的设置 : 

1.1 须知 : 必须是 绝对路径的,而绝对路径又是不固定的(硬编码),因此通过getRealPath() 获取在硬盘上的总目录 +  在 项目中存放的 路径 

1.2 为何不用相对路径? 因为相对路径是相对的每一个指令运行的当前目录, 这个是不固定的

2 ) . 获取类加载器加载资源文件的方式:"

2.1 类.class 获取类字节码文件 --> . getClassloader() 获取类加载器  -->  .getResourceAstream("classPath下的路径")  获取资源文件地址

3 ) .  Demo:

package cn.ittext.day01;
import java.io.FileInputStream ;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
 
public class ReflectApplication08
{
        public static void main(String[] args ) throws IOException
       {
             
              /**
              * 庖丁解牛 : 类加载器的加载路径详解
              *
              * 常用 : 第三种与第四种方式;  相对的是本项目中的class类字节码文件地址, 绝对的是bin下的精确地址
              *
              * 总结:
              *
              *  [1]class类加载器直接加载配置文件的方法getResourceAsStream() 底层调用的还是getClassLoader() 类加载器
              *
              *  [2] 当配置文件放在不同目录时, 需要使用绝对路径去加载配置文件地址
              *
              *
              *
              */
             
             
              /**
              * 第一种方式 :  配置文件与 src 路径平级时的加载方式
              *
              * 分析 : 这种方式 是 相对的 ,几乎不用,因为 正常情况下 地址是不固定的
              *
              */
             
             
             
              /*InputStream is =new FileInputStream("config.properties");
             
             Properties ps =new Properties();
             
             ps.load(is);
             
             String className = ps.getProperty("className");
             
              sop (className);*/
             
             
              /**
              * 第二种方式 :  配置文件在 src 目录下,用类加载的方式加载资源文件 --> 完整版
              *
              * 分析 : 这种方式配置的路径是绝对路径
              *
              */
/*           //通过类获取字节码文件,再获取类加载器,再获取加载资源文件方法以此加载资源配置文件
             InputStream resourceAsStream = ReflectApplication08.class.getClassLoader().getResourceAsStream(" cn / ittext /day01/config.properties");
             
             Properties ps =new Properties();
             
             ps.load(resourceAsStream);
             
             String className = ps.getProperty("className");
             
              sop (className);*/
             
             
              /**
              * 第三种方式 :  配置文件在 src 目录下的源文件地址中(classPath中),用类加载的方式加载资源文件 --> 简化版 : 相对路径
              *
              * 分析 : 这种方式配置的路径是相对路径 --> 相对的是 src 目录下的当前源文件(.class类文件)地址
              *
              */
              //通过类获取字节码文件,然后获取加载资源文件方法以此加载资源配置文件
        /*     InputStream resourceAsStream = ReflectApplication08.class.getResourceAsStream("config.properties");
                           
             Properties ps =new Properties();
             
             ps.load(resourceAsStream);
             
             String className = ps.getProperty("className");
             
              sop (className);*/
             
             
              /**
              * 第四种方式 :  配置文件在 src 目录的其他目录下(不在classPath中),用类加载的方式加载资源文件 --> 简化版 : 绝对路径
              *
              * 分析 : 这种方式配置的路径是绝对路径 ,是配置 bin以后的路径
              *
              */
             
             InputStream resourceAsStream = ReflectApplication08. class .getResourceAsStream( "/cn/ittext/day01/source/config.properties" );
             
             Properties ps = new Properties();
             
              ps .load( resourceAsStream );
             
             String className = ps .getProperty( "className" );
             
              sop ( className );
             
             
       }
       
       
        public static void sop(Object obj )
       {
                    
             System. out .println( obj );
       }
       
}

 

小结 :  

           1.  Eclipse将src目录下的java文件自动编译成.class文件 放入 用户目录 ,  将 src目录下的其他文件 原封不动的放入 用户目录
        
           2.  框架的配置文件都是放在classPath目录下,通过类加载器的方式进行加载获取的,对应到项目中就是放在 src目录下与源文件放在一起

           3. 加载本包内的相关资源文件用相对路径,加载不同包内的相关资源文件用绝对路径

  


      三. 由内省引出JavaBean的讲解

1 ) . 关于javaBean中内部属性的规则:

1.1 若get/set 之后单词的第二个字母是小的,则第一个字母变成小的  ;  案例 : Age  -->  age  

[1] gettime   --> time

[2] setTime  -->time

[3] getCPU --> CPU

2 ) . 什么是内省?  IntroSpector

2.1  描述 :   内省就是对内部进行检查,窥探底层细节,逻辑 ; 

2.2 功能 : 对 javaBean进行操作 ;
 

3 ) .  什么是 javaBean?

3.1 描述 : javaBean是一种特殊的java类,内部是按照约定俗成的方式进行构建的,符合某种特定的规则

3.2 须知  :  一个javabean可以当作java类进行操作,而一个java类是不一定可当作javabean操作的

 
      四. 对JavaBean的简单内省操作  --》这里有代码重构方式

1 ) . Demo: 完整版 未封装

package cn.ittext.day02;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTest
{
       
        public static void main(String[] args ) throws NoSuchMethodException, SecurityException, IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
       {
             
              /**
              *
              * 庖丁解牛 :操作 Javabean 的两种方式
              *
              * 需求: 获取JavaBean中的属性Name,设置javaBean中的属性Age
              *
              * 第一种 : 传统方式
              *
              * 第二种 : 反射方式
              *
              * 总结 :
              *
              * 【1】 也就是通过反射的方式获取其读写方法而后进行调用
              *
              * 【2】PropertyDescriptor类 可将其 属性名 与 对应类字节码文件关联访问其访问器方法
              *
              *
              *
              */
       
              //传统方式
             
              /*
              JavaBeanDemo jbd =new JavaBeanDemo("张三",13);   //实例化javaBean对象
             
             String name = jbd.getName();  //获取属性 name
             
             jbd.setAge(20);  //设置属性age
             
              int age = jbd.getAge();  //获取属性age
             
              sop (name+"::"+age);  //输出验证
             
              */
             
       
              /**
              * 反射方式  --> 获取参数
              *
              * 思路 :
              *
              * 【1】 实例化对象
              *
              * 【2】自定义想要属性的变量名
              *
              * 【3】 将自定义属性变量名通过PropertyDescriptor与对应对象的字节码文件之关联起来访问其访问器方法
              *
              * 【4】 获取其属性读的方法
              *
              * 【5】用已有对象调用其方法
              *
              */
             
        /*      JavaBeanDemo jbd =new JavaBeanDemo("张三",13);   //实例化javaBean对象
             String propertyName="name";  //自定义想要获取变量对应的属性名
             PropertyDescriptor pd =new PropertyDescriptor(propertyName, jbd.getClass()); //将属性名与与javaBean的字节码文件相关联访问其访问器方法
             
             Method readMethod = pd.getReadMethod(); //获取属性名对应的读方法
             
             Object retVal = readMethod.invoke( jbd );  //使用 jdb 调用读方法,读出数据
             
              sop (retVal);
             */
       
              /**
              * 反射方式  --> 设置参数
              *
              * 思路 :
              *
              * 【1】 实例化对象
              *
              * 【2】自定义想要属性的变量名
              *
              * 【3】 将自定义属性变量名通过PropertyDescriptor与对应对象的字节码文件之关联起来
              *
              * 【4】 获取其属性写的方法
              *
              * 【5】用已有对象调用其方法
              *
              */
             
                    JavaBeanDemo jbd = new JavaBeanDemo( "张三" ,13);   //实例化javaBean对象
       
                    String propertyName = "name" //自定义想要获取变量对应的属性名
       
                    PropertyDescriptor pd = new PropertyDescriptor( propertyName , jbd .getClass()); //将属性名与与javaBean的字节码文件相关联,访问其访问器方法
                    
                    Method writeMethod = pd .getWriteMethod();
                           
                    Object invoke = writeMethod .invoke( jbd , "王五" );
                    
                     sop ( jbd .getName());
 
       }
       
       
        public static void sop(Object obj )
       {
                    
             System. out .println( obj );
       }
       
}
 
2 ) . Demo: 完整版 封装版  --》使用了代码重构


package cn.ittext.day02;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTestEnc
{
       
        public static void main(String[] args ) throws NoSuchMethodException, SecurityException, IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
       {
             
              /**
              *
              * 庖丁解牛 :
              *
              * 需求: 获取JavaBean中的属性Name,设置javaBean中的属性Age
              *
              *
              * 第二种 : 反射方式  -- 》对完整版的做了代码重构
              *
              *
              *
              * 总结 : 代码重构方式
              *
              * 【1】 选中可进行封装的一堆代码,前提是保证其代码中没有常量存在
              *
              * 【2】右击 -- Refactor -- 》Extract  Method -- 》 输入封装的方法名 -- 》而后 Ok 即可
              *
              *
              *
              *  附加 :我们还可以做接口的提取
              * 
              */
       
        
                JavaBeanDemo jbd = new JavaBeanDemo( "张三" ,13);   //实例化javaBean对象
       
                    String propertyName = "name" //自定义想要获取变量对应的属性名
       
                    Object retVal = getProperty ( jbd , propertyName ); //调用其自定义的获取资源的方法
                    
                     sop ( retVal );
       
                    String vlaue = "王五" ;
       
                     setProperty ( jbd , propertyName , vlaue );//调用其自定义的设置资源的方法
                    
                     sop ( jbd .getName());
 
       }
        //以下代码是通过代码重构的方式得来
        private static void setProperty(JavaBeanDemo jbd , String propertyName , String vlaue )
                     throws IntrospectionException, IllegalAccessException, InvocationTargetException {
             PropertyDescriptor pd = new PropertyDescriptor( propertyName , jbd .getClass()); //将属性名与与javaBean的字节码文件相关联
             
             Method writeMethod = pd .getWriteMethod();
                    
             Object invoke = writeMethod .invoke( jbd , vlaue );
       }
        private static Object getProperty(JavaBeanDemo jbd , String propertyName )
                     throws IntrospectionException, IllegalAccessException, InvocationTargetException {
             PropertyDescriptor pd = new PropertyDescriptor( propertyName , jbd .getClass());
             
             Method readMethod = pd .getReadMethod();
             
             Object retVal = readMethod .invoke( jbd );
              return retVal ;
       }
       
       
        public static void sop(Object obj )
       {
                    
             System. out .println( obj );
       }
       
}

小结 :  

           1.  javaBean对象也称为值对象,就是传递值的对象
        
           2.  所谓的简单操作就是用反射的方式读写 ,记得一个资源描述类 PropertyDescriptor 用来将自定义属性名与 类字节码文件相关联,以便于去字节码文件中查找对应的访问器方法

       


     五 .  对javaBean的复杂内省操作

1 ) . Demo:

package cn.ittext.day02;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTest01
{
        public static void main(String[] args ) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
       {
              /**
              *
              *
              * 需求 : 对 javabean 的内省操作,使用正向思维的方式
              *
              * 分析:较上一章的反射方式,此章略显繁琐,但思路明确简单
              *
              * 方法解析: 
              *
              *
              *     beanInfo.getPropertyDescriptors();  获取资源描述指 类的属性描述
              *
              *  beanInfo.getMethodDescriptors();  获取方法描述是指类的方法描述
              * 
              *  beanInfo.getBeanDescriptor();   获取bean描述是指 类的描述
              * 
              * 
              *  总结方法:
              * 
              *  【1】 Introspector 类 : 用来了解目标Java Bean支持的属性,事件和方法。
              * 
              *  【2】getBeanInfo();  用来获取相关JavaBean的属性,事件和方法 与 已存在的 javabean 关联
              * 
              *  【3】getPropertyDescriptors();  用来获取 javaBean中的详细的所有资源(属性)描述
              * 
              *  【4】getReadMethod();  获取读取的方法
              * 
              *  【5】invoke();  执行方法的方法
              * 
              */
             
             
             JavaBeanDemo JBD = new JavaBeanDemo( "ZHANGSAN" , 20);
             
             String propertyName = "age" //定义好的想要获取属性的变量名
 
              getProperty ( JBD , propertyName );
             
             
             
       }
        //以下代码使用了代码重构,方法提取
        private static void getProperty(JavaBeanDemo JBD , String propertyName )
                     throws IntrospectionException, IllegalAccessException, InvocationTargetException
       {
             BeanInfo beanInfo = (BeanInfo) Introspector. getBeanInfo ( JBD .getClass()); // 通过 Introspector 获取其 javabean 的属性方法信息
             
             PropertyDescriptor[] propertyDescriptors = beanInfo .getPropertyDescriptors(); //获取所有属性资源描述信息
       
              for (PropertyDescriptor pd : propertyDescriptors //迭代所有属性资源信息
             {
                    String name = pd .getName();  //获取其资源信息名字
                    
                     if ( name .equals( propertyName ))  //判断是否与自定义的属性变量名是否吻合,若吻合
                    {
                            Method readMethod = pd .getReadMethod(); // 则 获取其读取方法
                           
                            Object invoke = readMethod .invoke( JBD );  //通过 javabean 类调用其方法对象获取到想获取的值
                           
                            sop ( name + ":" + invoke );  //输出 值
                    }
 
             }
       }
       
        public static void sop(Object obj )
       {
                    
             System. out .println( obj );
       }
       
       
}

 
 

       六. 使用BeanUtils工具包操作JavaBean


1 ) . Demo:

package cn.ittext.day02;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
 
public class IntroSpectorTest02
{
        public static void main(String[] args ) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
       {
             
              /**
              *
              *
              * 庖丁解牛 : 通过工具类获取/设置一个javaBean中的属性
              *
              * 第一种:获取
              *
              * 第二种:设置
              *
              * 小结 :
              *
              *  [1] BeanUtils工具类可设置/获取javaBean属性
              * 
              *  [2]BeanUtils工具类可对javaBean进行级联设置/获取操作
              *
              *     [3] BeanUtils工具类与PropertyUtils都是对 javaBean属性进行操作,不一样的地方在于 BeanUtils对javaBean属性全部以字符串类型操作,而PropertyUtils是值的原类型进行操作
              *
              *   [4]BeanUtils工具类也可对map集合进行操作
              *
              */
             
       
       
              //----------------------------------------------[1]
             
             JavaBeanDemo jbd = new JavaBeanDemo( "zhangsan" ,20);
             
             String propertyAge = "age" ;
             
             String propertyName = "name" ;
             
             String proName = BeanUtils. getProperty ( jbd , propertyName );  //获取相关属性
             
              sop ( "proName:::" + proName );
             BeanUtils. setProperty ( jbd , propertyAge , "22" ); //设置相关属性 , int 类型之所以加上"" ,一是因为主页面接受接受的是 字符串类型 ,而且 此方法有自动转型转换的功能
 
             String proAge = BeanUtils. getProperty ( jbd , propertyAge ); 
             
              sop ( "proAge:::" + proAge );
             
              //----------------------------------------------[2]
             
             String propertyBirths = "birthday.time" ;
             
             String proBirthBefor = BeanUtils. getProperty ( jbd , propertyBirths );
              sop ( "时间设置之前: proBirth:::" + proBirthBefor );
             
             BeanUtils. setProperty ( jbd , propertyBirths ,11100000);
             
             String proBirthEnd = BeanUtils. getProperty ( jbd , propertyBirths );
              sop ( "时间设置之后: proBirth:::" + proBirthEnd );
             
              //-------------------------------------------------[3]
             PropertyUtils pu = new PropertyUtils();
             
             String propertName = "name" ;
             
             PropertyUtils. setProperty ( jbd , propertName , "zhangsan" );
             
             String property = (String) PropertyUtils. getProperty ( jbd , propertName );
             
              sop ( property );
 
              //-------------------------------------------------[4]
             
             Map<String, String> map = new HashMap<>();
             
              map .put( "name" , "zhangsan" );
             
             BeanUtils. setProperty ( map , "name" , "李四" );
             
              sop ( map );
             
       }
       
       
        public static void sop(Object obj )
       {
                    
             System. out .println( obj );
       }
       
       
}

 

小结 :  

           1.  学会使用BeanUtils 工具类与 PropertyUtils 工具类 对javaBean进行操作
      


      七. 框架总结 : 

1 ) . 用资源配置文件的方式 反射 实例化对象

2 ) . 通过类加载器加载资源配置文件 ,关键的地方在于 配置文件的存放位置的不同会导致 使用 相对路径还是绝对路径的方式不同

3 ) .  通过 PropertyDescriptor类对javaBean进行属性访问器方法的 处理操作 ;  也可通过Introspector 类 的getBeanInfo() 方法对 javaBean进行资源方法类的操作

4 ) . 通过代码重构,方法提取的方式 对 臃肿的代码进行提取封装,以此简化代码

5 ). 站在巨人肩膀上学习变成,使用工具类 BeanUtils 与 PropertyUtils

 
          
 

 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值