搞懂Java代理和反射一篇就够了

  • Post author:
  • Post category:java

目录

————————————反射—————————————

什么是反射?

什么是java Bean

———————————-反射作业—————————————

java Bean

反射代码

属性文件(config.properties)

读取属性文件

————————————代理—————————————

静态代理:

动态代理:


 java Bean要求

————————————反射—————————————

什么是反射?

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到类对象之后,再通过类对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁

                反射:能够分析类信息的能力叫做反射

                那什么是类的信息?

简单的来说比如 属性、方法、构造器等等。。。

举例

Class class1 = Class.forName("reflect.Person");
System.out.println(class1);
Field[] fields = class1.getFields();
   for (Field field : fields) {
            System.out.println(field);
      }

那么一个新的问题就来了,我们明明可以通过new 一个对象来获取类信息,那么我们为什么要使用反射呢?

我们在这里说一个实际的列子,比如说我们使用的servlet,在servlet当中我们用过jdbc操作,在这个类当中我们引入了一个mysql.jar包,那么这个jar包是怎么起作用的呢,请大家注意,我们的servlet类当中没有main方法,那我们的servlet类是如何启动的呢?如何加载到我们的JVM当中呢?

答案就是使用了反射,大家看一个东西

从上面可以看到,正是因为有了反射,我们才能将他们加载如JVM当中。

那么反射是如何工作的呢?

要想理解反射是如何工作的我们先来看类的生命周期

 

好处:

        1.在程序的运行过程中,来操作这些对象

String str = "123";
str.

我们对 . 出很多的方法,这其实就是内部使用的是反射机制,你定义了一个字符串,那么他会把字符串的字节码文件加载进内存,在内存当中有一个class类对象,class对象已将所有的方法都抽取出来封装在method[]数组当中,所以我们能够 . 出这么多的程序。

        2.可以解耦,提高程序的可扩展性

*获取class对象的方式

        1.Class.forName(“全类名”):将字节码文件加载进内存,返回class对象

多用于配置文件,将类名定义在配置文件当中,读取文件,加载类

         2.类名.class: 通过类名的class获取

多用于参数的传递

                3.对象.getClass() :getClass()方法在object类当中定义着

多用于对象获取字节码的方式

首先定义一个person类,内部定义好成员变量,构造方法和成员方法

public class Person {
	
	private String nameString;
	private int age;
	
	public Person() {
		
	}
	
	public Person(String nameString, int age) {
		this.nameString = nameString;
		this.age = age;
	}

	public String getNameString() {
		return nameString;
	}

	public void setNameString(String nameString) {
		this.nameString = nameString;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [nameString=" + nameString + ", age=" + age + "]";
	}

}

定义测试类

public class refulectDemo1 {
	
	/**
	 *      1.Class.forName("全类名"):将字节码文件加载进内存,返回class对象
		2.类名.class:通过类名的class获取
		3.对象.getClass(): getClass()方法在object类当中定义着
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws Exception {
		// 1.Class.forName("全类名"):
		Class class1 = Class.forName("reflect.Person");
		System.out.println(class1);
		
		//2.类名.class:通过类名的class获取
		Class class2 = Person.class;
		System.out.println(class2);
		
		//3.对象.getClass()
		Person person = new Person();
		Class class3 = person.getClass();
		System.out.println(class3);
		
	}

}

用 == 进行比较

System.out.println(class1 == class2);
System.out.println(class2 == class3);

输出为:

true
true

结论:

同一个类加载器加载的文件(*.class)在一次程序运行过程中,只会被加载一次,不论你通过那种方式获取的class对象都是同一个

class对象常用功能

/*通过反射获取Person类对象*/
Class aClass = Class.forName("com.qcby.reflect.Person");

Field[] fields = aClass.getDeclaredFields();
System.out.println("获取Person的所有属性对象");
for (Field field : fields) {
    System.out.println(field);
}
System.out.println("获取Person的age属性对象");
Field age = aClass.getDeclaredField("age");
System.out.println(age);
System.out.println("-------------------------------------\n");


Constructor[] constructors = aClass.getDeclaredConstructors();
System.out.println("获取Person的所有构造方法");
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}
System.out.println("获取Person的无参构造方法");
Constructor constructor = aClass.getDeclaredConstructor();
System.out.println(constructor);
System.out.println("获取Person的有参构造方法");
Constructor constructor1 = aClass.getDeclaredConstructor(String.class,Integer.class);
System.out.println(constructor1);
System.out.println("-------------------------------------\n");

Method[] declaredMethods = aClass.getDeclaredMethods();
System.out.println("获取Person的所有普通方法");
for (Method declaredMethod : declaredMethods) {
    System.out.println(declaredMethod);
}
System.out.println("获取Person的getAge普通方法");
Method getAge = aClass.getDeclaredMethod("getAge");
System.out.println(getAge);
System.out.println("获取Person的getAge普通方法");
Method setAge = aClass.getDeclaredMethod("setAge",Integer.class);
System.out.println(setAge);
System.out.println("-------------------------------------\n");

