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;
}
}