Java基础加强之框架的概念及反射技术的开发框架的原理

学习概述:Java 内省和Java Bean.java 注解,以及框架的部分知识

学习目标:理解内省的概念,学会使用内省来操作Java Bean,对于Java EE框架大脑里有一个大体的概念。学会使用Beanutils,以及对注解的反射应用,注解往往是自己以前比较忽略的部分,认为注解只是程序的注释,除了增强程序的可读性之外没什么作用,这种想法是错误的,对于这部分知识,一定要深入掌握,改变这种错误观点。

反射的最大价值:实现框架

1.框架与框架要解决的核心问题

     框架与工具类的区别:工具类被用户类调用,而框架则是调用用户提供的类。

    框架要解决的核心问题:因为在写程序时才知道要被调用的类名,所以在程序中无法采用new关键字创建实例对象,而要使用反射的方式来做。

    模拟通过配置文件得到ArrayList实例对象(反射应用)

    

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.util.Collection;
import java.util.Properties;

public class TestReflect {
	public static void main(String[] args){
		
		try{
			InputStream ips = new FileInputStream("test.properties");
			Properties prop = new Properties();
			prop.load(ips);
			String classname = (String) prop.get("classname");
			try{
				Class<?> coll = Class.forName(classname);
				@SuppressWarnings("unchecked")
				Collection<String> a =(Collection<String>) coll.newInstance();
				a.add("abc");
				a.add("abc");
				System.out.print(a.size());
			}
			catch(ClassNotFoundException e){
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		catch(IOException e){
			e.printStackTrace();
		}
		}
	}

小细节:配置文件应该放在什么地方?在实际项目中,没有用相对路径的,一种解决方式是采用绝对路径。但是绝对路径不能用硬编码,而是计算出来。可以采用类加载器,示例如下:class.getClassLoader().getResourceStream( 文件名);

2.内省及引出JavaBean

   什么是JavaBean,Javabean是一种特殊的Java类,主要用于传递的数据信息,类中的属性均为私有的,所有的属性都有get和set方法。外部程序不能直接访问Java Bean的状态信息,这种类中的方法主要用于访问私有变量,Java Bean是对面向对象三大特征之一:封装的完美诠释。

  如果想在Java模块之间传递很多信息,可将这些新意一起封装到一个JavaBean中,特别是在Java EE开发中,Java Bean有广阔的应用,也被称为值对象(VO)。

    一个Java Bean实例:

        

public class Person{
   private String name;
   private int age;
   public Person(String name,int age){
       this.name=name;
        this.age=age;
}
   public String getName(){
      return this.name;
   }
   public void setName(String name){
     this.name=name;
}
public int getAge(){
   return this.age;
}
public void setAge(int age){
this.age=age;
}
}

内省(Introspector)是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法
<1>简单内省操作
     对Java Bean的简单内省操作比较容易。Java SAPI已经提供了相关操作类:PropertyDescriptor,示例代码如下:
public class IntroSpectorDemo {

	/**
	 * @param args
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws IntrospectionException 
	 * @throws InvocationTargetException 
	 * @throws NoSuchMethodException 
	 */
	public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException {
		// TODO Auto-generated method stub
		Person p = new Person(1,"james");
		
		PropertyDescriptor pd =  new PropertyDescriptor("name", Person.class);
		Method mR = pd.getReadMethod();
		Object object = mR.invoke(p);
		System.out.println(object);
	    //得到bean的set方法改变bean的值
		Method mW = pd.getWriteMethod();
		mW.invoke(p,"kobe");
}
}

     <2>复杂内省操作
 实际上复杂内省操作也不算复杂,因为api也提供了相关类:Introspector,甚至我们还有更为专业的工具类BeanUtils操作bean。示例代码如下
import org.apache.commons.beanutils.BeanUtils;

/*
 * javabean的内省操作,如果不适用内省,那么我们只能用反射的方法得到bean的get和set方法,有了内省之后操作方便了很多。
 */

public class IntroSpectorDemo {

	/**
	 * @param args
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 * @throws IntrospectionException 
	 * @throws InvocationTargetException 
	 * @throws NoSuchMethodException 
	 */
	public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException {
		// TODO Auto-generated method stub
		Person p = new Person(1,"james");
		
		PropertyDescriptor pd =  new PropertyDescriptor("name", Person.class);
		Method mR = pd.getReadMethod();
		Object object = mR.invoke(p);
		System.out.println(object);
	    //得到bean的set方法改变bean的值
		Method mW = pd.getWriteMethod();
		mW.invoke(p,"kobe");
		System.out.println(p.getName());
		//使用IntroSpector进行复杂内省操作
		BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		for(PropertyDescriptor propertyDescriptor:pds){
			System.out.println(propertyDescriptor.getReadMethod().getName());
		}
		//使用更加专业省事的工具类操作Java Bean
		//System.out.println(BeanUtils.getProperty(p, "name"));
		BeanUtils.setProperty(p, "age", "15");
		Field f = Person.class.getDeclaredField("age");
		f.setAccessible(true);
		System.out.println(f.getInt(p));
		
		

	}

}
          注意:使用BeanUtils是不要忘记导入两个jar包:commons-beanutils.jar和commons-logging.jar.
   

3.注解的定义与反射调用
从JDK1.5开始,Java增加了对元数据的支持,也就是Annotation,其实注解是代码里的特殊标记,这些标记既可以在编译,类加载和运行时被读取,并执行相应的处理。通过使用注解,程序员在不改变原有逻辑的情况下,在原文件嵌入一些有用的信息。注解能被用来微程序元素设置元数据。值得提出的是:注解不影响代码的执行,无论删除或是添加注解,代码会如一的执行。
<1> Java提供了三类基本注解,如下:@Override,@Deprected,@SuppressWarnings。其中@Override表示限定重写父类,用来指定方法重载,他可以强制一个子类必须覆盖父类的方法。@Deprecated用来表示某个程序元素已过时,当其他程序使用已过时的类或者方法时,编译器会给出警告。@SuppressWarnings用来取消指定的编译器警告。
  <2>自定义Annotation
       定义新的注解类型使用@interface关节子,定义一个Annotation类型和定义一个接口非常像,如下所示:
public @interface Test{
}

          定义了这个Annotation之后,就可以在程序的任何地方用来使用该Annotation,使用Annotation时的语法非常类似于public,final这样的修饰符。通常可用于修饰程序中的类,方法,变量,接口等定义,通常我我们应当把Annotation放在所有修饰符前面 。
一个自定义注解的实例:
package com.lee.Annotation;

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)
public @interface MyAnnotation {

}
package com.lee.Annotation;
//编写一个应用自定义注解的测试类

public class AnnotationDemo2 {
	@MyAnnotation
	public static  void method1(){}
	@MyAnnotation
	public  static void method2(){}
	public static void method3(){}

}

package com.lee.Annotation;

import java.lang.reflect.Method;

//反射得到方法的注释,以有没有方法注释作为标准判定该方法是否应该执行
public class TestProcessor {
	public static void process(String clazz) throws Exception, ClassNotFoundException{
		int passed=0;
		int failed=0;
		//反射得到类的所有方法
		for(Method m:Class.forName(clazz).getMethods()){
			if(m.isAnnotationPresent(MyAnnotation.class)){
				//测试的方法都是static修饰的
				m.invoke(null);
				passed++;
			}
			else{
				failed++;
			}
		}
		int methodSum = passed+failed;
		System.out.println("共运行了: "+methodSum+ "方法");
		System.out.println("成功了:" +passed+"个方法");
	}
	public static void main(String[] args) throws ClassNotFoundException, Exception{
		process("com.lee.Annotation.AnnotationDemo2");
	}

}


