(13) - 内省和注解 (图)

---------------------- ASP.Net+Android+IO开发S.Net培训、期待与您交流! ----------------------


一、内省(IntroSpector)


1、 内省概述

IntroSpector即内省,是对内部进行检查,了解更多细节。

为什么要学内省?在开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性。

内省的主要作用是操作JavaBean,它能在对象间进行数据的传送。

 

2、 JavaBean

JavaBean是一种特殊的Java类,也可以说成是一种类的规范,满足该规范的类叫JavaBean。这种类需符合某些特定的规范,如:要有无参的构造函数,属性私有,这些属性通过加前缀get和set特定的命名规则构成方法给外部进行访问等。

 

(1)JavaBean的属性方法构成规则,它的私有属性名(Bean属性)首字母大写,分别加上get和set的前缀,构成setXxx()和getXxx()供外部访问,外部也能通过,这些方法名逆向得知类中的Bean属性。

 

(2)一个JavaBean类,可以当作普通的类来操作,但是一个普通的类不一定能当做JavaBean类使用,因为它可能没有setter和getter方法。

 

(3)作用:如果需要在两个模板间传递多个信息,可将这些信息封装成一个JavaBean,这种JavaBean的实例对象称之为值对象(Value Object,简称VO)。

为什么要这样呢?这样不会显得麻烦?直接在模板间传递不就行了吗?

首先,它是一种面向对象的思想,它是对数据进行封装的,我们需要访问数据时,访问这个对象就可以了。其次,当某个方法需要传入多个参数,比如10个,难道要写10个形参,这用JavaBean进行封装,只需写一个JavaBean的对象形参则可,具体操作交给JavaBean则可。简单说它的作用就是为了方便操作数据的。

 

结构图:


3、 内省操作JavaBean属性的两种方式

(1)对JavaBean的简单内省操作

该方式通过PropertyDescriptor(属性描述器)操作Bean属性。

PropertyDescriptor类通过JavaBean的setter/getter操作一个属性,它的getReadMethod方法获取getter的方法,getWriteMethod方法获取setter的方法,再利用反射方式采用Method的invoke进行操作。

例子:

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
 
public class EasyDemo
{
     public static void main(String[] args) throws Exception
    {
        Demo d = new Demo(1, 2);
        //属性名
        String propsName = "x";
        //PropertyDescriptor类通过JavaBean的存储器导出一个属性
        PropertyDescriptor pd =new PropertyDescriptor(propsName, d.getClass());
        //获取JavaBean属性的getter方法
        Method methodGetX = pd.getReadMethod();
        //利用反射读取值
        Object retVal = methodGetX.invoke(d);
        System.out.println(retVal);
       
        //获取JavaBean的setter方法
        Method methodSetX = pd.getWriteMethod();
        //反射设置值
        methodSetX.invoke(d, 4);
        System.out.println(d.getX());
    }
}
 
class Demo//JavaBean类
{
    private int x;
    private int y;
 
    public Demo(int x, int y)
   {
        this.x = x;
        this.y = y;
   }
   public int getX()
  {
        return x;
   }
   public void setX(int x)
  {
        this.x = x;
  }
   public int getY()
  {
        return y;
  }
  public void setY(int y)
  {
        this.y = y;
  }
}


(2)对JavaBean的复杂内省操作

Introspector:内省的支持类,为JavaBean的属性,事件和方法的知识提供了一个标准的方法,寻找其显示或隐式信息,使用这些信息构建一个全面描述目标bean的BeanInfo对象。

BeanInfo:是个接口,顾名思义,存储bean的信息。

 

该方式通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(即PropertyDescriptor),通过这个属性描述器就可以获取某个对应的getter/setter方法,然后通过反射机制来调用这些方法。

代码:

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

class Demo
{
.....//Demo类同上面的“JavaBean的简单内省操作”示例代码
}

