反射与内省

本文详细介绍了Java中的反射和内省技术,包括Class类的使用、动态代理的实现、JavaBean的概念以及单例模式的优化。通过反射,我们可以获取类的信息并动态调用属性和方法。内省则是Java语言对Bean类属性、事件的一种处理方式,允许我们通过标准API访问getter和setter。同时,文章还提及了AOP的简单实现和单例模式的多种优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

github 示例代码:参考Demo

1、什么是反射

类信息     --->    对象
 对象      --->     类信息

2、Class类

Class类是一切的反射根源。

Class类表示什么?

很多的人——可以定义一个Person类(有年龄,性别,姓名等)
很多的车——可以定义一个Car类(有发动机,颜色,车轮等)
很多的类——Class类(类名,构造方法,属性,方法)

得到Class类的对象有三种方式:
第一种形式:Object类中的getClass()方法
第二种形式:类.class
第三种形式:通过Class类的forName方法

@Setter
@Getter
@ToString
public class Car {
	private String name;
	private String type;
	private int volum;
	public String color;
	
	public Car(){
		
	}
	
	public Car(String name, String type, int volum, String color){
		this.name = name;
		this.type = type;
		this.volum = volum;
		this.color = color;
	}
	
	private void changeColor() {
		setColor("blue");
		System.out.println("color has changed to blue!");
	}
		
	protected void toBlue() {
		changeColor();
	}
}


@Test
	public void test1(){
		Car car = new Car();
		Class<Car> class1 = (Class<Car>) car.getClass();
		@SuppressWarnings("rawtypes")
		Class class2 = Car.class;
		Class<Car> class3 = null;
		try {
			class3 = (Class<Car>) Class.forName("cn.saul.reflection.Car");
		} catch (ClassNotFoundException e) {
			System.out.println("class 对象不存在");
		}
		System.out.println(class1 + "\n" + class2 + "\n" + class3);
	}

3、通过Class类取得类信息

使用Class类进行对象的实例化操作
调用无参构造进行实例化
public T newInstance() throws InstantiationException,IllegalAccessException
调用有参构造进行实例化
public Constructor<?>[] getConstructors() throws SecurityException

取得类所在的包
public Package getPackage() //得到一个类所在的包
public String getName() //得到名字