   <3>带成员变量的Annotation,成员变量在Annotation中以无参数方法的形式用来声明。其方法名和返回值定义了该成员的名字和类型,如下所示:
      
public @interface Test{
String name();
int age();
}
  特别要注意的是:一旦使用了带有成员变量的Annotation时,需要为该Annotation中的变量赋值,如下所示
public class Test{
@Test(name="abc",age=6)
  public void info(){
   ... ...
}
}


4. 提取注解信息
Java使用Annotation接口来代表程序前面的注释,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下实现类:Class: 类定义。Constructor:构造器定义。Field:类成员变量定义。Method :类方法定义。Package:类的包定义。
该包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect包所提供的反射API扩充了读取运行的注解的能力。当一个注解类型被定义为运行时的Annotation后,该注解才是运行可见的,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement接口是所有程序元素的父接口,所以程序通过反射获取某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下三个方法来访问Annnotation信息:
<1>getAnnotation(Class<T> annotationClass):翻译该程序元素上存在的注解,注定类型的注解。如果该类型不存在,则返回null
<2>Annotation[ ] getAnnotations():该返回程序元素上所存在的注释
<3>boolean isAnnotationPresent(Class<?extends Annotation>AnnotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则则返回false
示例代码1.一个注解的最简单应用
package com.lee.Annotation;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
	

}

package com.lee.Annotation;

import java.lang.annotation.Annotation;

/**
 * 
 * @李亮亮
 * 自定义注解的应用
 */
@AnnotationDemo
public class AnnotationTest {

	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
		//先判断注解在不在
		if(AnnotationTest.class.isAnnotationPresent(AnnotationDemo.class)){
		      Annotation annotation = AnnotationTest.class.getAnnotation(AnnotationDemo.class);
		System.out.println(annotation);
		}
		
	}

}