public class ComplexDemo
{
       public static void main(String[] args) throwsException
      {
              Demo d = new Demo(1, 2);
              String proptName = "x";//要获取的属性名
       
              //通过Introspector返回一个Bean的BeanInfo对象
              BeanInfo beanInfo =Introspector.getBeanInfo(d.getClass());
       
              //获取BeanInfo中的所有属性,PropertyDescriptor数组接收
              PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();
              Object retVal = null;
       
              for(PropertyDescriptor pd : pds)
             {
                    //遍历这些属性,找到需要的
                    if(pd.getName().equals(proptName))
                    {
                           //利用反射机制取值
                           Method methodGetX =pd.getReadMethod();
                          retVal =methodGetX.invoke(d);
                          break;
                   }
             }
             System.out.println(retVal);
}
}


4、 BeanUtils工具包

BeanUtils工具包由阿帕奇提供的,为了便于Bean的操作和开发。应用这些包需下载包,在工程中导入要用的jar包。

导入包后,则可直接用该工具类操作JavaBean的属性,如果报NoCalssDefFoundError:…../LogFactory错误,则还需导入logging日志包。

对JavaBean属性的操作:

Demo d = new Demo();//自定义类,有一私有属性x
//获得属性
BeanUtils.getProperty(d,”x”);
//设置属性,参数3为设置值,只不过要用字串表示
BeanUtils.setProperty(d,”x”,“9”);


也支持属性的级联操作:

//如在Demo中有一私有属性为privateDate date= new Date();
//而Date类中有setTime的setter方法,则在Date中有time属性,
//则可用级联方式操作,从属性date-->time:
BeanUtils.setProperty(d,”date.time”,“222”);
BeanUtils.getProperty(d,”date.time”);

工具类的操作方法很多,需慢慢去试,这里不赘述了。

 



二、注解


1、 注解概述

注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则没有某种标记。以后,javac编译器,开发工具和其他程序可以反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。

标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

 

注解的格式比较简单:一个注解,它以@开头,后面是注解的关键字。


2、 三个基本内置注解

(1)@SuppressWarnings(“deprecation”);

     忽略特定的警告,有时再用某些语句时,会提示一些警告,比如你用了过时的方法,它就会提示你该方法过时,你可以在语句前加该句注释,就不会在出现警告了。

(2)@Deprecated

     在某个方法前加了该注释,就标记着,该方法过时了,别人用时会有过时警告。

(3)@Override

     在要复写父类的方法上加上该注释,能检测你覆盖的方法是否写正确。


3、 注解的应用

注解就相当于一个源程序要调用的类,当你要使用某个注解时,那就得先准备好这个注解类。你用注解,相当于你调用一个类,那么这个类得先存在。

 

(1)注解的应用结构

注解类:一个注解的定义相当于一个类,格式有点特殊为@interface A{}。

 

应用了注解的类:就是在一个类上加注解。

 

对应用了注解的类进行反射操作的类:别人在用到你这个带注解的类,如何去知道该类是否带注解,就是再写一个类,该类负责反射检查,检查用的这个类是否带注解。

结构图:



(2)元注解(读以下内容,要区分元注解和注解)

元注解:即注解的注解,元注解服务于注解。那么一个注解类会用到什么样的元注解呢?如代表注解生命周期(Retetion)和存放位置(Taget)的元注解等。

a) @Retetion:

该元注解用于说明注解可以存在于哪个时期,即注解的生命周期。

一个注解的生命周期可以有三个:

java源程序—>(通过Javac)-->calss文件—>(类加载器)-->内存中的字节码。

//@Retetion可取的值:
//Retetion对应了上述的三个时期,每个时期用一个枚举值表示:
RetetionPolicy.SOURSE //Java源文件时期,如@Overried,@SuppressWarning
RetetionPolicy.CLASS //class文件时期(默认时期,即不写时默认该时期)
RetetionPolicy.RUNTIME //运行时期,如@Deprecated

b) @Taget:

这个元注解说明了注解可以存放在那些类元素上,默认是任何元素。

如当你在注解类上添加元注解:@Taget(ElementType.METHOD),那么你在用该注解类时,只能放在方法上,若放在类上,则编译出错。

也可以@Taget(ElementType.METHOD,ElementType.TYPE)那么在类和方法上都可以。


存在于每种类元素上都由一个枚举值表示:

ElementType.PACKAGE //包
ElementType.FIELD //字段,包括枚举
ElementType.ANNOTATION_TYPE //注解类型
ElementType.CONSIRUCTOR //构造器(构造函数)
ElementType.METHOD //方法,不包括构造函数
ElementType.PARAMETER //参数
ElementType.TYPE //类,接口,枚举,不能是注解
ElementType.LOCAL_VARIABLE  //局部变量


 那么看一个两者结合的例子:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
//元注解,注解的注解,服务注解的
//该元注解表示服务的注解可用到运行期
@Retention(RetentionPolicy.RUNTIME)
 
//注解可用在方法和类的位置上
@Target({ElementType.METHOD,ElementType.TYPE})
 
//自定义的注解
@interface MyAnnotation
{
}
 
@MyAnnotation
class Test//用了注解的类
{
}
 
//该类用于反射检查Test类的注解
public class AnnotationTest
{
       public static voidmain(String[] args)
       {
              //检查类上是否有注解
              if(Test.class.isAnnotationPresent(MyAnnotation.class))
              {
                     //通过反射获取注解
                     MyAnnotation annotation = (MyAnnotation)Test.class.getAnnotation(MyAnnotation.class);
                     System.out.println(annotation);
              }
       }
}

上述代码,你若将@Retention(RetentionPolicy.RUNTIME)语句去掉,那么你运行该程序,没有结果,因为不加Retention元注解的注解默认是生存到RetetionPolicy.CLASS时期,即class文件时期,到运行时期,该注解的生命周期已过。这也体现了元注解对于注解的作用。


4、 为注解增加基本属性

其属性的定义,类似于接口的方法,如String color();,即增加了一个color属性,看上去类似于接口的抽象方法,没有实现体。

调用一个属性:@MyAnnotation(color = “red”)

有多个属性:@MyAnnotation(color = “red”,value = “abc”)

 

当其它属性已用default指定默认值,还剩一个没初始的时,调用时可以不用写属性名,直接写值。像@SuppressWarnings("deprecation")就是如此,如下例:

@interface MyAnnotation2
{
   String color() default "red";//默认赋值了
   String value();//剩一个
}
//应用时写成:
@MyAnnotation2("abc")


又如@Retention(RetentionPolicy.RUNTIME),其实就是设置了一个RetentionPolicy的枚举值。

 

其中可以设置的属性类型很多,像接口方法一样声明就是,如数组,String,Class类型,枚举,注解等。

看下面的代码了解用法:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//先定义一些类型,用作注解的属性

//定义一个枚举
enum Lamp
{
        RED,GREEN;
}

//定义一个注解,当属性类型用的
@interface MyAnnot
{
    String name();//它也有个属性
}


//下面开始演示一些可添加的属性

//元注解
@Retention(RetentionPolicy.RUNTIME)
//自定义的注解
@interface MyAnnotation
{
        //其属性的定义类似接口的方法,只有声明没实现体
        String color();

        int[] arry1();//数组
        int[] arry2() default {1,2,3,4};

        //枚举类型
        Lamp lamp() default Lamp.RED;

        //注解类型
        MyAnnot annot1() default @MyAnnot(name ="aaaa");//默认格式
        MyAnnot annot2();
        
        //Class类型
        Class cls();
}

//该注解的属性赋值,默认的可以不赋值
@MyAnnotation(color= "red",
                             arry1 = {1,2,3},
                             annot2 =@MyAnnot(name = "bbbb"),//注解类型的非默认格式
                             cls = String.class
                             )
class Test
{//用了注解的类
}
 
//该类用于反射检查Test类的注解
public class AnnotationTest
{
        public static void main(String[] args)
        {
                //检查类上是否有注解
                if(Test.class.isAnnotationPresent(MyAnnotation.class))
                {
                       //通过反射获取注解
                       MyAnnotation annotation =(MyAnnotation)Test.class.getAnnotation(MyAnnotation.class);
                       System.out.println(annotation.color());//String类型
                       System.out.println(annotation.arry1().length);//数组
                       System.out.println(annotation.lamp());//获取枚举属性
                       System.out.println(annotation.annot1().name());//获取注解类型的属性
                       System.out.println(annotation.cls());//获取Class类型的值
                }
}
}


---------------------- ASP.Net+Android+IO开发S.Net培训、期待与您交流! ----------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值