1:所写的东西都经过验证,保证正确,环境jdk8,eclipse
2:在例子中,尽量以生产环境中实际代码为例,那种固定值什么的没什么意义
问题:
1:想获取调用方法所需要的参数
2:参数是以json形式存储,在字符串的字段里
3: 传入的参数可以与方法的参数顺序不一致 解决方案:
1:涉及到的技术:反射,json转换
2:思路:
(1):先将json转换成对应的bean
(2):通过反射找到对应方法,获取对应的参数
(3):通过反射获取参数对应的bean,对应参数名称的值,
(4):注入
3:可能存在的问题:参数类型未校验,考虑到时json形式,就未校验
4:上代码(注意:这里的所有参数都是动态,我这里为了测试/方便看才写的固定值)
//quartz job 核心 记录,对于注入的service方法调用需要配合@postConstruct 进行初始化
package com.sony.sie.hrevaluate.quartz.job; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.impl.JobDetailImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.StringUtils; import net.sf.json.JSONObject; /**
* 动态定时任务Job
*
* @author linrx1
*
*/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution // 不允许并发执行
public class DynamicQuartzJob extends QuartzJobBean { private static final Logger logger = LoggerFactory.getLogger(DynamicQuartzJob.class); @Autowired
private ApplicationContext applicationContext; @Override
protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException {
// use JobDetailImpl replace JobDetail for get jobName
JobDetailImpl jobDetail = (JobDetailImpl) jobexecutioncontext.getJobDetail();
String name = jobDetail.getName();
if (StringUtils.isEmpty(name)) {
throw new JobExecutionException("can not find service info, because desription is empty");
} String[] serviceInfo = name.split("\\.");
// serviceInfo[0] is JOB_NAME_PREFIX
String beanName = serviceInfo[1];
// methodName & modelName
String methodName = serviceInfo[2];
Object beanNameContext = applicationContext.getBean(beanName);
Object modelNameContext = applicationContext.getBean(methodName);
// method parameter
JobDataMap dataMap = jobDetail.getJobDataMap(); try {
Class beanNameCls = beanNameContext.getClass();
Class modelNameCls = modelNameContext.getClass(); Object object = JSONObject.toBean(JSONObject.fromObject(dataMap.get("data")), modelNameCls); Class<?>[] parameterTypes = null;
Method[] methods = beanNameCls.getMethods();
Parameter[] invokeMethodParams;
// the array size may be not enough for (Method n : methods) {
if (methodName.equals(n.getName())) {
parameterTypes = n.getParameterTypes();
invokeMethodParams = n.getParameters();
Object[] invokeParam = new Object[invokeMethodParams.length];
for (int i = 0; i < invokeMethodParams.length; i++) {
String parameterName = invokeMethodParams[i].getName();
Field field = modelNameCls.getDeclaredField(parameterName);
field.setAccessible(true);
invokeParam[i] = field.get(object);
}
Method method = beanNameCls.getMethod(methodName, parameterTypes);
method.invoke(beanNameCls.newInstance(), invokeParam);
}
} logger.info("dynamic invoke {}.{}()", beanNameContext.getClass().getName(), methodName);
} catch (Exception e) {
logger.error("reflect invoke service method error", e);
} } }


import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import org.springframework.stereotype.Service; import net.sf.json.JSONObject; @Service
public class HelloService {
public void sayHello(String aa, Integer bb) {
System.out.format("%s %d", aa, bb);
} public static class Test {
String aa;
Integer bb; public String getAa() {
return aa;
} public void setAa(String aa) {
this.aa = aa;
} public Integer getBb() {
return bb;
} public void setBb(Integer bb) {
this.bb = bb;
} }
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchFieldException {      //json 转换 net.sf.json.JSONObject;
String str = "{\r\n" + " \"aa\": \"zhangsan\",\r\n" + " \"bb\": 1\r\n" + "}";
     //注意实例化
Test test = new Test(); test = (Test) JSONObject.toBean(JSONObject.fromObject(str), Test.class); Class<?> clazz = HelloService.class;
     //获取类中的方法
Method[] methods = clazz.getMethods(); Class<?>[] parameterTypes = null;
Parameter[] parameters;
Object[] obj;
for (Method n : methods) {
if ("sayHello".equals(n.getName())) {
parameterTypes = n.getParameterTypes(); parameters = n.getParameters(); // Class clazzTest = test.getClass();
// Object o1 = clazzTest.newInstance();
for (int i = 0; i < parameters.length; i++) {
            //获取方法参数对应的方法名,这里只有jdk1.8及以上支持
String parameterName = parameters[i].getName();
            //获取类中的属性
Field field = test.getClass().getDeclaredField(parameterName);
            //设置类中私有属性可见
field.setAccessible(true);
            //获取属性值
Object obj1 = field.get(test);
}
}
}
     //获取目标方法
Method method = clazz.getMethod("sayHello", parameterTypes);
      //为目标方法注值,第二个参数是值
method.invoke(clazz.newInstance(), test); }
}
 
5:拓展
(1):这张图片是常用的反射方法


(2)这是一个别人写的例子,还不错
参考这篇文章:https://www.cnblogs.com/sun1993/p/7828535.html
import java.lang.reflect.Constructor;
import java.lang.reflect.*; /*Class:代表一个字节码文件的对象,每当有类被加载进内存,JVM就会在堆上给
* 该类创建一个代表该类的对象。每个类的Class对象是的。
*Class类没有构造方法,获得类对应的Class方法有3种
*1.:getClass()、2.类、接口.class 、3.Class.forName("类全名");
*比较推荐使用第3种方式,使用前两种方式程序扩展性不好。
*
*Class类中定义了许多关于获取类中信息的方法:
*1.获得该类的构造方法,属性,方法、实例的方法。包含特定情况的获得
*2.获得该类的父类,实现的接口,该类的类加载器,类名、包名等。
*3.判断该类的具体是接口、类、内部类等
*4.方法中加Declared表示可以获得本类定义的任何方法和属性
*
*注意:关于获得到的方法、属性、构造器的具体操作被封装在import java.lang.reflect包里面
*Method:里面最常用的方法invoke(对象,可变参数列表)--调用指定的方法
*Field:get/set;获取和修改属性值
*Constrcutor:使用newInstance(可变参数列表)--调用指定构造方法创建类的实例
*注意:私有的要调用前先去掉访问权限限制setAccssible()
* */
public class ReflectionWithClass { public static void main(String[] args) throws Exception { //第一种方式获得Class对象,比较麻烦,要先创建对象,再使用对象调用方法
HelloKitty ht = new HelloKitty();
Class clazz = ht.getClass(); //第二种方式获得Class对象。使用静态的属性创建
Class clazz1 = HelloKitty.class; //使用Class对象的静态方法获得Class对象
Class clazz2 = Class.forName("HelloKitty"); //获得该类的类加载器
ClassLoader c = clazz2.getClassLoader();
System.out.println(c.toString()); Class clazz3 = String.class;
System.out.println(clazz3.getClassLoader()); //获得该类的实例
Object obj = clazz2.newInstance();
//获得该类的构造器---公开的,getDeclaredConstructors()--可以获得私有的
Constructor[] con = clazz2.getDeclaredConstructors();
for(Constructor cc:con){
System.out.print(cc + " ");
} //获得类的方法
Method[] mm = clazz2.getDeclaredMethods();
for(Method mmm:mm){
System.out.print(mmm + " ");
} System.out.println();
//获取特定的方法
Method m = clazz2.getMethod("walk",null);
System.out.println(m.toString()); Field[] f = clazz2.getDeclaredFields();
for(Field ff:f){
System.out.print(ff+ " ");
} //调用指定的方法---先获取,在调用;注意私有方法先设置访问权限
Method m1 = clazz2.getMethod("walk", null);
System.out.println("hahahhha");
m1.invoke(obj,null); //调用指定的构造方法创建类实例;先获取在调用
Constructor cc = clazz2.getConstructor(int.class,String.class);
Object o1 = cc.newInstance(12,"blue"); //获取和修改对象的属性值
Field ffs = clazz2.getDeclaredField("age");
ffs.setAccessible(true);
ffs.set(obj, 29);
Object oo = ffs.get(obj);
System.out.println(oo); } } class HelloKitty {
private int age;
public String color = "pink";
public HelloKitty() {} public HelloKitty(int age) {
this.age = age;
} public HelloKitty(int age,String color) {
this.age = age;
this.color = color;
System.out.println("okokok");
} public void walk(){
System.out.println("hhhhhhhhhhhhh");
} public void talk(int i){
System.out.println(i + "----------" + age);
}
}

6:如果只是应用,网上文章一大堆,用就要知道一些具体的细节,不然 emmmm.....(不引战,懂就好)

这里说一下核心方法 method.invoke

(1)这是invoke 的入口方法
/**
* Invokes the underlying method represented by this {@code Method}
* object, on the specified object with the specified parameters.
* Individual parameters are automatically unwrapped to match
* primitive formal parameters, and both primitive and reference
* parameters are subject to method invocation conversions as
* necessary.
*
* <p>If the underlying method is static, then the specified {@code obj}
* argument is ignored. It may be null.
*
* <p>If the number of formal parameters required by the underlying method is
* 0, the supplied {@code args} array may be of length 0 or null.
*
* <p>If the underlying method is an instance method, it is invoked
* using dynamic method lookup as documented in The Java Language
* Specification, Second Edition, section 15.12.4.4; in particular,
* overriding based on the runtime type of the target object will occur.
*
* <p>If the underlying method is static, the class that declared
* the method is initialized if it has not already been initialized.
*
* <p>If the method completes normally, the value it returns is
* returned to the caller of invoke; if the value has a primitive
* type, it is first appropriately wrapped in an object. However,
* if the value has the type of an array of a primitive type, the
* elements of the array are <i>not</i> wrapped in objects; in
* other words, an array of primitive type is returned. If the
* underlying method return type is void, the invocation returns
* null.
*
* @param obj the object the underlying method is invoked from
* @param args the arguments used for the method call
* @return the result of dispatching the method represented by
* this object on {@code obj} with parameters
* {@code args}
*
* @exception IllegalAccessException if this {@code Method} object
* is enforcing Java language access control and the underlying
* method is inaccessible.
* @exception IllegalArgumentException if the method is an
* instance method and the specified object argument
* is not an instance of the class or interface
* declaring the underlying method (or of a subclass
* or implementor thereof); if the number of actual
* and formal parameters differ; if an unwrapping
* conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value
* cannot be converted to the corresponding formal
* parameter type by a method invocation conversion.
* @exception InvocationTargetException if the underlying method
* throws an exception.
* @exception NullPointerException if the specified object is null
* and the method is an instance method.
* @exception ExceptionInInitializerError if the initialization
* provoked by this method fails.
*/
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
} (2)这段英文就不具体翻译啦,大致意思就是使用这个方法的各种可能情况,可能出现的问题,特别提醒,如果练习英语但是不会翻墙,看不到国外的文章可以看底层代码的注释,一样可以练习英语
(3)开始正式解读这个方法,首先说一下这注解@CallerSensitive
这个注解是为了堵住漏洞用的。曾经有黑客通过构造双重反射来提升权限,
原理是当时反射只检查固定深度的调用者的类,看它有没有特权,
例如固定看两层的调用者(getCallerClass(2))。如果我的类本来没足够
权限群访问某些信息,那我就可以通过双重反射去达到目的:反射相关
的类是有很高权限的,而在 我->反射1->反射2 这样的调用链上,反射2
检查权限时看到的是反射1的类,这就被欺骗了,导致安全漏洞。
使用CallerSensitive后,getCallerClass不再用固定深度去寻找
actual caller(“我”),而是把所有跟反射相关的接口方法都标注上
CallerSensitive,搜索时凡看到该注解都直接跳过,这样就有效解决了
前面举例的问题
思考:反射还有双重反射?有没有多重反射呢?

(3):invoke方法可以分为俩部分,一部分是访问控制检查,第一个if;第二部分就是MethodAccessor。invoke()实现执行方法

针对第一部分:简单来说就是访问权限检查,判断你是否可以访问这个方法

具体来说就是: 检查override,如果override为true,(Method的父类AccessibleObject中声明的变量)跳过检查;否则继续; 快速检查,判断该方法的修饰符modifiers是否为public,如果是跳过检查;否则继续; 详细检查,通过方法的(protected/private/package)修饰符或方法的声明类(例如子类可以访问父类的protected方法)与调用者caller之间的关系,判断caller是否有权限访问该方法。这里推荐看一下java四种访问权限,可能我们一般只用到private和public,但访问权限控制很重要,在一些多人合作的项目中尤为重要

这里就涉及到一个有意思的 Field(他父类也是AccessibleObject),他的override是false,导致我们在获取属性是常用setAccessible(true),当属性为private修饰

针对第二部分MethodAccessor.invoke();也就是真正的调用方法

这里有一个注释  // read volatile,这个很有意思是,volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略。
//这是methodAccessor,对应的代码
private volatile MethodAccessor methodAccessor;
逻辑很简单就是简单的赋值,如果不存在就创建;实现就不太好理解啦,看源码(提示:一定要好好看注释)
// NOTE that there is no synchronization used here. It is correct
// (though not efficient) to generate more than one MethodAccessor
// for a given Method. However, avoiding synchronization will
// probably make the implementation more scalable.
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
} return tmp;
} // Returns MethodAccessor for this Method object, not looking up
// the chain to the root
MethodAccessor getMethodAccessor() {
return methodAccessor;
} // Sets the MethodAccessor for this Method object and
// (recursively) its root
void setMethodAccessor(MethodAccessor accessor) {
methodAccessor = accessor;
// Propagate up
if (root != null) {
root.setMethodAccessor(accessor);
}
} //这个是我在看别人文章时看到的,这个方法为了维护root引用,标记一下,日后再来研究
//这个方法可能会导致类膨胀
//类膨胀:类的膨胀(Bloating)指的是类中成员过多,甚至出现无序增加的情况。过大的类,会使得复杂度急剧增加,维护会变得更为困难。所以需要控制类的增长。
//https://www.2cto.com/kf/201506/410716.html,这篇文章讲解类膨胀
Method copy() {
Method res = new Method(clazz, name, parameterTypes, returnType,
exceptionTypes, modifiers, slot, signature,
annotations, parameterAnnotations, annotationDefault);
res.root = this;
res.methodAccessor = methodAccessor;
return res;
}
注意这个方法acquireMethodAccessor,他有一个调用反射工厂的过程,ReflectionFactory利用MethodAccessor的字节码生成类MethodAccessorGenerator直接创建一个代理类,通过间接调用原方法完成invoke()任务;
这句话有几个概念:字节码,动态代理,反射最终都是调用原始类
这些遗留问题日后在解决把!!!

7:总结一下我理解的反射和应用场景

(1)反射的概念:可以理解成你去了一个小黑屋,里面可能有各种东西,都上了锁,你通过反射就能获取里面的东西,并且去除锁,知道东西的真正面目

(2)反射应用场景:简单来说就是“动态带有固定”,举个例子,比如我上面的例子,方法是动态的,但是方法的参数是固定的;传入的参数固定的,但是顺序未必和方法一致的;

