1. 定义
- 反向探知,在程序运行过程中动态获取类的相关属性。
- 对于给定的一个类,可以获取类的所有属性和方法。
2. 使用
public class User {
public void sayHello() {
System.out.println("hello");
}
}
public class Test {
public static void main(String[] args) throws Exception {
Class<User> clazz = User.class; //获取一个类对象
User user = clazz.newInstance(); //获取一个实例对象
Method method = clazz.getDeclaredMethod("sayHello"); //获取类中的方法
method.invoke(user); //通过反射执行方法
}
}
3. 反射的优缺点
优点
- 增加程序的灵活性,避免固有逻辑写死到程序中
- 代码相当简洁,可以提高程序的复用性
public class Test {
public static void main(String[] args) throws Exception {
Animal animal = sayHello("cat");
animal.sayHello();
}
// 现在Animal下有两个实现类,两个判断,但是如果新添加一个实现类,还需要再次修改这块代码,不够灵活
public static Animal sayHello(String name) {
if ("dog".equals(name)) {
return new Dog();
}
if ("cat".equals(name)) {
return new Cat();
}
return null;
}
}
使用反射改进,增加灵活性
public class Test {
public static void main(String[] args) throws Exception {
Animal animal = sayHello(Cat.class.getName());
animal.sayHello();
}
// 添加新的实现类,只需要传入新实现类的全路径名即可,不用修改这块代码
public static Animal sayHello(String name) {
try { // 根据类的全路径名反射获取类
Class clazz = Class.forName(name);
return (Animal) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
上面只是举例说明反射的灵活性,实际中并不建议这样用,可以用Java多态的特性“声明接口,创建实现类”实现这一点
public class Test {
public static void main(String[] args) throws Exception {
Animal animal = new Cat(); //声明接口Animal,创建实现类Cat
Animal animal2 = new Dog(); //声明接口Animal,创建实现类Dog
animal.sayHello();
animal2.sayHello();
}
}
缺点
- 相比于直接调用,反射有比较大的性能消耗,速度比较慢
- 内部暴露和安全隐患
性能比较
public class Test {
public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
int length = 10000;
for (int i = 0; i < length; i++) {
Animal animal = new Cat();
animal.sayHello();
}
long endTime = System.currentTimeMillis();
System.out.println("对象调用"+ length +"次耗时:" + (endTime-startTime) + " ms");
long startTime2 = System.currentTimeMillis();
for (int i = 0; i < length; i++) {
Animal animal = sayHello(Cat.class.getName());
animal.sayHello();
}
long endTime2 = System.currentTimeMillis();
System.out.println("反射调用"+ length +"次耗时:" + (endTime2-startTime2) + " ms");
}
}
反射慢的原因
- 调用了native方法
- 每次newInstance都会做安全检查,比较耗时
4. 反射的操作
反射获取类对象的方式
Class clazz = Cat.class;
Class clazz2 = Class.forName("com.lucifer.tree.Cat");
Class clazz3 = new Cat().getClass();
Class clazz4 = clazz3.getClassLoader().loadClass("com.lucifer.tree.Cat");
反射获取类的相关结构
int clazzModifier = clazz.getModifiers(); // 获取类的修饰符 1
Package clazzPackage = clazz.getPackage(); // package com.lucifer.tree
String clazzName = clazz.getName(); // com.lucifer.tree.Cat
String canonicalName = clazz.getCanonicalName(); // com.lucifer.tree.Cat
String simpleName = clazz.getSimpleName(); // Cat
ClassLoader classLoader = clazz.getClassLoader(); // sun.misc.Launcher$AppClassLoader@18b4aac2
Class superClass = clazz.getSuperclass(); // class java.lang.Object
Annotation[] annotations = clazz.getAnnotations(); // [Ljava.lang.annotation.Annotation;@677327b6
Class<?>[] interfaces = clazz.getInterfaces(); //[Ljava.lang.Class;@14ae5a5
Modifier and Type | Constant Field | Value |
---|---|---|
public static final int | ABSTRACT | 1024 |
public static final int | FINAL | 16 |
public static final int | INTERFACE | 512 |
public static final int | NATIVE | 256 |
public static final int | PRIVATE | 2 |
public static final int | PROTECTED | 4 |
public static final int | PUBLIC | 1 |
public static final int | STATIC | 8 |
public static final int | STRICT | 2048 |
public static final int | SYNCHRONIZED | 32 |
public static final int | TRANSIENT | 128 |
public static final int | VOLATILE | 64 |
反射获取类的属性
// 获取当前类和父类的公有属性
Field[] fields = clazz.getFields();
// 获取当前类所有属性,包括私有属性
Field[] fields2 = clazz.getDeclaredFields();
// 获取当前类和父类的公有方法
Method[] methods = clazz.getMethods();
// 获取当前类所有方法,包括私有方法
Method[] methods2 = clazz.getDeclaredMethods();
Cat cat = new Cat();
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("miaomiao");
privateMethod.setAccessible(true); //获取私有方法需要放开访问权限,否则报错
privateMethod.invoke(cat);
// 调用静态方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null);
反射获取构造器
// 获取所有公有的构造器
Constructor<?>[] constructors = clazz.getConstructors();
// 获取所有构造器,包括私有的
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 根据参数类型获取指定构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
// 如果是私有构造器,需要放开访问权限
declaredConstructor.setAccessible(true);
// 可以通过构造器获取实例对象
declaredConstructor.newInstance("小花");
// 直接通过newInstance获取对象
Cat cat3 = (Cat) clazz.newInstance();
// 底层本质上还是通过构造器获取实例对象
tmpConstructor.newInstance((Object[])null);
单例的漏洞
问题:正常调用单例类对外提供的方法,只能获取到唯一的一个实例,但是通过反射机制可以获取到不同的实例
原因:反射可以调用私有的构造器造成的
// 通过单例对外提供的方法拿到唯一的实例
SingleInstanceClass instanceClass1 = SingleInstanceClass.getInstance();
SingleInstanceClass instanceClass2 = SingleInstanceClass.getInstance();
SingleInstanceClass instanceClass3 = SingleInstanceClass.getInstance();
// 通过反射获取私有构造器,可以获取到新的实例
Constructor declaredConstructor = instanceClass1.getClass().getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingleInstanceClass instanceClass4 = (SingleInstanceClass) declaredConstructor.newInstance();
解决方法:在私有构造中加入判断逻辑,结合异常处理
public class SingleInstanceClass {
public static SingleInstanceClass instance;
private SingleInstanceClass() {
// 处理通过反射获取实例的漏洞
if (instance != null) {
throw new RuntimeException();
}
}
public static SingleInstanceClass getInstance() {
if (instance == null) {
instance = new SingleInstanceClass();
}
return instance;
}
}
反射的应用场景
- jdbc封装
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mysql";
String user = "root";
String pwd = "root";
Connection connection = DriverManager.getConnection(url, user, pwd);
- SpringIoC
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-ioc.xml");
User user = (User)ac.getBean("user");
// ioc的底层本质上还是反射
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {// 创建对应IOC容器对象
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
// 将配置文件中的<bean>解析成一个BeanDefinition对象
this.loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
// 获取文件路径
InputStream inputStream = encodedResource.getResource().getInputStream();
// SAX 加载配置文件
Document doc = this.doLoadDocument(inputSource, resource);
// 将配置文件解析成BeanDefinition对象
this.registerBeanDefinitions(doc, resource);
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
protected abstract Object createBean(String var1, RootBeanDefinition var2, Object[] var3) throws BeanCreationException;
Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
BeanUtils.instantiateClass(constructorToUse, new Object[0]);
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
ReflectionUtils.makeAccessible(ctor);//放开访问权限
return ctor.newInstance(args);//底层还是使用的反射机制,反射获取构造器获取对象实例
}
- jdbcTemplate
- Mybatis