反射和javaweb注解开发

反射
web 框架 底层 全都都是使用反射来做。

反射的概念: 把字节文件  映射到 内存中 成为 class文件对象,  根据class文件对象 创建 类的对象 使用类中的各种内容(成员变量 构造方法 成员方法)
	class文件  用 Class 对象表示
	class文件中 成员变量  Field 对象表示
	class文件中 构造方法  Constructor 对象表示
	class文件中 成员方法  Method 对象表示。

	// idea
     /*
     自动提示 你对象里面具备什么方法。
     底层怎么知道 这个对象中具备什么方法啊?
     获取 对象 所属的 class文件对象。
     根据class文件对象 拿到所有的method对象。
     获取每个method对象的名字
     展示出来。
      */

获取class文件对象:
	反射研究生的是 通过class文件对象 创建 类的对象。 所以首先你得先获取 class文件对象

	三种方式:
		1:对象.getClass();

			Student s = new Student();
			Class c = s.getClass();

			class Student {
				@Override
				public boolean equals(Object obj){
					if (obj == this){
						return true;
					}
					if (obj ==null){
						return false;
					}
					if (obj.getClass() != this.getClass()){
						return false;
					}
					Student s = (Student)obj;
					return s.name.equals(this.name)&& s.age ==this.age;
				}
			}
		
		2: 类名.class
			
			Class c = Student.class;

			class Student {
				public static synchronized void show(){
				
				}

				public static void show1(){
					synchronized(Student.class){
					
					}
				}
			}
		
		3: Class.forName("类的全路径名(带包的)");  // 框架中用的很多。
			
			Class c  = Class.forName("com.itheima.domain.Student");
利用class文件对象
	创建 类 对象

	使用 成员变量
		
		Class c  = Class.forName("com.itheima.domain.Student");
		Field[] fs = c.getFields(); // fs 包含了Student里面所有的 public的属性
		
		Field f = c.getField("age"); // f 是Student里面的age属性
		//f = 18; // 错误
		Student s = new Student();
		f.set(s, 18); // age属性 是Student的 成员变量 ,成员变量的生命周期 随着对象的创建而存在,随着对象的消失而消失。
		System.out.println(s); // Student[name=null,age = 18]

		Field[] fs1 = c.getDeclaredFields();// fs1 包含了Student里面所有的属性

		Field f1 = c.getDeclaredField("name"); // f 是Student里面的私有的name属性
		Student s1 = new Student();
		f1.setAccessible(true);
		f.set(s1, "张三"); 

	使用 构造方法

		Class c  = Class.forName("com.itheima.domain.Student");
		//c.getConstructor(可变参数)  此处之所以设计为可变参数  是因为 你的构造方法 不知道有多少参数把。
		Constructor con = c.getConstructor(); //可变参数
		Object obj = con.newInstance(); //可变参数
		Student s = (Student)obj;

	使用 成员方法
		class Student {
			private void show(int a){
				System.out.println(this.a); //this指的是 谁来调用 包含this的这个方法 this就指的是谁。
			}
		}

		Class c  = Class.forName("com.itheima.domain.Student");
		Method[] ms = c.getMethods();  //ms 包含了Student里面所有的 public的方法
		Method[] ms1 = c.getDeclaredMethods();  //ms 包含了Student里面所有的方法
		
		Method ms2 = c.getDeclaredMethod("show", int.class)
		ms2.setAccessible(true);
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		ms2.invoke(obj,20); //   20   必须要传一个 对象进去,  非静态方法 是需要对象调用的,否则里面的this不知道是谁,


案例:已知 配置文件a.properties 和 Student类, 请读取配置文件中的类名className 和方法名methodName ,通过反射 运行该方法。
	
	class Student {
		public void show(){
			System.out.println("abc");
		}
	}

	配置文件a.properties
		className=advance.day15.Student
		methodName=show
	
	代码:
		Properties pro = new Properties();
		//1.2加载配置文件,转换为一个集合
		//1.2.1获取class目录下的配置文件
		ClassLoader classLoader = ReflectTest.class.getClassLoader();
		InputStream is = classLoader.getResourceAsStream("pro.properties");
		pro.load(is);

		//2.获取配置文件中定义的数据
		String className = pro.getProperty("className");
		String methodName = pro.getProperty("methodName");


		//3.加载该类进内存
		Class cls = Class.forName(className);
		//4.创建对象
		Object obj = cls.newInstance();
		//5.获取方法对象
		Method method = cls.getMethod(methodName);
		//6.执行方法
		method.invoke(obj);

作业:模拟 idea的提示功能// 键盘录入一个 类的名字。   你立刻给我打印出 他有哪些方法 哪些成员变量
		
	Scanner sc = new Scanner(System.in);
	String s = sc.nextLine();

	Class<?> c = Class.forName(s);
	Field[] declaredFields = c.getDeclaredFields();
	for (Field declaredField : declaredFields) {
		System.out.println(declaredField);
	}
	Method[] declaredMethods = c.getDeclaredMethods();
	for (Method declaredMethod : declaredMethods) {
		System.out.println(declaredMethod);
	}

注解

框架 web  注解开发。

java的数据类型:
	基本类型:byte short int long float double boolean char
	引用类型:
		数组 []
		类 class
			lambda   jdk1.8的时候出现的
			枚举 enum  jdk1.5的时候出现的
		接口 interface
			注解 @interface jdk1.5的时候出现的