Java反射简单使用--第一次细致阅读底层代码的更多相关文章

  1. Java反射-简单应用

    为了程序更好的维护和扩展,在面向对象思维的世界里,首先是面向接口编程,然后我们应该把做什么和怎么做进行分离. 以下我将用一个开晚会的样例来演示一下,终于达到的效果是:工厂+反射+配置文件实现程序的灵活 ...

  2. Java反射+简单工厂模式总结

    除了 new 之外的创建对象的方法 通过 new 创建对象,会使得程序面向实现编程,先举个例子,某个果园里现在有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用get()方法表示即可 ...

  3. Java反射 - 简单的给Bean赋值和取值

    由于项目的实际需要,所以利用java反射原理写了一个简单给bean赋值和取值通用的类,在此记录下方便自己日后用到,也为需要的兄弟提供个参考例子. 工具类BeanRefUtil:   package c ...

  4. Java 反射(简单捋一下)

    有Student类,Person类,还有一个叫Class的类,这是反射的源头. 正常方式:通过完整的类名 > 通过new实例化 > 取得实例化对象 反射方式:实例化对象 > getC ...

  5. java反射简单实例

    这篇博友的总结的反射知识点是比较全面的 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html 下面介绍我用反射做的两个功能 ...

  6. java反射机制简单实例

    目录 Java反射 简单实例 @(目录) Java反射 Java语言允许通过程序化的方式间接对Class进行操作.Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通 ...

  7. Java反射机制深度剖析

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java反射机制是Java语言中一种很重要的机制,可能在工作中用到的机会不多,但是在很多框架中都有用到这种机制.我们知道Java是一门静态 ...

  8. java反射机制的简单介绍

    参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先给出反射机制中常用的几个方法: Class.forName (& ...

  9. Java反射(六)纯面向接口编程的简单框架实践

    我们知道在使用MyBatis开发时,只需要添加DAO接口和对应的映射XML文件,不需要写DAO的实现类,其实底层是通过动态代理实现. 本文将使用前几篇文章的知识点实现一个纯面向接口编程的简单框架,与M ...

随机推荐

  1. vmstat/top/iostat/route/sar 常用命令

    [vmstat]Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.是对系统的整体情况进行统计,不足之处是无法对某个进程进行深 ...

  2. [洛谷P1144][题解]最短路计数

    这道题可以用各种算法踩掉,我选择的是SPFA. 因为题目要求计数,所以我们开一个ans数组表示数量. 分两种情况讨论: 一:dis_v>dis_u+1 最短路被更新了,可以直接ans_v=ans ...

  3. java对 zip文件的压缩和解压(ant解决中文乱码)

    说明: 1.对于压缩的文件,当文件名称是中文时,若使用JDK API中自带的类(java.util.zip.ZipEntry; java.util.zip.ZipOutputStream;)进行压缩, ...

  4. typescript与nodejs(一)最简单的webserver

    安装nodejs tsc cnpm vscode 这些略 如果网络慢,可以考虑使用CNPM 一. 基本WebServer模块环境 1. 命令行  npm init 初始化一个目录为nodejs项目 2 ...

  5. Java连载54-两种单例模式、接口详解

    一.单例模式分为两种: (1)饿汉式单例:在类加载阶段就创建了一个对象. (2)懒汉式单例:用对对象的时候才会创建对象.(连载53中例子就是懒汉式) 饿汉式举例: package com.bjpowe ...

  6. HDU - 5952 Counting Cliques

    Counting Cliques HDU - 5952 OJ-ID: hdu-5952 author:Caution_X date of submission:20191110 tags:dfs,gr ...

  7. Java生鲜电商平台-电商起送价的深入思考与实战

    Java生鲜电商平台-电商起送价的深入思考与实战 说明:在生鲜电商中,起送价是一个非常普遍的话题,今天我们就用实战来告诉大家,如何设置起送价,如何编写起送价的代码,以及如何同步起送价. 在开始题目之前 ...

  8. Abp小知识-如何全局设置DontWrapResult属性

    demo地址 GitHub相关demo地址:https://github.com/PuzzledAlien/DotNetCore_Practice/tree/master/ABP.Demo/WebAp ...

  9. 42-volume 生命周期管理

    Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要.前面我们主要关注的是 volume 的创建.共享和使用,本节将讨论如何备份.恢复.迁移和销毁 volume. ...

  10. sqlserver中将datetime类型转换为yyyyMMddHHmmss格式

    JSON 中时间格式要求yyyyMMddHHmmss,从sqlserver 中转换 语句如下: Select REPLACE(CONVERT(varchar(100), GETDATE(), 112) ...