取得一个类中的全部方法
public Method[] getMethods()
public int getModifiers() //Modifier.toString(mod); // 还原修饰符
public Class<?> getReturnType()
public Class<?>[] getParameterTypes()
public Class<?>[] getExceptionTypes()
public static String toString(int mod)

	//获取所有构造方法
	@Test
	public void test3() {
		Class<Car> carClass = Car.class;
		Constructor<?>[] constructors = carClass.getConstructors();
		for (int i = 0; i < constructors.length; i++) {
			System.out.println(constructors[i].getName());
			System.out.println(constructors[i].getParameterCount());
		}
		try {
			//获取一个指定的构造方法
			Constructor<Car> constructor = carClass.getConstructor(String.class, String.class, int.class, String.class);
			try {
				//调用带参数的构造器来实例化对象
				Car car = constructor.newInstance("audi","xl",2,"red");
				System.out.println(car);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}

取得一个类中的全部属性
public Field[] getFields()
public Field[] getDeclaredFields()
public Class<?> getType()
public int getModifiers()
public String getName()

/**
	 * 获取类属性
	 */
	@SuppressWarnings("unchecked")
	@Test
	public void test4() {
		Class<Car> carClass = null;
		try {
			carClass = (Class<Car>) Class.forName("cn.saul.reflection.Car");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		//获取非私有属性
		Field[] fields = carClass.getFields();
		System.out.println(fields.length);
		
		//获取所有属性(包含私有属性)
		Field[] declaredFields = carClass.getDeclaredFields();
		System.out.println(declaredFields.length);
		
		for (int i = 0; i < declaredFields.length; i++) {
			int modifiers = declaredFields[i].getModifiers();
			System.out.println(Modifier.toString(modifiers) + " " + declaredFields[i].getType() + " " + declaredFields[i].getName());
		}
		
	}

4、通过Class类调用属性或方法

调用类中的方法
调用类中的方法,传入实例化对象,以及具体的参数内容
public Object invoke(Object obj,Object… args)
直接调用属性
取得属性
public Object get(Object obj)
//设置属性,等同于使用“=”完成操作
public void set(Object obj,Object value)
//让属性对外部可见
public void setAccessible(boolean flag)

@Test
	public void test5() {
		Class<Car> carClass = Car.class;
		Car car = null;
		try {
			car = carClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		car.setName("benz");
		car.setColor("red");
		car.setType("xl");
		car.setVolum(3);
		//获取类的包名
		Package package1 = carClass.getPackage();
		System.out.println(package1.getName());
		
		//获取公共的方法,包括继承的公有方法
		Method[] methods = carClass.getMethods();
		for(Method method : methods) {
			System.out.println(method);
			if (method.getName().equals("toString")) {
				try {
					String str = (String) method.invoke(car);
					System.out.println(str);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
		
		System.out.println("------------------------------------------");
		Method[] declaredMethods = carClass.getDeclaredMethods();
		for (int i = 0; i < declaredMethods.length; i++) {
			System.out.println(declaredMethods[i]);
			if (declaredMethods[i].getName().equals("changeColor")) {
				//设置私有方法可以被访问(去除访问修饰符的检查)
				declaredMethods[i].setAccessible(true);
				try {
					declaredMethods[i].invoke(car);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
	} 

5、动态代理

所谓动态代理,即通过代理类:Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联。

java动态代理主要是使用java.lang.reflect包中的两个类。
InvocationHandler类

public Object invoke(Object obj,Method method,Object[] obs)
其中第一个参数 obj 指的是代理类,method是被代理的方法,obs是指被代理的方法的参数组。此方法由代理类来实现。

Proxy类
protected Proxy(InvocationHandler h);
static Class getProxyClass(ClassLoader loader,Class[] interfaces);
static Object newProxyInstance(ClassLoader loader,Class[]interfaces,InvocationHandler h);
动态代理其实是在运行时生成class,所以,我们必须提供一组interface,然后告诉他class已经实现了这些interface,而且在生成Proxy的时候,必须给他提供一个handler,让他来接管实际的工作。

类加载器原理分析与实现

1、类的加载过程
JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤:
1 ) 装载:查找并加载类的二进制数据
2 )链接:
---- 验证:确保被加载类的正确性;
---- 准备:为类的静态变量分配内存,并将其初始化为默认值;
---- 解析:把类中的符号引用转换为直接引用;
3 )初始化:为类的静态变量赋予正确的初始值;

2、类的初始化,类什么时候才被初始化:
1)创建类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName(“com.vince.Dog”))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类

3、类的加载:
指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的Java.lang.Class对象,用来封装类在方法区类的对象。

6 JavaBean的概念

什么是 JavaBean?
Bean理解为组件意思, JavaBean就是Java组件,在广泛的理解就是一个类,对于组件来说,关键在于要具有“能够被IDE构建工具侦测其属性和事件”的能力,通常在Java中。

一个JavaBean要具备这样的命名规则
1、对于一个名称为xxx的属性,通常你要写两个方法:getXxx()和setXxx()。任何浏览这些方法的工具,都会把get或set后面的第一个字母自动转换为小写。

2、对于布尔型属性,可以使用以上get和set的方式,不过也可以把get替换成is。

3、Bean的普通方法不必遵循以上的命名规则,不过它们必须是public的。

4、对于事件,要使用Swing中处理监听器的方式。如addWindowListener,removeWindowListener

@Test
	public void test1() {
		//从客户端获取的数据
		String name = "audi";
		String type = "Xl";
		String volum = "3";
		String color = "black";
		Car car = new Car();
		
		try {
		/**
			 * BeanUtils自动完成类型转换
			 */
			BeanUtils.setProperty(car, "name", name);
			BeanUtils.setProperty(car, "type", type);
			BeanUtils.setProperty(car, "volum", volum);
			BeanUtils.setProperty(car, "color", color);
		} catch (IllegalAccessException | InvocationTargetException e) {
			e.printStackTrace();
		}
		System.out.println(car);
		
	}

7、内省基本概念

内省(Introspector)是Java 语言对 Bean 类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。
通过 getName/setName 来访问 name 属性,这就是默认的规则。

Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则,这些 API 存放于包 java.beans 中,一般的做法是通过类 Introspector 的 getBeanInfo方法 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。

8、 Introspector 相关API

1、Introspector类:
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。

  • static BeanInfo getBeanInfo(Class<?> beanClass)
    在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。

2、BeanInfo类:
该类实现此 BeanInfo 接口并提供有关其 bean 的方法、属性、事件等显式信息。

  • MethodDescriptor[] getMethodDescriptors()
    获得 beans MethodDescriptor。

  • PropertyDescriptor[] getPropertyDescriptors()
    获得 beans PropertyDescriptor。
    Properties 属性文件工具类的使用

3、PropertyDescriptor 类:
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。

  • Method getReadMethod()
    获得应该用于读取属性值的方法。
  • Method getWriteMethod()
    获得应该用于写入属性值的方法。

4、MethodDescriptor 类:
MethodDescriptor 描述了一种特殊方法,即 Java Bean 支持从其他组件对其进行外部访问。

  • Method getMethod()
    获得此 MethodDescriptor 封装的方法。

config.properties:

bean.name=cn.saul.reflection.introspector.Config
bean.id=935209
bean.username=saul
bean.password=12345
bean.url=https://github.com/ShuaiMou

entity 类:

package cn.saul.reflection.introspector;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Config {
	
	private int id;
	private String username;
	private String password;
	private String url;
	
	public Config(int iD, String username, String password, String url) {
		super();
		id = iD;
		this.username = username;
		this.password = password;
		this.url = url;
	}

	public Config() {
		super();
	}
}

public class BeanFactory {
	private static Properties propt = new Properties();
	static {
		InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("cn/saul/reflection/introspector/config.properties");
		try {
			propt.load(stream);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static Object getBean(String name) {
		Object bean = null;
		try {
			Class<?> beanClass = Class.forName(propt.getProperty(name));
			bean = beanClass.newInstance();
			
			//通过类信息获取javaBean的描述信息
			BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
			
			//通过javaBean描述信息,获取该类的所有属性描述器
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for(PropertyDescriptor descriptor : propertyDescriptors) {
				String propertyName = descriptor.getName();
				Method writeMethod = descriptor.getWriteMethod();
				if ("id".equals(propertyName)) {
					//调用属性的 set 方法
					writeMethod.invoke(bean, Integer.parseInt(propt.getProperty("bean.id")));
				}else if ("username".equals(propertyName)) {
					//调用属性的 set 方法
					writeMethod.invoke(bean, propt.getProperty("bean.username"));
				}else if ("password".equals(propertyName)) {
					//调用属性的 set 方法
					writeMethod.invoke(bean, propt.getProperty("bean.password"));
				}else if ("url".equals(propertyName)) {
					//调用属性的 set 方法
					writeMethod.invoke(bean, propt.getProperty("bean.url"));
				}
				
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IntrospectionException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return bean;
	}
}
public class TestIntrospector {
	
	@Test
	public void testGetBean() {
		Config config = (Config) BeanFactory.getBean("bean.name");
		System.out.println(config);
	}
}

补充知识:
1、AOP的概念:Aspect Oriented Programming(面向切面编程)
2、可配置 AOP框架实现

AOP使用场景
AOP用来封装横切关注点,具体可以在下面的场景中使用:

权限
缓存
错误处理
调试
记录跟踪
持久化
同步
事务
等等。。

简单实现代码如下:

通知接口:
package cn.saul.reflection.aop;

public interface Advice {
	public void beforeAdvice();
	public void afterAdvice();
}
通知实现类
package cn.saul.reflection.aop;

public class LogAdvice implements Advice {

	@Override
	public void beforeAdvice() {
		System.out.println("start time :" + System.currentTimeMillis());

	}

	@Override
	public void afterAdvice() {
		System.out.println("end time : " + System.currentTimeMillis());

	}

}
实体动作接口
package cn.saul.reflection.aop;

public interface Emanager {
	public void add(String name);
}

动作接口实现类
package cn.saul.reflection.aop;

import lombok.ToString;

@ToString
public class EmanagerImpl implements Emanager {
	private String name;

	@Override
	public void add(String name) {
		this.name = name;
		System.out.println("add name successfully!");

	}
}
代理对象工厂类
package cn.saul.reflection.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class ProxyFactoryBean implements InvocationHandler{
	private Object target;
	private Advice advice;
	
	public Object getProxy() {
		Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
		return proxyInstance;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		advice.beforeAdvice();
		Object result = method.invoke(target, args);
		advice.afterAdvice();
		return result;
	}
}

bean工厂类,代理对象的装配
package cn.saul.reflection.aop;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * AOP框架的简单实现
 * @author moushuai
 *
 */
public class BeanFactory {
	private Properties propt;
	
	public BeanFactory(InputStream stream) {
		propt = new Properties();
		try {
			propt.load(stream);
		} catch (IOException e) {
			System.out.println("Failed in initializing properties stream!");
		}
	}
	
	/**
	 * 获取一个bean实例
	 * @param name
	 * @return
	 */
	public Object getBean(String name) {
		String className = propt.getProperty(name);
		Object bean = null;
		try {
			//获取ProxyFactoryBean的class对象
			Class<?> aClass = Class.forName(className);
			//实例化对象
			bean = aClass.newInstance();
			Object target = Class.forName(propt.getProperty(name + ".target")).newInstance();
			Object advice = Class.forName(propt.getProperty(name + ".advice")).newInstance();
			
			//通过内省实现对ProxyFactoryBean的属性赋值
			BeanInfo beanInfo = Introspector.getBeanInfo(aClass);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for(PropertyDescriptor pd : propertyDescriptors) {
				String propertyName = pd.getName();
				Method writeMethod = pd.getWriteMethod();
				if ("target".equals(propertyName)) {
					writeMethod.invoke(bean, target);
				}
				if ("advice".equals(propertyName)) {
					writeMethod.invoke(bean, advice);
				}
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IntrospectionException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return bean;
	}
}
properties文件:

bean.target=cn.saul.reflection.aop.EmanagerImpl
bean.advice=cn.saul.reflection.aop.LogAdvice
bean=cn.saul.reflection.aop.ProxyFactoryBean
测试类:
package cn.saul.reflection.aop;

import java.io.InputStream;

import org.junit.Test;

public class AopTest {
	
	@Test
	public void test1() {
		//1.读取配置文件
		InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("cn/saul/reflection/aop/config.properties");
		//2.创建bean的工厂对象
		BeanFactory beanFactory = new BeanFactory(inputStream);
		//3.获取代理对象
		ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) beanFactory.getBean("bean");
		Emanager proxy = (Emanager) proxyFactoryBean.getProxy();
		proxy.add("hello, aop!");
		
		
	}
}

9、单例模式优化

1、使用同步保正线程安全 synchronized
2、使用volatile关键字

  • volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

3、防止反射调用私有构造方法
4、让单例类序例化安全

  • 单线程单例模式:
package cn.saul.reflection.singleton;

/**
 * 单例模式
 * @author moushuai
 *
 */
public class SingletonNormal {
	private static SingletonNormal singletonNormal = null;
	private SingletonNormal(){}
	
	public static SingletonNormal getInstance() {
		if (singletonNormal == null) {
			singletonNormal = new SingletonNormal();
		}
		return singletonNormal;
	}
}
  • 适用性单例模式:
package cn.saul.reflection.singleton;

import java.io.Serializable;

/**
 * 单例模式:
 * 1.多线程访问的安全问题,加synchronized同步代码块。
 * 2.加上volatile关键字保证变量的一致性
 * 3.防止反射调用私有构造方法
 * 4。让单例类序例化安全(让单例类实现Serializable接口)
 * @author moushuai
 *
 */

public class Singleton implements Serializable{
	
	private static final long serialVersionUID = 1L;
	private static volatile Singleton singleton = null;
	private Singleton(){
		//防止反射调用私有构造方法
		if (singleton != null) {
			throw new RuntimeException("this class is singleton, and the object has been created");
		}
	}
	
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		
		return singleton;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值