注解的概念:
	1:也叫做 代码的元数据。
		元数据:用来修饰其他数据的 数据 叫做元数据。	
	2:和类 接口 数组 并列为 引用类型。
	3:注解往往写在 类 接口 方法 成员变量的前面。
	
注解功能来分:
	文档注释的注解:对生成 程序的说明文档 的时候 起作用。
		@author  Joseph D. Darcy 作者
		@since 1.0 版本
		@param 参数说明
		@return 返回值说明
		@see  方法中涉及到的其他类的内容

	编译期的注解: 编译期起作用
		@Override		重写
		@Deprecated   过时
		@FunctionalInterface  函数式接口
		@SuppressWarnings("all")  压制警告
	
	运行时注解: 运行时期起作用
		框架中给我们提供:
		jdk给我们也提供了:
		自定义注解。


自定义注解:
	格式:
		元注解
		public @interface 名字{
			属性列表;
		}

		元注解: 
			元数据:就是修饰数据的数据
			元注解:就是修饰注解的注解。
	本质:
		public interface 名字 extends java.lang.annotation.Annotation {
			public abstract void show();
		}

	属性:
		属性类型:
			基本类型
			String类型
			枚举类型
			注解类型
			以上类型数组
			enum En{
				e1,e2,e3;
			}
			public @interface MyAnno{
				int a();
				String str();
				En en();

			}
					
			public @interface MyAnno1{
				int a();
				String str();
				En en();
				MyAnno ma();
				int[] as();
				//Student sts();  //编译报错
				// 为什么 属性类型里面 是不能有 类 和接口啊。
				// 我们的定义的注解 以后加在哪上面?  类 或者 接口上面。  那么你在 注解里面有定义 类属性和接口  就会 递归下去了。
			}
			@MyAnno1(a=10,str="abc",en=En.e2,ma=@MyAnno(a=20,str="bcd",en=En.e1),as={10,20})
			class Student{
				
			}
		
		注意事项:
			案例1:
				public @interface MyAnno{
					int[] as();
				}
				@MyAnno(as=10)
				class Student{
					
				}				
			案例2:
				public @interface MyAnno{
					int[] as();
				}
				@MyAnno()  //编译报错  因为 有as属性 你没赋值就比编译报错
				class Student{
					
				}
			案例3:
				public @interface MyAnno{
					int[] as() default {10,20};
				}
				@MyAnno()  //编译正确
				class Student{
					
				}
			案例4:
				public @interface MyAnno{
					int[] as();
				}
				@MyAnno({10,20})  //编译报错  因为 没有 as =
				class Student{
					
				}
			案例5:
				public @interface MyAnno{
					int[] value();
				}
				@MyAnno({10,20})  //编译正确  
				class Student{
					
				}
			案例6:
				public @interface MyAnno{
					int[] value();
					String str();
				}
				@MyAnno({10,20},str="abc")  //编译报错  因为 没有 value =  value不是单独的
				class Student{
					
				}
	
	元注解:用于描述注解的注解
		@Target: 描述注解能够作用的位置
			ElementType取值:
				TYPE:可以作用于类上
				METHOD:可以作用于方法上
				FIELD:可以作用于成员变量上
		@Retention: 描述注解被保留的阶段
			@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
		@Documented: 描述注解是否被抽取到api文档中
		@Inherited: 描述注解是否被子类继承
	
	自定义注解的使用:
		(框架底层)反射+配置文件 案例:已知 配置文件a.properties 和 Student类, 请读取配置文件中的类名className 和方法名methodName ,通过反射 运行该方法。
			已知:
				class Student {
					public void show(){
						System.out.println("abc");
					}
				}

				配置文件a.properties
					className=advance.day15.Student
					methodName=show
			
			程序:
				Properties p = new Properties();
				p.load("a.properties");
				p.getProperty("className");
				p.getProperty("methodName");
				// 反射创建Student对象 反射执行show方法
		
		(框架底层)反射+注解
			已知:
				public @interface MyAnno{
					String className();
					String methodName();
				}
				
				class Student {
					public void show(){
						System.out.println("abc");
					}
				}

				@Pro(className = "cn.itcast.annotation.Demo1",methodName = "show")

			程序:
				/*
				 反射获取注解
				 获取 className  methodName

				 反射 创建Student对象 反射执行 show方法。
				*/
				@Pro(className = "cn.itcast.annotation.Demo1",methodName = "show")
				public class ReflectTest {
					public static void main(String[] args) throws Exception {
						//1.解析注解
						//1.1获取该类的字节码文件对象
						Class<ReflectTest> reflectTestClass = ReflectTest.class;
						//2.获取上边的注解对象
						//其实就是在内存中生成了一个该注解接口的子类实现对象
						/*

							public class ProImpl implements Pro{
								public String className(){
									return "cn.itcast.annotation.Demo1";
								}
								public String methodName(){
									return "show";
								}

							}
						*/
						Pro an = reflectTestClass.getAnnotation(Pro.class);
						//3.调用注解对象中定义的抽象方法,获取返回值
						String className = an.className();
						String methodName = an.methodName();
						System.out.println(className);
						System.out.println(methodName);

						//3.加载该类进内存
						Class cls = Class.forName(className);
						//4.创建对象
						Object obj = cls.newInstance();
						//5.获取方法对象
						Method method = cls.getMethod(methodName);
						//6.执行方法
						method.invoke(obj);
					}
				}
上一篇:从零开始,徒手撸一个简单的 RPC 框架,轻松搞定!


下一篇:通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况