前言:记录一篇自己入门java安全的故事,捋一下思路,轻量知识 ,重在调试 !
.
这篇文章四个部分:
引入篇:整理一下CVE-2022-22965漏洞的来龙去脉
基础篇:回顾Java中一些基础的内容
调试篇:阅读Spring MVC部分源码
分析篇:分析篇:分析CVE-2010-1622、CVE-2022-22965的漏洞成因
.
基础篇
( 紧接 "浅谈CVE-2022-22965漏洞成因(一)",回顾一些后面分析程序时要用到的Java基础 )
1、Java Bean
Java中的Bean是一种特殊的类,用来封装数据,传递信息,下面给出一个Bean:
```
public class Person {
private String name;
private int age;
private Boolean student;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Boolean isStudent() {
return student;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setStudent(Boolean student) {
this.student = student;
}
}
```
- “类”这个概念很好理解,直白的说就是有“class”关键字修饰
- 封装的数据可以理解为定义了一些私有属性,如, private关键字修饰的变量
- 传递信息的过程则指Bean被调用并通过Bean内的公共方法或getter/setter访问Bean属性的过程
- Bean是特殊的类可以表现在Bean属性与Bean的getter/setter方法相互对应
如果一个属性为abc,那么相对应的getter/setter方法就是: - ( 公式:getter/setter方法+首字母大字的属性abc )
getAbc() 和 setAbc()
特殊的,布尔型变量形如 boolean student,对应的getter/setter方法分别为:
isStudent() 和 setStudent
更多举例:
java
String name
getName()
setName(String name)
===================================
int age
getAge()
setAge(int age)
===================================
Boolean student;
getStudent()
isStudent(Boolean student)
2、BeanInfo
BeanInfo是JDK自带的Java.Beans包下的一个接口,根据官方的描述,BeanInfo通过Bean属性( properties )、事件( events )和其他特殊特性( features )来实现对一个已经实例化类的准确描述,下面给出一个BeanInfo的完整结构:
```
仅需关注我标注的地方
public interface BeanInfo {
获取一个Bean的整体信息
BeanDescriptor getBeanDescriptor();
获取一个Bean的所有事件的描述信息
EventSetDescriptor[] getEventSetDescriptors();
int getDefaultEventIndex();
获取一个Bean的所有属性的描述信息
PropertyDescriptor[] getPropertyDescriptors();
int getDefaultPropertyIndex();
获取一个Bean的所有方法的描述信息
MethodDescriptor[] getMethodDescriptors();
BeanInfo[] getAdditionalBeanInfo();
Image getIcon(int iconKind);
static final int ICON_COLOR_16x16 = 1;
static final int ICON_COLOR_32x32 = 2;
static final int ICON_MONO_16x16 = 3;
static final int ICON_MONO_32x32 = 4;
}
```
对于一个Java应用的开发人员来说,可以通过实现该接口的方法来定制自己需要的BeanInfo,这个我们在调试Spring MVC 的过程中也有所涉及。
3、PropertyDescriptor
PropertyDescriptor即属性描述器,顾名思义,这个类完整的描述了一个属性的所有信息,PropertyDescriptor继承自FeatureDescriptor:
```
public class PropertyDescriptor {
private final MethodRef readMethodRef = new MethodRef();
private final MethodRef writeMethodRef = new MethodRef();
private Reference<? extends Class<?>> propertyEditorClassRef;
private boolean bound;
private boolean constrained;
private String baseName;
private String writeMethodName;
private String readMethodName;
.......
设置PropertyDescriptor属性的方法(包括getter/setter)
.......
}
```
这里我们仅需要知道,PropertyDescriptor本身描述了一个Bean属性的可读可写权限,一个Bean属性是否可读或可最终是通过Java Bean的getter/setter方法来实的,但getter/setter方法的调用则是通过给Method类传参PropertyDescripto后调用反射实现的
4、Introspector
Introspector同样是JDK自带的Java.Beans包下的一个类,Introspector就是内省,通过Introspector类提供的方法,我们可以完整的获取到目标 Java Bean 的属性、事件和方法,即BeanInfo,官方原版的描述如下:
The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean.For each of those three kinds of information, the Introspector will separately analyze the bean's class and superclasses looking for either explicit or implicit information and use that information to build a BeanInfo object that comprehensively describes the target bean.For each class "Foo", explicit information may be available if there exists a corresponding "FooBeanInfo" class that provides a non-null value when queried for the information. We first look for the BeanInfo class by taking the full package-qualified name of the target bean class and appending "BeanInfo" to form a new class name. If this fails, then we take the final classname component of this name, and look for that class in each of the packages specified in the BeanInfo package search path.Thus for a class such as "sun.xyz.OurButton" we would first look for a BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd look in each package in the BeanInfo search path for an OurButtonBeanInfo class. With the default search path, this would mean looking for "sun.beans.infos.OurButtonBeanInfo".If a class provides explicit BeanInfo about itself then we add that to the BeanInfo information we obtained from analyzing any derived classes,but we regard the explicit information as being definitive for the current class and its base classes, and do not proceed any further up the superclass chain.If we don't find explicit BeanInfo on a class, we use low-level reflection to study the methods of the class and apply standard design patterns to identify property accessors, event sources, or public methods. We then proceed to analyze the class's superclass and add in the information from it (and possibly on up the superclass chain).
翻译过来大意就是通过内省获取Bean属性时,会先尝试按默认的规则找找是否存在现成的目标的BeanInfo,如果不存在,最后会递归解析并调用反射来尽可能构造一个完整的目标BeanInfo,常用到的方法如下:
更多详情,参考官方文档:https://docs.oracle.com/javase/8/docs/api/
归纳下来后,有两类,一类不能设立解析Bean属性时停止的位置,一类可以设立解析Bean属性时停止的位置:
- BeanInfo getBeanInfo(Class beanClass)
- BeanInfo getBeanInfo(Class beanClass, Class stopClass)
我们用第二种(设立解析时停止位置)来获取一下Person类的属性(Person类,参上)
```java
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Main {
public static void main(String[] args) throws Exception {
//获取Person类所有的属性
BeanInfo info = Introspector.getBeanInfo(Person.class,Object.class);
//遍历Person类所有的属性
PropertyDescriptor[] properties =
info.getPropertyDescriptors();
for (PropertyDescriptor pd : properties) {
System.out.println("Property: " + pd.getName());
}
}
}
```
上面的代码运行后,成功获取到的Person类的所有属性,这与实际的Person类中的情况相符:
(原Person类)
(运行后)
我们再用第一种(不设立解析时停止位置)来获取一下Person类的属性(Person类,参上)
此时就会发现,Student里虽然没有定义class属性,但在通过Introspector.getBeanInfo方法解析并获取Student类BeanInfo的PropertyDescriptor时,莫名多出了一个class属性。 原因就在于Object是Java 类中所用类的超类,而Object类里有一个getClass()方法,因此导致递归解析到父类时便会多获取到了一个Bean的Property。
这里就可以抛出一个疑问:
为什么Object类存在一个getClass()方法Student类的PropertyDescriptors就要多一个class属性?
获取一个Bean的属性,最直观的办法我们都能想到,就是调用Bean的getter/setter方法,因此如果能批量调取Bean的所有getter/setter方法就能获取到Bean的所有属性。
并且,这样做比直接访问Bean属性更容易实现,因为getter/setter方法有固定的首格式,且与Bean属性对照,而单一的Bean属性名却没有统一的规律。
但是Introspector.getBeanInfo一个目标类,实际并不是直接批量调用了Bean的getter/setter方法,而是通过推导实现的。
按照Ruilin师傅的说法,Introspector.getBeanInfo一个目标类,直接通过getter/setter方法的命名规则就能获得一个Bean的各种属性。
也就是说,目标Bean里有getter或setter方法的任意一个,Introspector.getBeanInfo时就会默认认为存在一个相对应的Bean属性。
再者,由于Introspector本身在获取BeanInfo时存在"尽可能完整的"的特性,因此递归解析到了Object并从它的getClass()方法中解析出了一个class属性,最后封装到目标的BeanInfo里。
这里我们可以通过几个例子粗浅的感受一下这个过程:
第一组:删掉name、age、student三个属性,保留三个属性对应的getter/setter方法,修改这些getter/setter内容为空
```java
public class Person {
public String getName() {
return null;
}
public int getAge() {
return 0;
}
public Boolean isStudent() {
return null;
}
public void setName(String name) {
}
public void setAge(int age) {
}
public void setStudent(Boolean student) {
}
}
```
第二组:在第一组的基础上删除所有setter方法
```java
public class Person {
public String getName() {
return null;
}
public int getAge() {
return 0;
}
public Boolean isStudent() {
return null;
}
}
```
第三组:在第一组的基础上删除所有getter方法
```java
public class Person {
public void setName(String name) {
}
public void setAge(int age) {
}
public void setStudent(Boolean student) {
}
}
```
运行、遍历Person类后,最后的结果无一例外,依然是上面提到的三个属性
第四组:直接对Object作内省
5、Java反射机制
Java反射就是借用Java中内置的反射库使编写的Java程序具有了动态的操作Java类的能力,具体表现在利用Java反射机制我们可以:
- 在Java程序运行中分析一个类
- 在运行中查看对象
- 使用Method对象 等
.
我们先来看看反射库是什么(不同jdk版本的反射库略有不同,这里我们对比着看一下)
.
rt.jar是jre/lib下的一个运行时库,这个包会在JVM环境启动时被加载,rt.jar包含了我们最熟悉的java.lang包,下面分别打印出jdk8和jdk11的lang包结构:
jdk 1.8
java
java.lang
AbstractMethodError.java
AbstractStringBuilder.java
annotation
AnnotationFormatError.java
Annotation.java
AnnotationTypeMismatchException.java
Documented.java
ElementType.java
IncompleteAnnotationException.java
Inherited.java
Native.java
package-info.java
Repeatable.java
Retention.java
RetentionPolicy.java
Target.java
Appendable.java
ApplicationShutdownHooks.java
ArithmeticException.java
ArrayIndexOutOfBoundsException.java
ArrayStoreException.java
AssertionError.java
AssertionStatusDirectives.java
AutoCloseable.java
Boolean.java
BootstrapMethodError.java
Byte.java
CharacterData00.java
CharacterData01.java
CharacterData02.java
CharacterData0E.java
CharacterData.java
CharacterDataLatin1.java
CharacterDataPrivateUse.java
CharacterDataUndefined.java
Character.java
CharacterName.java
CharSequence.java
ClassCastException.java
ClassCircularityError.java
ClassFormatError.java
Class.java
ClassLoaderHelper.java
ClassLoader.java
ClassNotFoundException.java
ClassValue.java
Cloneable.java
CloneNotSupportedException.java
Comparable.java
Compiler.java
ConditionalSpecialCasing.java
Deprecated.java
Double.java
EnumConstantNotPresentException.java
Enum.java
Error.java
ExceptionInInitializerError.java
Exception.java
Float.java
FunctionalInterface.java
IllegalAccessError.java
IllegalAccessException.java
IllegalArgumentException.java
IllegalMonitorStateException.java
IllegalStateException.java
IllegalThreadStateException.java
IncompatibleClassChangeError.java
IndexOutOfBoundsException.java
InheritableThreadLocal.java
InstantiationError.java
InstantiationException.java
instrument
ClassDefinition.java
ClassFileTransformer.java
IllegalClassFormatException.java
Instrumentation.java
UnmodifiableClassException.java
Integer.java
InternalError.java
InterruptedException.java
invoke
AbstractValidatingLambdaMetafactory.java
BoundMethodHandle.java
CallSite.java
ConstantCallSite.java
DelegatingMethodHandle.java
DirectMethodHandle.java
DontInline.java
ForceInline.java
InfoFromMemberName.java
InjectedProfile.java
InnerClassLambdaMetafactory.java
InvokeDynamic.java
InvokerBytecodeGenerator.java
Invokers.java
LambdaConversionException.java
LambdaFormBuffer.java
LambdaFormEditor.java
LambdaForm.java
LambdaMetafactory.java
MemberName.java
MethodHandleImpl.java
MethodHandleInfo.java
MethodHandle.java
MethodHandleNatives.java
MethodHandleProxies.java
MethodHandles.java
MethodHandleStatics.java
MethodTypeForm.java
MethodType.java
MutableCallSite.java
package-info.java
ProxyClassesDumper.java
SerializedLambda.java
SimpleMethodHandle.java
Stable.java
SwitchPoint.java
TypeConvertingMethodAdapter.java
VolatileCallSite.java
WrongMethodTypeException.java
Iterable.java
LinkageError.java
Long.java
management
BufferPoolMXBean.java
ClassLoadingMXBean.java
CompilationMXBean.java
GarbageCollectorMXBean.java
LockInfo.java
ManagementFactory.java
ManagementPermission.java
MemoryManagerMXBean.java
MemoryMXBean.java
MemoryNotificationInfo.java
MemoryPoolMXBean.java
MemoryType.java
MemoryUsage.java
MonitorInfo.java
OperatingSystemMXBean.java
PlatformComponent.java
PlatformLoggingMXBean.java
PlatformManagedObject.java
RuntimeMXBean.java
ThreadInfo.java
ThreadMXBean.java
Math.java
NegativeArraySizeException.java
NoClassDefFoundError.java
NoSuchFieldError.java
NoSuchFieldException.java
NoSuchMethodError.java
NoSuchMethodException.java
NullPointerException.java
NumberFormatException.java
Number.java
Object.java
OutOfMemoryError.java
Override.java
package-info.java
Package.java
ProcessBuilder.java
ProcessEnvironment.java
ProcessImpl.java
Process.java
Readable.java
ref
FinalizerHistogram.java
Finalizer.java
FinalReference.java
PhantomReference.java
Reference.java
ReferenceQueue.java
SoftReference.java
WeakReference.java
reflect
AccessibleObject.java
AnnotatedArrayType.java
AnnotatedElement.java
AnnotatedParameterizedType.java
AnnotatedType.java
AnnotatedTypeVariable.java
AnnotatedWildcardType.java
Array.java
Constructor.java
Executable.java
Field.java
GenericArrayType.java
GenericDeclaration.java
GenericSignatureFormatError.java
InvocationHandler.java
InvocationTargetException.java
MalformedParameterizedTypeException.java
MalformedParametersException.java
Member.java
Method.java
Modifier.java
package-info.java
ParameterizedType.java
Parameter.java
Proxy.java
ReflectAccess.java
ReflectPermission.java
Type.java
TypeVariable.java
UndeclaredThrowableException.java
WeakCache.java
WildcardType.java
ReflectiveOperationException.java
Runnable.java
RuntimeException.java
Runtime.java
RuntimePermission.java
SafeVarargs.java
SecurityException.java
SecurityManager.java
Short.java
Shutdown.java
StackOverflowError.java
StackTraceElement.java
StrictMath.java
StringBuffer.java
StringBuilder.java
StringCoding.java
StringIndexOutOfBoundsException.java
String.java
SuppressWarnings.java
System.java
Terminator.java
ThreadDeath.java
ThreadGroup.java
Thread.java
ThreadLocal.java
Throwable.java
TypeNotPresentException.java
UnknownError.java
UnsatisfiedLinkError.java
UnsupportedClassVersionError.java
UnsupportedOperationException.java
VerifyError.java
VirtualMachineError.java
Void.java
jdk 11
java.lang
AbstractMethodError.java
AbstractStringBuilder.java
annotation
AnnotationFormatError.java
Annotation.java
AnnotationTypeMismatchException.java
Documented.java
ElementType.java
IncompleteAnnotationException.java
Inherited.java
Native.java
package-info.java
Repeatable.java
Retention.java
RetentionPolicy.java
Target.java
Appendable.java
ApplicationShutdownHooks.java
ArithmeticException.java
ArrayIndexOutOfBoundsException.java
ArrayStoreException.java
AssertionError.java
AssertionStatusDirectives.java
AutoCloseable.java
Boolean.java
BootstrapMethodError.java
Byte.java
CharacterData00.java
CharacterData01.java
CharacterData02.java
CharacterData0E.java
CharacterData.java
CharacterDataLatin1.java
CharacterDataPrivateUse.java
CharacterDataUndefined.java
Character.java
CharacterName.java
CharSequence.java
ClassCastException.java
ClassCircularityError.java
ClassFormatError.java
Class.java
ClassLoaderHelper.java
ClassLoader.java
ClassNotFoundException.java
ClassValue.java
Cloneable.java
CloneNotSupportedException.java
Comparable.java
Compiler.java
ConditionalSpecialCasing.java
Deprecated.java
Double.java
EnumConstantNotPresentException.java
Enum.java
Error.java
ExceptionInInitializerError.java
Exception.java
FdLibm.java
Float.java
FunctionalInterface.java
IllegalAccessError.java
IllegalAccessException.java
IllegalArgumentException.java
IllegalCallerException.java
IllegalMonitorStateException.java
IllegalStateException.java
IllegalThreadStateException.java
IncompatibleClassChangeError.java
IndexOutOfBoundsException.java
InheritableThreadLocal.java
InstantiationError.java
InstantiationException.java
Integer.java
InternalError.java
InterruptedException.java
invoke
AbstractConstantGroup.java
AbstractValidatingLambdaMetafactory.java
BootstrapCallInfo.java
BootstrapMethodInvoker.java
BoundMethodHandle.java
CallSite.java
ClassSpecializer.java
ConstantBootstraps.java
ConstantCallSite.java
ConstantGroup.java
DelegatingMethodHandle.java
DirectMethodHandle.java
GenerateJLIClassesHelper.java
InfoFromMemberName.java
InjectedProfile.java
InnerClassLambdaMetafactory.java
InvokeDynamic.java
InvokerBytecodeGenerator.java
Invokers.java
LambdaConversionException.java
LambdaFormBuffer.java
LambdaFormEditor.java
LambdaForm.java
LambdaMetafactory.java
MemberName.java
MethodHandleImpl.java
MethodHandleInfo.java
MethodHandle.java
MethodHandleNatives.java
MethodHandleProxies.java
MethodHandles.java
MethodHandleStatics.java
MethodTypeForm.java
MethodType.java
MutableCallSite.java
package-info.java
ProxyClassesDumper.java
SerializedLambda.java
SimpleMethodHandle.java
StringConcatException.java
StringConcatFactory.java
SwitchPoint.java
TypeConvertingMethodAdapter.java
VarForm.java
VarHandleBooleans.java
VarHandleByteArrayAsChars.java
VarHandleByteArrayAsDoubles.java
VarHandleByteArrayAsFloats.java
VarHandleByteArrayAsInts.java
VarHandleByteArrayAsLongs.java
VarHandleByteArrayAsShorts.java
VarHandleByteArrayBase.java
VarHandleBytes.java
VarHandleChars.java
VarHandleDoubles.java
VarHandleFloats.java
VarHandleGuards.java
VarHandleInts.java
VarHandle.java
VarHandleLongs.java
VarHandleObjects.java
VarHandleShorts.java
VarHandles.java
VolatileCallSite.java
WrongMethodTypeException.java
Iterable.java
LayerInstantiationException.java
LinkageError.java
LiveStackFrameInfo.java
LiveStackFrame.java
Long.java
Math.java
module
Configuration.java
FindException.java
InvalidModuleDescriptorException.java
ModuleDescriptor.java
ModuleFinder.java
ModuleReader.java
ModuleReference.java
package-info.java
ResolutionException.java
ResolvedModule.java
Resolver.java
Module.java
ModuleLayer.java
NamedPackage.java
NegativeArraySizeException.java
NoClassDefFoundError.java
NoSuchFieldError.java
NoSuchFieldException.java
NoSuchMethodError.java
NoSuchMethodException.java
NullPointerException.java
NumberFormatException.java
Number.java
Object.java
OutOfMemoryError.java
Override.java
package-info.java
Package.java
ProcessBuilder.java
ProcessEnvironment.java
ProcessHandleImpl.java
ProcessHandle.java
ProcessImpl.java
Process.java
PublicMethods.java
Readable.java
ref
Cleaner.java
FinalizerHistogram.java
Finalizer.java
FinalReference.java
package-info.java
PhantomReference.java
Reference.java
ReferenceQueue.java
SoftReference.java
WeakReference.java
reflect
AccessibleObject.java
AnnotatedArrayType.java
AnnotatedElement.java
AnnotatedParameterizedType.java
AnnotatedType.java
AnnotatedTypeVariable.java
AnnotatedWildcardType.java
Array.java
Constructor.java
Executable.java
Field.java
GenericArrayType.java
GenericDeclaration.java
GenericSignatureFormatError.java
InaccessibleObjectException.java
InvocationHandler.java
InvocationTargetException.java
MalformedParameterizedTypeException.java
MalformedParametersException.java
Member.java
Method.java
Modifier.java
package-info.java
ParameterizedType.java
Parameter.java
ProxyGenerator.java
Proxy.java
ReflectAccess.java
ReflectPermission.java
Type.java
TypeVariable.java
UndeclaredThrowableException.java
WildcardType.java
ReflectiveOperationException.java
Runnable.java
RuntimeException.java
Runtime.java
RuntimePermission.java
SafeVarargs.java
SecurityException.java
SecurityManager.java
Short.java
Shutdown.java
StackFrameInfo.java
StackOverflowError.java
StackStreamFactory.java
StackTraceElement.java
StackWalker.java
StrictMath.java
StringBuffer.java
StringBuilder.java
StringCoding.java
StringConcatHelper.java
StringIndexOutOfBoundsException.java
String.java
StringLatin1.java
StringUTF16.java
SuppressWarnings.java
System.java
Terminator.java
ThreadDeath.java
ThreadGroup.java
Thread.java
ThreadLocal.java
Throwable.java
TypeNotPresentException.java
UnknownError.java
UnsatisfiedLinkError.java
UnsupportedClassVersionError.java
UnsupportedOperationException.java
VerifyError.java
VersionProps.java
VirtualMachineError.java
Void.java
WeakPairMap.java
我们再在java.lang包下找到reflect库,并对比一下不同版本的区别:
基本上都差不多,java.lang.reflect包就是Java为我们提供的反射库
.
再者,通过上图,我们可以发现在java.lang包下都有一个Class.java
.
前面我们有提到,通过java反射我们可以在java程序运行时动态的分析一个类,而这个动态分析一个类的过程就离不开这个 Class.java
。也就是说通过java反射动态的分析一个类离不开java.lang包下的 Class.java
。
我们知道,一个编写的Java代码会保存成 b
,而这种 demo.java
格式是不能直接被JVM虚拟机运行的,需要先将 demo.java
编译成 demo.class
后才能被JVM虚拟机运行。
.
这种 .class
格式的文件就是字节码文件,里面是二进制内容。当demo.class被加载进内存并生成一个demo对象时,同时也会加载Class.java并生成一个Class对象用于管理demo对象。换言之,也就是说通过Class这个对象,我们可以获得demo对象对象的完整信息。
.
要理解这个过程我们需要先来了解一下Java代码的执行时过程,见下图:
可以归纳为如下这几个关键点:
- Java代码编写后要编译成
.class
文件 - .class文件是字节码,可加密,可用不同语言实现,是Java跨平台的关键
- JVM执行Java程序功能仅仅是通过解析 .class 文件实现的
- JDK中内置 Class 类 实现了对 .class 文件的描述与管理
- Object类是所有类的超类,Class类则可以说是所有运行时类的超类
- new 一个对象的过程可以理解为加载 .class 文件并在内存中创建对象的过程
- 内存中,多个People类的实例化对象只能有一个与People类相关联的Class类的实例化对象
- 通过内存中的Class对象我们不仅可以实现People类的操作,还能实现与Class类相关的Method类、Constructor类的操作,也就是真正的反射
再者,通过Class.java导入的包头,我们也应该知道,java反射与Class.java密不可分,但严格来说Class.java并不属于反射的范畴,因为它不在反射库中,但是Class.java中大量的使用了反射库的功能。
jdk1.8 Class.java引入了反射库:
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Member;
import java.lang.reflect.Field;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.AnnotatedType;
import java.lang.ref.SoftReference;
import java.io.InputStream;
import java.io.ObjectStreamField;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import sun.misc.Unsafe;
import sun.reflect.CallerSensitive;
import sun.reflect.ConstantPool;
import sun.reflect.Reflection;
import sun.reflect.ReflectionFactory;
import sun.reflect.generics.factory.CoreReflectionFactory;
import sun.reflect.generics.factory.GenericsFactory;
import sun.reflect.generics.repository.ClassRepository;
import sun.reflect.generics.repository.MethodRepository;
import sun.reflect.generics.repository.ConstructorRepository;
import sun.reflect.generics.scope.ClassScope;
import sun.security.util.SecurityConstants;
import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import sun.reflect.annotation.*;
import sun.reflect.misc.ReflectUtil;
既然Class对象如此重要,我们先来了解一下获取Class对象的方法,一共有三个:
1.使用forName方法获取class对象(最常用且最好用的方法)
Class pClass= Class.forName("pojo.People")
该方法是一个静态方法,属于Class 类本身,不受是否实例化一个类对象的影响,仅仅需要在forName()方法里传入一个类的具体包路径就能获取到该类的class对象
Class.forName方法的实现
```
Class
@CallerSensitive
public static Class<?> forName(String className)throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
private static native Class<?> forName0(String name, boolean initialize,ClassLoader loader, Class<?> caller)throws ClassNotFoundException;
reflect
@CallerSensitive
public static native Class<?> getCallerClass();
```
也就是说Class.forName方法最终是通过调用反射的reflect.getCallerClass来实现获取一个类对象的Class对象的。
而最终的反射的具体实现我们是无需关注的,因为native关键字修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在其他语言实现的文件中(如C和C++),Java通过JNI接口来封装了底层实现。
2.使用Object.getClass方法获取class对象
```java
通过new关键字产生People对象和Class对象
People people = new People();
Class pClass = people.getClass();
```
3.使用class属性获取class对象
Class pClass = People.class;
class属性是一个静态属性,静态的属性可以被所有相关对象共享,且任何的数据类型都有class属性,结合下面的图我们就很好理解这个class属性
我们总结一下这以上三种方法获取class对象的特点:
最后谈一下反射,反射就是获取到内存中student1这个Student类的实例对象的Class对象后通过反射库操作Student类的各种构造方法、属性域、公有私有方法等。
.
直接看一个实例,理解如何通过反射调用计算器就能理解什么是Java反射
java
//先通过反射获取到了java.lang.Runtime对象的Class对象,即clazz
Class clazz = Class.forName("java.lang.Runtime");
//java.lang.Runtime里面有exec方法,我们直接通过clazz去调用反射库的Method方法去拿exec方法(记得将参数类型传入)
//再用invoke方法去执行(记得传入调用对象和参数)
//而获取调用对象时又用了一次反射
clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke("java.lang.Runtime"),"calc.exe");
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论