`

内省-注解-动态代理 .

 
阅读更多

一、注解(Annotation)


从JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)
可以说注解就相当于一个修饰符,可以修饰包,变量,方法,类等
三个基本的Annotation:
1).@Override  这个注解很熟悉了,只能修饰方法,标上此注解就表明该方法重写了父类的方法


2).@Deprecated  用于表示某个程序元素(类, 方法等)已过时,变量或者方法标上此注解后,在调用时会出现"已过时"的标记
  有些时候有些方法或者属性的确已经过时了,但其他一些方法中也的确调用过这类方法,所以这些过时方法也不能删除,
  但是我们希望以后不在使用这些方法,就可以加上@Deprecated


3).@SuppressWarnings  抑制编译器警告,通常的用法为:@SuppressWarnings("unchecked"),即不检查警告

自定义一个注解
pbulic @interface Overload
{
 //定义注解的一个属性
 String value();
}

Annotation的属性声明方式:String name();
Annotation属性默认值声明方式:String name() default “xxx”;


特殊属性value:如果注解中有一个名称为value的属性,那么使用注解时,可以省略value=部分,例如:@MyAnnotation(“xxx")

元Annotation指修饰Annotation的Annotation,以下列举了几种:
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域,
 @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。


 RetentionPolicy.CLASS: 编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注释. 这是默认值
 RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释
 RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释


@Target: 指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value 的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解其中比较常用的@Retention(RetentionPolicy.RUNTIME)
指定注解修饰方法还是变量 @Target(value = {ElementType.METHOD, ElementType.FIELD}),修饰类的注解不用写@Target

 

 

二、类加载器
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象
当 JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构:

bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的,因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库

 

extension classloader:扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,
所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的

 

Appclassloader: 系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。

 

上面摘抄了一些文章,三种类加载器介绍的很详细

 

classloader 加载类用的是全盘负责委托机制
全盘负责:
  我们写了一个类Person,由类加载器classloader来加载,假如Person类中还调用了其他类,则这些其他类被加载的类加载器
  通常也是加载Person类的加载器
 
委托机制:
  比方我们调用了一个类(不管是核心类,扩展类还是自己编写的类),加载的时候首先会去找父类的加载器,然后父类的加载器就推给爷爷加载器
  一级级往上推,当super的加载器可以加载,那就加载此类,如果不行就往下推,下面的能加载就加载,不行的话在往下推,就这样直到类被加载
 
 
三、动态代理
这块只是挺抽象的,因为目前学到的知识中还没有动态代理的一些应用场景,所以总结起来就相对很无奈...
从开始学Java SE时,我们就明白了万物皆对象,在java里,每个对象都有一个类与之对应
现在要生成某一个对象的代理对象,这个代理对象也要通过一个类来生成,所以首先要编写用于生成代理对象的类,举个例子
自定义一个Person类
class Person
{
 public void sing(){System.out.println("sing")}
 public void dance(){System.out.println("dance")}
}
这里需要说明一点,被代理对象必须要实现某个接口,这个不暂时还不知道为什么,应该是一种机制吧
interface Pe
{
 void sing();
 void dance();
}
然后创建一个代理对象来包装Person类的对象


如何编写生成代理对象的类,有两个要素:
代理谁:  这个我们需要在编写代理类的时候就要指定需要被代理的对象
如何生成被代理对象:  设计一个方法生成代理对象(这个地方有歧义,我理解但写不出来,这个方法返回的代理对象可不是new一个我们编写的代理类,方法返回的对象是JavaAPI提供的一个代理类Proxy生成的对象)

 

咱们来编写代理类
class PersonProxy
{
 private Person person;
 
 public PersonProxy(Person person)
 {
  this.person = person;
 }
 
 public Object getProxyPerson()
 {
  
 } 
}
getProxyPerson()返回的是一个Object类型,方法该如何写,这是个难点
咱们查询下API,参考下Proxy类,发现有一个类似newInstance的方法,有戏...
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);


来好好研究下这些参数:
ClassLoader loader 表示用的哪个加载器,这个好办,咱们写的代理类用的是哪个就写哪个 即:PersonProxy.class.getClassLoaderI();
Class<?>[] interfaces 怎么为Person类创建代理对象,那么此处的接口就是被代理对象实现的接口 即:person.getClass().getInterfaces();
InvocationHandler h 这个参数的意思就是咱们产生了制定的代理对象,需要这个对象干些什么事情呢
查询API发现InvocationHandler是个接口,匿名内部类实现其方法
new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
这些参数的意思我也不大明白,就那么用着
method参数我感觉就相当于反射类的方法时得到的一个Method对象,然后method可以调用invoke方法来 调用被反射的类中的那个方法

这个方法会返回一个Object类型的,因为我们并不知道以后要代理的对象道理是什么类型的,所以当我们使用这个生成的代理对象时,可能
需要强转成被代理的类型

main方法
public class Demo
{
 public static void main(String[] args)
 {
  Persn per = new Person();
  PersonProxy pp = new PersonProxy(per); //包装了被代理的对象
  
  Object obj = pp.getProxyPerson(); //调用生成代理对象的方法,返回Object类型
  
  //然后将obj强转成我们需要被代理的对象的类型
  Pe pe = (Pe)obj;
 }
}
看强转的那块,只能用接口类型的变量来指向这个对象,其实很好理解,通常来说咱们是要代理Person类的对象,但Person必须要实现一个接口
归根到底还是到接口那里了,开始我也很不理解,到最后想出了这个借口,勉强把自己糊弄过去了, 恩恩,很强大
总之,记住就好


三、内省
1.Introspector
Java 语言对 Bean 类属性的一种缺省处理方法
对于一个JavaBean文件,如何访问里面的属性,通常情况下我们是去反射setter和getter方法,然后method.invoke来set或者get
面对这种情况,Java提供了一套API可以专门访问Bean类的属性,其实这套API的底层也是采用了反射的技术

 

① 通过类 Introspector 来获取某个对象的 BeanInfo 信息
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);

② 然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor)
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
这个数组中存放的都是Bean属性

③ 通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法, 然后通过反射机制来调用这些方法
这里是需要判断的,咱们可以先打印出所有属性的名字,然后想修改哪个属性在来判断,比如:
for(PropertyDescriptor descriptor : descriptors)
{
 System.out.println(descriptor.getName());  //打印出所有的属性
  System.out.println(descriptor.getReadMethod().getName());  //打印出所有的get方法的名字,也包括getClass()
 //注意:这里貌似不能全部打印出set方法的名字,会抛NullPointException异常,Why?
 
 if ("name".equals(descriptor.getName()))  //判断属性的名字
  descriptor.getWriteMethod().invoke(per, "Tom");  //设置属性的内容,参数分别为被修改的对象,和修改的内容
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics