
反射说白了就是可以获得一个类的所有信息,主要包括方法和属性两部分。
1获得方法包括获得方法的名称,方法的返回类型,方法的访问修饰符,以及通过反射执行这个方法。
2获得属性包括属性的名称,类型,访问修饰符,以及这个属性的值。
这些获得都有相应的API提供 *** 作。
举个例子:
先定义一个类,有age这个属性,以及age的get和set方法:
public class TestRflectionFather {
public int age =32;
public int getAge() {
return age;
}
public void setAge(int age) {
thisage = age;
}
}
然后 *** 作获得所有方法:
import javalangreflectMethod;
public class Testmain {
public static void main(String[] args) throws Exception {
Class clazz = ClassforName("TestRflectionFather");
Method[] methods =clazzgetDeclaredMethods();
for(Method me:methods)
{
Systemoutprintln(megetName());
}
}
}
//最后输出
getAge
setAge
/
获取一个函数的依赖
@param string|callable $func
@param array $param 调用方法时所需参数 形参名就是key值
@return array 返回方法调用所需依赖
/
function getFucntionParameter($func,$param=[]) {
if(!is_array($param)) {
$param = [$param];
}
$ReflectionFunc = new \ReflectionFunction($func);
$depend = array();
foreach($ReflectionFunc->getParameters() as $value) {
if(isset($param[$value->name])) {
$depend[] = $param[$value->name];
}elseif($value->isDefaultValueAvailable()){
$depend[] = $value->getDefaultValue();
}else{
$tmp = $value->getClass();
if(is_null($tmp)) {
throw new \Exception("Function parameters can not be getClass {$class}");
}
$depend[] = $this->get($tmp->getName());
}
}
return $depend;
}
function test($a,$b=20) {
echo $a,',',$b;
}
$depend = getFucntionParameter('test',['a'=>30,'b'=>40]);
call_user_func_array('test',$depend); // 30,40
上面的函数是我开发的框架的容器的方法。
php提供了很完整的反射机制。不但可以反射函数,还可以反射方法,反射类构造函数。
一个最简单的C#反射实例,首先编写类库如下:
namespace ReflectionTest
{
public class WriteTest
{
//带参数的公共方法
public void WriteString(string s, int i)
{
ConsoleWriteLine("WriteString:" + s + iToString());
}
//带一个参数的静态方法
public static void StaticWriteString(string s)
{
ConsoleWriteLine("StaticWriteString:" + s);
}
//不带参数的静态方法
public static void NoneParaWriteString()
{
ConsoleWriteLine("NoParaWriteString");
}
}
}
class TestApp
{
public static void Main()
{
Assembly ass;
Type type;
Object obj;
//用来测试静态方法
Object any = new Object();
//指定类库文件必须使用绝对路径,不能使用相对路径
ass = AssemblyLoadFile(@"D:\Source Code\00C#Sudy\01Reflection\01\ReflectTestdll");
//命名空间和类的名字必须一起指定
type = assGetType("ReflectionTestWriteTest");
///example1---------/
MethodInfo method = typeGetMethod("WriteString");
string test = "test";
int i = 1;
Object[] parametors = new Object[]{test,i};
//在例子1种必须实例化反射要反射的类,因为要使用的方法并不是静态方法。
//创建对象实例
obj = assCreateInstance("ReflectionTestWriteTest"); //执行带参数的公共方法
methodInvoke(obj, parametors);
//methodInvoke(any, parametors);//异常:必须实例化反射要反射的类,因为要使用的方法并不是静态方法。
///example2----------/
method = typeGetMethod("StaticWriteString");
methodInvoke(null, new string[] { "test"}); //第一个参数忽略//对于第一个参数是无视的,也就是我们写什么都不会被调用,
//即使我们随便new了一个any这样的Object,当然这种写法是不推荐的。
//但是对应在例子1种我们如果Invoke的时候用了类型不一致的实例来做为参数的话,将会导致一个运行时的错误。
methodInvoke(obj, new string[] { "test"});
methodInvoke(any, new string[] { "test"});
///example3-----------/
method = typeGetMethod("NoneParaWriteString"); //调用无参数静态方法的例子,这时候两个参数我们都不需要指定,用null就可以了。methodInvoke(null, null);
}
}
从上面的总结中可以看出,对于外部调用的动态库应用反射时要用到AssemblyLoadFile(),然后才是获取类型、执行方法等;
当用反射创建当前程序集中对象实例或执行某个类下静态方法时只需通过TypeGetType("类的完整名")。
因为一次在做项目的时候需要扫描接口的信息,其中包括参数名,遇到了点障碍就想着把这个解决方案和问题讲一下。
我们要查看的方法如下
java18以后,官方提供了反射的方法能获取到接口的参数名称。示例如下。其中getParameters方法是18才开始提供的。并且需要在javac编译时,加上-parameters参数才行。
通过javap -p -v可以查看class的字节码,如下
其中MethodParameters就是18后在字节码中记录参数名的地方。但是18之前是怎么实现的呢?
spring中有个ParameterNameDiscoverer接口,他有6个实现类。如下:
Aspect开头的都是对增强类的信息获取。我用不到。
PrioritizedParameterNameDiscoverer是一个链表,就是记录一系列的Discoverer。
这个Discoverer就是封装了JDK18的getParameters
这个类是重点,它通过asm获取了class文件的LocalVariableTable信息。class,字节码如下:
其中有一行字节码记录了LocalVariableTable信息,LocalVariableTable里不仅保存了参数名,还保存了其他局部变量信息。spring通过slot来判定哪些是参数以及参数的顺序。
但是LocalVariableTable不是类的必须信息,所以不是编译后必须存在的。只有在javac时-g或-g:vars时,才会保存LocalVariableTable信息。
在idea工具中,我们可以通过如下方式,关闭编译时,自动生成LocalVariableTable来尝试查看字节码。
这个Discoverer就是在18时多添加了个StandardReflectionParameterNameDiscoverer。
在ParameterNameDiscoverer接口上有这么段注释:
它告诉我们,不是任何时候都能获取到参数名的,只能尝试去获取。
当我们关闭了class debug信息,并且将编译级别设置为16时,启动一个简单的spring boot项目。在idea中关闭 *** 作如下:
controller如下:
我们会发现这时候访问该接口传递info参数会报如下错误:
所以,spring mvc中也是有可能获取不到方法参数名的。如果我们需要使用spring mvc的话,最好通过Require等注解来绑定。
反射:在运行状态下,通过class文件对象,去使用构造方法,成员变量,成员方法
方法有三种:
1类名class
2对象getclass()
3classforName("包名类名")
好处:
只要有一个类或者一个类的对象,就可以得到这个类或对象的所有属性和方法,包括私有的
同步方法
反射成员方法:
1获取字节码文件
Class c = ClassforName("包名类名");
2反射方法
Method m = cgetMethod("反射的方法",方法的参数类型class);
3用方法,minvoke(对象,参数) 对象,cnewInstance ,在A建立对象。
minvoke(cnewInstance(),参数);
类加载器与反射有什么关系
反射是通过字节码文件对象,将类的字段,方法,构造器等映射成相应的类,并进行各自的 *** 作;
类加载器是通过某个类的classLoader()方法,将该类的class文件从硬盘中加载到java虚拟机中,形成字节码文件;
以上就是关于说说对java反射的理解,使用反射如何获取一个类的所有方法,使用反射需要注意哪些问题全部的内容,包括:说说对java反射的理解,使用反射如何获取一个类的所有方法,使用反射需要注意哪些问题、php有没有什么函数可以获取一个方法中的参数名和参数类型的、c# 中反射里的invoke方法的参数等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)