5.JDK的元Annotation
 JDK除了在java.lang下提供了3个基本Annotation之外,还在java。lang。annotation包下提供了四个Meta Annotation,这四个Annotation都是用于修饰其他Annotation定义
<1> @Retention  只能用于修饰一个Annotation定义,用于指定该Annotation可以被保留多长时间,其中使用该注解时必须为value成员变量赋值,value成员变量有三个:
        【1】RetentionPolicy.CLASS:编译器会将注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值。
        【2】RetentionPolicy.RUNTIME:编译器会将注释记录在class文件中。当运行Java程序时,JVM会保留注释,程序可以通过反射获取该注释。
        【3】RetentionPolicy.Source:编译器直接丢弃这种策略的注释
 <2>@Documented用于该元Annotation修饰的Annotation类将被javadoc工具提取成文档。
 <3> @Inherited 指定被他修饰的将具有继承性:如果某个Annotation使用了@Inherited修饰,则其子类将自动具有父类注释
<4>  @Target  用于被指定被修饰的Annotation能用于修饰那些程序元素。
  【1】ElementType.ANNOTATION_TYPE:表示定义的Annotation只能修饰Annotation
  【2】ElementType.CONSTRUCTOR:表示定义的Annotation只能修饰构造方法
  【3】ElementType.FIELD:表示定义的Annotation只能修饰属性
  【4】ElementType.LOCAL_VARIABLE:表示定义的Annotation只能修饰局部变量
  【5】ElementType.METHOD:表示定义的Annotation只能修饰方法
  【6】ElementType.PACKAGE:表示定义的Annotation只能修饰包定义
  【7】ElementType.PARAMETER:表示定义的Annotation只能修饰参数
【1】ElementType.TYPE:表示定义的Annotation只能修饰类,接口,枚举类
学习总结:1.新知识:学会了使用专门的工具类(beanUtils)操作Java Bean
                    2.对于以前直接比较忽视的注释部分有了全新的认识,认识到注释的作用极其强大,并深入认识了元数据这一概念

 

    



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值