System.out.println("获取Person的全类名");
System.out.println(aClass.getName());
System.out.println("获取Person的类名");
System.out.println(aClass.getSimpleName());

Field成员变量       

        操作

                1.设置值

                        void set(Object obj,Object value)

                2.获取值

                        get(Object obj)

 public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
             1. 获取成员变量们
                 * Field[] getFields()
                 * Field getField(String name)

                 * Field[] getDeclaredFields()
                 * Field getDeclaredField(String name)

         */
        //1.Field[] getFields()获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("------------");
        //2.Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);

        System.out.println("===================");

        //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);

    }

忽略安全访问修饰符的安全检查 setAccessible(true) //暴力反射 —》private

Constructor访问构造方法  

        *创建对象

                *T newInstance(Object… initarges)

                *如果使用空参数构造方法,此操作可以简化:class对象的newInstance方法

public static void main(String[] args) throws Exception {
		//0.获取Person的class对象
		Class personClass = Person.class;

		//因为构造器的方法名称是相同的,不同的是参数列表,所以我们可以根据不同的参数列表来找到构造器
		Constructor constructor = personClass.getConstructor(String.class,int.class);
		System.out.println(constructor);
		//创建对象
		Object perObject = constructor.newInstance("张三",20);
		System.out.println(perObject);
		
		System.out.println("-------------------------------");
	
		Constructor constructor1 = personClass.getConstructor();
		System.out.println(constructor1);
		//创建对象
		Object perObject1 = constructor1.newInstance();
		System.out.println(perObject1);
		
		System.out.println("-------------------------------");
		Object o = personClass.newInstance();
		System.out.println(o);
	}

Method:方法对象

        *执行方法

                *object invoke(Object object,Object… ages)

        *获取方法名称

                *String getName:获取方法名

public static void main(String[] args) throws Exception {
		//0.获取Person的class对象
		Class personClass = Person.class;
		
		//获取指定名称的方法,方法无参
		Method eat_method = personClass.getMethod("eat");
		//执行方法
		Person person =new Person();
		eat_method.invoke(person);
		
		//获取指定名称的方法,方法有参
		Method eat_method1 = personClass.getMethod("eat",String.class);
		//执行方法
		eat_method1.invoke(person,"饭");
		
		//获取所有public修饰的方法
		Method[] methods = personClass.getMethods();
		for (Method method : methods) {
			System.out.println(method);
			//获取方法的名称
			String name = method.getName();
			System.out.println(name);
		}
	}

什么是java Bean

//java bean是java类的一种特殊格式,java bean要求

1.该类下所有的成员变量都是私有的

2..每一个私有的变量都有一对方法对他进行操作,分别是get()和set()方法

3.在set方法当中有一个给成员变量赋值的方法

———————————-反射作业—————————————

作业:给定一个java bean,获取该类的构造方法来构造对象,获取该类的名称和该类中每一个变量的名称和方法的名称,并执行相关方法。给每一个变量赋值,并获取值,执行每一个构造方法,执行相应的方法

java Bean

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

反射代码

public class CommonReflect {

	//获取该类的名称
	public static void getClassName(Object object) {
		String className = object.getClass().getSimpleName();
		System.out.println("类的名字是:"+className);
	}
	
	//获取类的变量名称
	public static void getField(Object object) {
		Field[] fields = object.getClass().getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			System.out.println("成员变量的名称为:"+fields[i].getName());
		}
	}
	
	//获取类的成员方法
	public static void getMethod(Object object) {
		Method[] methods = object.getClass().getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			System.out.println("成员方法的名称:"+methods[i].getName());
		}
	}
	
	//调用该类的成员方法,以get为例
	public static void getAction(Object object) throws Exception {
		//获取到有多少get方法
		Field[] fields = object.getClass().getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			String fieldName = fields[i].getName();
			String fistfont = fieldName.substring(0,1).toUpperCase();
			String methodname = "get"+fistfont+fieldName.substring(1);
			Method method = object.getClass().getMethod(methodname);
			System.out.println("get的输出结果:"+method.invoke(object));
		}

	}

	
	public static void main(String[] args) throws Exception {
		Object studentObject = Student.class.getConstructor(String.class,int.class,String.class).newInstance("张三",20,"保定");
		getClassName(studentObject);
		getField(studentObject);
		getMethod(studentObject);
		getAction(studentObject);
		
	}
}

属性文件(config.properties)

reflect.className=com.qcby.reflect.Person
reflect.MethodName=getAge

读取属性文件

//1、加载配置文件
  //1.1、创建properties对象
 Properties properties = new Properties();
  //1.2、加载配置文件
  ClassLoader classLoader = Person.class.getClassLoader();
  InputStream input = classLoader.getResourceAsStream("com/qcby/reflect/config.properties");
  properties.load(input);

  //2、获取配置文件当中定义的数据源
  String className = properties.getProperty("reflect.className");
  String methodName = properties.getProperty("reflect.MethodName");
    
 //反射代码
  Class clz = Class.forName(className);
  Object object = clz.getDeclaredConstructor(String.class,Integer.class,Integer.class).newInstance("gs",23,5);
  Method method = clz.getDeclaredMethod(methodName);
  System.out.println(method.invoke(object));

————————————代理—————————————

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

 

        java中的代理模式:

        *定义:给目标对象提供一个代理对象,并且由代理对象控制对目标对象的引用

        *目的:①:通过代理对象的方式间接的访问目标对象,防止直接访问目标对象给系统带来不  必要的复杂性

                        ②:通过代理业务对原有业务进行增强

                        java当中有三种方式来创建代理对象:静态代理,基于jdk(接口)的动态代理,基于CGLLIB(父类)的动态代理。

相关概念:

        目标类:原对象,我们需要代理对象控制他的访问,拓展其功能。

        代理类:代理模式产生的对象,是原对象“替身”,已经在原有基础上修改逻辑


静态代理:

        编写代理类:实现目标的接口或者直接继承目标类,完成逻辑的修改

接口实现方式:

关于接口的方式,我们在实现接口的时候,目标类和代理类都必须实现目标接口当中所实现的方法,从某种意义上代理类就可以帮我们实现目标类当中的方法,并且代理类还可以有自己的扩展方法。

举例子:1

最开始我们想买东西就必须代购,因为只有代购员才知道真实的工厂在哪里

代码实现:

首先定义接口——相当于工厂地址和功能

public interface ByClothes {
	void clothes(String size);
}

定义目标对象—制造衣服的工厂

public class ClothesFactory implements ByClothes {
	@Override
	public void clothes(String size) {
		System.out.println("已经为您制作好了一整套size为"+size+"的衣服。。。。。。。。");
	}
}

制造代理对象——-代购

public class proxy implements ByClothes {

	
	//被包含的真是对象
	public ClothesFactory factory;
	
	public proxy(ClothesFactory factory) {
		// TODO Auto-generated constructor stub
		this.factory = factory;
	}
	
	@Override
	public void clothes(String size) {
		FrontService();
		factory.clothes(size);
		endService();
		
	}
	
	//前置服务
	public void FrontService() {
		System.out.println("根据您的需求进行市场调研");
	}

	//前置服务
	public void endService() {
		System.out.println("为您提供一条龙的包办服务");
	}
}


制造测试类------买衣服的人
public class Test {
	public static void main(String[] args) {
		ClothesFactory clothesFactory = new ClothesFactory();
		proxy proxy = new proxy(clothesFactory);
		proxy.clothes("XXl");
	}
}

真实静态代理

细节:我们的目标对象必须实现我们的接口,同时代理对象也必须实现这一接口

静态代理存在哪些问题?

        违反了开闭原则:

程序对访问开放,对修改关闭,换句话来说,当需求发生变化时,我们可以增加新模块来解决新需求,而不是通过改变原来的代码来解决我们的新需求

动态代理:

举例子:2

当发现这是一个商机的时候,聚拢身边的朋友成立公司

公司的优势:可以代购很多种商品,不是定性的代购

java实现:

1.新建接口

public interface ByShoot {
	void byShoot(String size);
}

2.新建工厂

public class ShootFactory implements ByShoot{

	@Override
	public void byShoot(String size) {
		System.out.println("已经为您生产出了尺码为"+size+"的鞋子");
	}

}

3.新建海外代购公司

public class LisiFactory implements InvocationHandler {

	// 被代理的对象
	private Object factory ;

	public Object getFactory() {
		return factory;
	}

	public void setFactory(Object factory) {
		this.factory = factory;
	}

	//ssm: Spring SpringMVC mybitys
	//Spring:AOP IOC +....
	//AOP:代理    (面向切面的编程)
	// 通过代理对象对方法进行增强
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		FrontService();
		Object retObject = method.invoke(factory, args);  //这里用了反射的思想,因为我们不知道调用的是那个工厂的那个方法
		endService();
		return null;
	}

	// 前置服务
	public void FrontService() {
		System.out.println("根据您的需求进行市场调研");
	}

	// 后置服务
	public void endService() {
		System.out.println("为您提供一条龙的包办服务");
	}
	
	//调度员工
	/**
	* 1 新建一名员工
	 * 2.告诉员工工厂地址
	 * this:绑定
	 * @return
	 */
	public Object getProxyInstance() {
		// TODO Auto-generated method stub
		return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);
	}
	
	
}

4.新建测试类—买东西的人

public class Test {
	public static void main(String[] args) {
		ClothesFactory clothesFactory = new ClothesFactory();
		ShootFactory shootFactory = new ShootFactory();
		LisiFactory lisiFactory = new LisiFactory();
		lisiFactory.setFactory(shootFactory);
		ByShoot yuangong1 = (ByShoot) lisiFactory.getProxyInstance();
		yuangong1.byShoot("42");
		lisiFactory.setFactory(clothesFactory);
		ByClothes yuangong  = (ByClothes) lisiFactory.getProxyInstance();
		yuangong.clothes("XXL");
	}
}

 


版权声明:本文为weixin_51309915原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。