运行 Java 中的函数 如有必要,请使用 Swing EDT
Running a function in Java Swing EDT if necessary
如果 class 的 public 函数更新了一些 java swing GUI 元素,我必须检查调用此函数的上下文并确保更新已完成在美国东部时间。这就是我解决它的方法,但由于可能有很多功能我必须做同样的事情(但调用 class 的不同私有方法)或其他 classes 我必须做的一样,我厌倦了总是写同样的代码
public void updateGUI()
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
update();
}
});
}
else
{
update();
}
}
private void update()
{
//update the swing elements here
}
一个可能的解决方案可能是创建一个具有此更新方法的接口(名为 UpdateInterface)。 class 现在将实现此接口,我使用静态方法创建一个助手 class,该方法引用此接口并在 EDT 上下文中调用更新方法:
public static void callUpdateInEDT(UpdateInterface f)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
f.update();
}
});
}
else
{
f.update();
}
}
但这不是很灵活,因为我可能有一些我应该在 EDT 上下文中调用的其他私有方法。是否有任何我迄今为止没有找到的 SwingUtilities 解决方案或任何其他比我提出的更灵活的解决方案?
所以在写了很多包装代码之后,我开始思考,在 inner/anonymous classes 之前,我写了一个基于反射的 API 基本上简化使用任意数量的参数调用任意方法的过程
所以,您的代码会变得更像...
SwingSafeMethodInvoker.invokeLater(this, "update").execute();
这只是一种可能的解决方案(而且我很懒,所以我想减少必须(重新)编写的代码量)
该代码足够智能,可以知道您是否在 EDT 上并采取适当的措施。最棒的是它允许我调用 protected
方法(记住,在 inner/anonymous class 之前,你必须创建一个独立的外部 Runnable
class,所以它不能调用 protected
方法)
API 是建立在附加的反射实用程序之上的,因此有点复杂。
警告 此 API 使用了反射,众所周知反射效率较低,因此您需要知道何时不使用它。它还 "breaks" protected
和 private
适用于 class 的访问限制,所以你可以做一些非常有趣的事情,其中大部分我不鼓励你 trying/doing.
这也不会提取您可能对代码所做的任何重构,因此如果您重命名一个方法,这将看不到它,请小心。
记住:这是我写的 bigger reflection API 的一部分,它允许我在 classes 上执行方法,所以它被故意分解成几个 classes
SwingSafeMethodInvoker
public class SwingSafeMethodInvoker {
public static InvokeLater invokeLater(Object obj, String methodName) {
return new InvokeLater(obj, methodName);
}
public static InvokeLater invokeLater(Class clazz, String methodName) {
return new InvokeLater(clazz, methodName);
}
public static InvokeAndWait invokeAndWait(Object obj, String methodName) {
return new InvokeAndWait(obj, methodName);
}
public static InvokeAndWait invokeAndWait(Class clazz, String methodName) {
return new InvokeAndWait(clazz, methodName);
}
public static InvokeAfter invokeAfter(Object obj, String methodName) {
return new InvokeAfter(obj, methodName);
}
public static InvokeAfter invokeAfter(Class clazz, String methodName) {
return new InvokeAfter(clazz, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Object obj, String methodName) {
return new InvokeAfterAndWait(obj, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Class clazz, String methodName) {
return new InvokeAfterAndWait(clazz, methodName);
}
public static MethodInvoker invoke(Object obj, String methodName) {
return new MethodInvoker(obj, methodName);
}
public static MethodInvoker invoke(Class clazz, String methodName) {
return new MethodInvoker(clazz, methodName);
}
}
InvokeLater
import java.awt.EventQueue;
import javax.swing.SwingUtilities;
/**
* This will make a synchronised call into the EventQueue.
*
* There is no way to determine when the actually call will be made. If you
* need to wait for the result of the call, you are better of using
* InvokeAndWait instead
*
* If the invoke method is called within the ETD, it will be executed
* immediately.
*
* If you want the call to occur later (ie have it placed at the end
* of the EventQueue, use InvokeAfter)
*
* The invoke method will always return null.
* @author shane
*/
public class InvokeLater extends AbstractSwingMethodInvoker {
public InvokeLater(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeLater(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeLater(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
if (EventQueue.isDispatchThread()) {
run();
} else {
SwingUtilities.invokeLater(this);
}
return null;
}
}
InvokeAndWait
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
public class InvokeAndWait extends AbstractSwingMethodInvoker {
public InvokeAndWait(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAndWait(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAndWait(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() {
if (EventQueue.isDispatchThread()) {
run();
} else {
try {
SwingUtilities.invokeAndWait(this);
} catch (InterruptedException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
} catch (InvocationTargetException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
}
}
return getResult();
}
}
InvokeAfter
import javax.swing.SwingUtilities;
/**
* This will place the method call onto the end of the event dispatching
* queue and return immediately.
*
* There is no means to known when the call actually takes and place and if
* you are interested in the return result, you are better of using InvokeAndWait
* @author shane
*/
public class InvokeAfter extends InvokeLater {
public InvokeAfter(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAfter(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAfter(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
SwingUtilities.invokeLater(this);
return null;
}
}
AbstractSwingMethodInvoker
import core.util.MethodInvoker;
public abstract class AbstractSwingMethodInvoker extends MethodInvoker implements Runnable {
private Object result;
public AbstractSwingMethodInvoker(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public AbstractSwingMethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public AbstractSwingMethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public void run() {
result = super.execute();
}
public Object getResult() {
return result;
}
public class SynchronisedDispatcherException extends Error {
public SynchronisedDispatcherException(String message) {
super(message);
}
public SynchronisedDispatcherException(String message, Throwable cause) {
super(message, cause);
}
}
}
MethodInvoker
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Provides a means to invoke any method on any class/object using reflection
*
* @author Shane Whitegead
*/
public class MethodInvoker {
private Object parent;
private Class parentClass;
private String methodName;
private List<Parameter> lstParameters;
public MethodInvoker() {
}
public MethodInvoker(Object parent, Class parentClass, String methodName) {
this.parent = parent;
this.parentClass = parentClass;
this.methodName = methodName;
lstParameters = new ArrayList<Parameter>(5);
}
public MethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public MethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
public static MethodInvoker invoke(Object parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Class parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Object parent, Class clazz, String methodName) {
return new MethodInvoker(parent, clazz, methodName);
}
public MethodInvoker setParent(Object parent) {
this.parent = parent;
if (parent != null) {
setParentClass(parentClass.getClass());
}
return this;
}
public MethodInvoker setParentClass(Class parent) {
this.parentClass = parent;
return this;
}
public MethodInvoker setMethodName(String method) {
this.methodName = method;
return this;
}
public <T> MethodInvoker with(Class<T> type, T value) {
with(new Parameter(value, type));
return this;
}
public MethodInvoker with(Class[] types, Object[] parameters) {
if (types == null || parameters == null) {
} else if (types.length != parameters.length) {
} else {
for (int index = 0; index < types.length; index++) {
with(types[index], parameters[index]);
}
}
return this;
}
public MethodInvoker with(Parameter parameter) {
lstParameters.add(parameter);
return this;
}
public Object getParent() {
return parent;
}
public Class getParentClass() {
return parentClass;
}
public String getMethodName() {
return methodName;
}
public Class[] getParameterTypes() {
List<Class> lstTypes = new ArrayList<Class>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getType());
}
return lstTypes.toArray(new Class[lstTypes.size()]);
}
public Object[] getParameterValues() {
List<Object> lstTypes = new ArrayList<Object>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getValue());
}
return lstTypes.toArray(new Object[lstTypes.size()]);
}
public Object execute() {
Object result = null;
Class type = getParentClass();
String methodName = getMethodName();
Class[] lstTypes = getParameterTypes();
Object[] lstValues = getParameterValues();
Object parent = getParent();
try {
Method method = findMethod(type, methodName, lstTypes);
if (method == null) {
throw new NoSuchMethodException(getMethodDescription(type, methodName, lstTypes));
}
method.setAccessible(true);
// logger.info("Method = " + method);
result = method.invoke(parent, lstValues);
} catch (Exception ex) {
StringBuilder sb = new StringBuilder(64);
sb.append("parent = ").append(parent).append("\n");
sb.append("type = ").append(type).append("\n");
sb.append("methodName = ").append(methodName).append("\n");
for (int index = 0; index < lstTypes.length; index++) {
sb.append("[").append(index).append("] ").append(lstTypes[index].getName()).append(lstValues[index]).append("\n");
}
System.err.println("Called by\n" + sb.toString());
throw new InvocationException("Failed to invoke " + methodName, ex);
}
return result;
}
public static Field findField(Class parent, String name) {
Field field = null;
try {
field = parent.getDeclaredField(name);
} catch (NoSuchFieldException noSuchFieldException) {
try {
field = parent.getField(name);
} catch (NoSuchFieldException nsf) {
if (parent.getSuperclass() != null) {
field = findField(parent.getSuperclass(), name);
}
}
}
if (field != null) {
field.setAccessible(true);
}
return field;
}
/**
* This method basically walks the class hierarchy looking for a matching
* method.
*
* The issue is getXMethod only returns a list of the methods for the current
* class and not the inherited methods. This method basically over comes that
* limitation.
*
* If no method can be found matching the criteria, then this method returns a
* null value.
*
* This makes this method not only very powerful, but also very dangerous, as
* it has the power of finding ANY declared method within the hierarchy,
* public, protected or private.
*
* @param parent
* @param name
* @param lstTypes
* @return
*/
public static Method findMethod(Class parent, String name, Class[] lstTypes) {
Method method = null;
try {
method = parent.getDeclaredMethod(name, lstTypes);
} catch (NoSuchMethodException noSuchMethodException) {
try {
method = parent.getMethod(name, lstTypes);
} catch (NoSuchMethodException nsm) {
if (parent.getSuperclass() != null) {
method = findMethod(parent.getSuperclass(), name, lstTypes);
}
}
}
return method;
}
/**
* Builds up a description of the method call in the format of
* [package.class.method([{package.class}{, package.class}{...}])
*
* This is typically used to throw a NoMethodFound exception.
*
* @param clazz
* @param name
* @param lstTypes
* @return
*/
public static String getMethodDescription(Class clazz, String name, Class[] lstTypes) {
StringBuilder sb = new StringBuilder();
sb.append(clazz.getName()).append(".").append(name).append("(");
for (Class type : lstTypes) {
sb.append(type.getName()).append(", ");
}
if (lstTypes.length > 0) {
sb.delete(sb.length() - 2, sb.length());
}
sb.append(")");
return sb.toString();
}
public class Parameter<T> {
private T value;
private Class<T> clazz;
public Parameter(T value, Class<T> clazz) {
this.value = value;
this.clazz = clazz;
}
public T getValue() {
return value;
}
public Class<T> getType() {
return clazz;
}
}
public class InvocationException extends Error {
public InvocationException(String message) {
super(message);
}
public InvocationException(String message, Throwable cause) {
super(message, cause);
}
}
public static boolean hasMethod(Object instance, String methodName) {
return hasMethod(instance.getClass(), methodName);
}
public static boolean hasMethod(Object instance, String methodName, Class[] types) {
return hasMethod(instance.getClass(), methodName, types);
}
public static boolean hasMethod(Class clazz, String methodName) {
return hasMethod(clazz, methodName, new Class[0]);
}
public static boolean hasMethod(Class clazz, String methodName, Class[] types) {
return findMethod(clazz, methodName, types) != null;
}
}
我已经通过代理解决了这样的样板代码。
您的 UI 管理抽象:
public interface UIManager {
void normal();
void internalCall();
void throwException();
}
跟踪实现:
public class UIManagerImpl implements UIManager {
private void traceCall(String method) {
new Exception(Thread.currentThread().getName() + " > " + method).printStackTrace(System.out);
}
@Override
public void normal() {
traceCall("normal");
}
@Override
public void internalCall() {
traceCall("internalCall");
normal();
}
@Override
public void throwException() {
traceCall("throwException");
throw new RuntimeException("doB");
}
}
一个简单的 EDT 感知代理:
public class EdtHandler implements InvocationHandler {
private Object target;
public EdtHandler(Object target) {
this.target = target;
}
public static <T> T newProxy(Class<T> contract, T impl) {
return (T) Proxy.newProxyInstance(impl.getClass().getClassLoader(), new Class<?>[] { contract }, new EdtHandler(impl));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (SwingUtilities.isEventDispatchThread()) {
dispatch(method, args);
} else {
SwingUtilities.invokeLater(() -> dispatch(method, args));
}
return null;
}
protected void dispatch(Method method, Object[] args) {
try {
method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
onError(e);
}
}
protected void onError(Throwable e) {
throw new IllegalStateException(e);
}
}
现在主要检查:
public static void main(String[] args) {
UIManagerImpl impl = new UIManagerImpl();
UIManager edt = EdtHandler.newProxy(UIManager.class, impl);
Runnable block = () -> { System.out.println("---- Start ---- "); edt.normal(); edt.internalCall(); edt.throwException(); System.out.println("---- Stop ---- "); };
block.run();
SwingUtilities.invokeLater(block);
}
实施说明:
- 无论您是否使用美国东部夏令时间,始终拨打
invokeLater
可能会更好。
- 处理程序总是 returns
null
并且更适合 void
方法。
- 您可以轻松实现
return
感知代理:
- 使用 SynchronousQueue
等阻塞机制
- (使用当前实现)引入回调参数(即 Consumer)
在阅读了一些有关 Lambda 的资料后,我得出了以下解决方案。我使用与 SwingUtilites 同名的静态方法创建了一个简单的 class,例如 invokeLater:
public static void invokeLater(Runnable doRun)
{
if(SwingUtilities.isEventDispatchThread())
{
doRun.run();
}
else
{
SwingUtilities.invokeLater(() -> {
doRun.run();
});
}
}
现在,假设新的 class 是 SwingUtilities2(我仍在为新的 class 寻找一个好名字 :-)),将此函数与 Lambda 一起使用非常容易:
SwingUtilities2.invokeLater(() -> {
//here is the code which should run in the EDT
});
如果 class 的 public 函数更新了一些 java swing GUI 元素,我必须检查调用此函数的上下文并确保更新已完成在美国东部时间。这就是我解决它的方法,但由于可能有很多功能我必须做同样的事情(但调用 class 的不同私有方法)或其他 classes 我必须做的一样,我厌倦了总是写同样的代码
public void updateGUI()
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
update();
}
});
}
else
{
update();
}
}
private void update()
{
//update the swing elements here
}
一个可能的解决方案可能是创建一个具有此更新方法的接口(名为 UpdateInterface)。 class 现在将实现此接口,我使用静态方法创建一个助手 class,该方法引用此接口并在 EDT 上下文中调用更新方法:
public static void callUpdateInEDT(UpdateInterface f)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
f.update();
}
});
}
else
{
f.update();
}
}
但这不是很灵活,因为我可能有一些我应该在 EDT 上下文中调用的其他私有方法。是否有任何我迄今为止没有找到的 SwingUtilities 解决方案或任何其他比我提出的更灵活的解决方案?
所以在写了很多包装代码之后,我开始思考,在 inner/anonymous classes 之前,我写了一个基于反射的 API 基本上简化使用任意数量的参数调用任意方法的过程
所以,您的代码会变得更像...
SwingSafeMethodInvoker.invokeLater(this, "update").execute();
这只是一种可能的解决方案(而且我很懒,所以我想减少必须(重新)编写的代码量)
该代码足够智能,可以知道您是否在 EDT 上并采取适当的措施。最棒的是它允许我调用 protected
方法(记住,在 inner/anonymous class 之前,你必须创建一个独立的外部 Runnable
class,所以它不能调用 protected
方法)
API 是建立在附加的反射实用程序之上的,因此有点复杂。
警告 此 API 使用了反射,众所周知反射效率较低,因此您需要知道何时不使用它。它还 "breaks" protected
和 private
适用于 class 的访问限制,所以你可以做一些非常有趣的事情,其中大部分我不鼓励你 trying/doing.
这也不会提取您可能对代码所做的任何重构,因此如果您重命名一个方法,这将看不到它,请小心。
记住:这是我写的 bigger reflection API 的一部分,它允许我在 classes 上执行方法,所以它被故意分解成几个 classes
SwingSafeMethodInvoker
public class SwingSafeMethodInvoker {
public static InvokeLater invokeLater(Object obj, String methodName) {
return new InvokeLater(obj, methodName);
}
public static InvokeLater invokeLater(Class clazz, String methodName) {
return new InvokeLater(clazz, methodName);
}
public static InvokeAndWait invokeAndWait(Object obj, String methodName) {
return new InvokeAndWait(obj, methodName);
}
public static InvokeAndWait invokeAndWait(Class clazz, String methodName) {
return new InvokeAndWait(clazz, methodName);
}
public static InvokeAfter invokeAfter(Object obj, String methodName) {
return new InvokeAfter(obj, methodName);
}
public static InvokeAfter invokeAfter(Class clazz, String methodName) {
return new InvokeAfter(clazz, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Object obj, String methodName) {
return new InvokeAfterAndWait(obj, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Class clazz, String methodName) {
return new InvokeAfterAndWait(clazz, methodName);
}
public static MethodInvoker invoke(Object obj, String methodName) {
return new MethodInvoker(obj, methodName);
}
public static MethodInvoker invoke(Class clazz, String methodName) {
return new MethodInvoker(clazz, methodName);
}
}
InvokeLater
import java.awt.EventQueue;
import javax.swing.SwingUtilities;
/**
* This will make a synchronised call into the EventQueue.
*
* There is no way to determine when the actually call will be made. If you
* need to wait for the result of the call, you are better of using
* InvokeAndWait instead
*
* If the invoke method is called within the ETD, it will be executed
* immediately.
*
* If you want the call to occur later (ie have it placed at the end
* of the EventQueue, use InvokeAfter)
*
* The invoke method will always return null.
* @author shane
*/
public class InvokeLater extends AbstractSwingMethodInvoker {
public InvokeLater(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeLater(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeLater(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
if (EventQueue.isDispatchThread()) {
run();
} else {
SwingUtilities.invokeLater(this);
}
return null;
}
}
InvokeAndWait
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
public class InvokeAndWait extends AbstractSwingMethodInvoker {
public InvokeAndWait(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAndWait(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAndWait(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() {
if (EventQueue.isDispatchThread()) {
run();
} else {
try {
SwingUtilities.invokeAndWait(this);
} catch (InterruptedException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
} catch (InvocationTargetException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
}
}
return getResult();
}
}
InvokeAfter
import javax.swing.SwingUtilities;
/**
* This will place the method call onto the end of the event dispatching
* queue and return immediately.
*
* There is no means to known when the call actually takes and place and if
* you are interested in the return result, you are better of using InvokeAndWait
* @author shane
*/
public class InvokeAfter extends InvokeLater {
public InvokeAfter(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAfter(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAfter(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
SwingUtilities.invokeLater(this);
return null;
}
}
AbstractSwingMethodInvoker
import core.util.MethodInvoker;
public abstract class AbstractSwingMethodInvoker extends MethodInvoker implements Runnable {
private Object result;
public AbstractSwingMethodInvoker(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public AbstractSwingMethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public AbstractSwingMethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public void run() {
result = super.execute();
}
public Object getResult() {
return result;
}
public class SynchronisedDispatcherException extends Error {
public SynchronisedDispatcherException(String message) {
super(message);
}
public SynchronisedDispatcherException(String message, Throwable cause) {
super(message, cause);
}
}
}
MethodInvoker
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Provides a means to invoke any method on any class/object using reflection
*
* @author Shane Whitegead
*/
public class MethodInvoker {
private Object parent;
private Class parentClass;
private String methodName;
private List<Parameter> lstParameters;
public MethodInvoker() {
}
public MethodInvoker(Object parent, Class parentClass, String methodName) {
this.parent = parent;
this.parentClass = parentClass;
this.methodName = methodName;
lstParameters = new ArrayList<Parameter>(5);
}
public MethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public MethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
public static MethodInvoker invoke(Object parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Class parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Object parent, Class clazz, String methodName) {
return new MethodInvoker(parent, clazz, methodName);
}
public MethodInvoker setParent(Object parent) {
this.parent = parent;
if (parent != null) {
setParentClass(parentClass.getClass());
}
return this;
}
public MethodInvoker setParentClass(Class parent) {
this.parentClass = parent;
return this;
}
public MethodInvoker setMethodName(String method) {
this.methodName = method;
return this;
}
public <T> MethodInvoker with(Class<T> type, T value) {
with(new Parameter(value, type));
return this;
}
public MethodInvoker with(Class[] types, Object[] parameters) {
if (types == null || parameters == null) {
} else if (types.length != parameters.length) {
} else {
for (int index = 0; index < types.length; index++) {
with(types[index], parameters[index]);
}
}
return this;
}
public MethodInvoker with(Parameter parameter) {
lstParameters.add(parameter);
return this;
}
public Object getParent() {
return parent;
}
public Class getParentClass() {
return parentClass;
}
public String getMethodName() {
return methodName;
}
public Class[] getParameterTypes() {
List<Class> lstTypes = new ArrayList<Class>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getType());
}
return lstTypes.toArray(new Class[lstTypes.size()]);
}
public Object[] getParameterValues() {
List<Object> lstTypes = new ArrayList<Object>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getValue());
}
return lstTypes.toArray(new Object[lstTypes.size()]);
}
public Object execute() {
Object result = null;
Class type = getParentClass();
String methodName = getMethodName();
Class[] lstTypes = getParameterTypes();
Object[] lstValues = getParameterValues();
Object parent = getParent();
try {
Method method = findMethod(type, methodName, lstTypes);
if (method == null) {
throw new NoSuchMethodException(getMethodDescription(type, methodName, lstTypes));
}
method.setAccessible(true);
// logger.info("Method = " + method);
result = method.invoke(parent, lstValues);
} catch (Exception ex) {
StringBuilder sb = new StringBuilder(64);
sb.append("parent = ").append(parent).append("\n");
sb.append("type = ").append(type).append("\n");
sb.append("methodName = ").append(methodName).append("\n");
for (int index = 0; index < lstTypes.length; index++) {
sb.append("[").append(index).append("] ").append(lstTypes[index].getName()).append(lstValues[index]).append("\n");
}
System.err.println("Called by\n" + sb.toString());
throw new InvocationException("Failed to invoke " + methodName, ex);
}
return result;
}
public static Field findField(Class parent, String name) {
Field field = null;
try {
field = parent.getDeclaredField(name);
} catch (NoSuchFieldException noSuchFieldException) {
try {
field = parent.getField(name);
} catch (NoSuchFieldException nsf) {
if (parent.getSuperclass() != null) {
field = findField(parent.getSuperclass(), name);
}
}
}
if (field != null) {
field.setAccessible(true);
}
return field;
}
/**
* This method basically walks the class hierarchy looking for a matching
* method.
*
* The issue is getXMethod only returns a list of the methods for the current
* class and not the inherited methods. This method basically over comes that
* limitation.
*
* If no method can be found matching the criteria, then this method returns a
* null value.
*
* This makes this method not only very powerful, but also very dangerous, as
* it has the power of finding ANY declared method within the hierarchy,
* public, protected or private.
*
* @param parent
* @param name
* @param lstTypes
* @return
*/
public static Method findMethod(Class parent, String name, Class[] lstTypes) {
Method method = null;
try {
method = parent.getDeclaredMethod(name, lstTypes);
} catch (NoSuchMethodException noSuchMethodException) {
try {
method = parent.getMethod(name, lstTypes);
} catch (NoSuchMethodException nsm) {
if (parent.getSuperclass() != null) {
method = findMethod(parent.getSuperclass(), name, lstTypes);
}
}
}
return method;
}
/**
* Builds up a description of the method call in the format of
* [package.class.method([{package.class}{, package.class}{...}])
*
* This is typically used to throw a NoMethodFound exception.
*
* @param clazz
* @param name
* @param lstTypes
* @return
*/
public static String getMethodDescription(Class clazz, String name, Class[] lstTypes) {
StringBuilder sb = new StringBuilder();
sb.append(clazz.getName()).append(".").append(name).append("(");
for (Class type : lstTypes) {
sb.append(type.getName()).append(", ");
}
if (lstTypes.length > 0) {
sb.delete(sb.length() - 2, sb.length());
}
sb.append(")");
return sb.toString();
}
public class Parameter<T> {
private T value;
private Class<T> clazz;
public Parameter(T value, Class<T> clazz) {
this.value = value;
this.clazz = clazz;
}
public T getValue() {
return value;
}
public Class<T> getType() {
return clazz;
}
}
public class InvocationException extends Error {
public InvocationException(String message) {
super(message);
}
public InvocationException(String message, Throwable cause) {
super(message, cause);
}
}
public static boolean hasMethod(Object instance, String methodName) {
return hasMethod(instance.getClass(), methodName);
}
public static boolean hasMethod(Object instance, String methodName, Class[] types) {
return hasMethod(instance.getClass(), methodName, types);
}
public static boolean hasMethod(Class clazz, String methodName) {
return hasMethod(clazz, methodName, new Class[0]);
}
public static boolean hasMethod(Class clazz, String methodName, Class[] types) {
return findMethod(clazz, methodName, types) != null;
}
}
我已经通过代理解决了这样的样板代码。
您的 UI 管理抽象:
public interface UIManager {
void normal();
void internalCall();
void throwException();
}
跟踪实现:
public class UIManagerImpl implements UIManager {
private void traceCall(String method) {
new Exception(Thread.currentThread().getName() + " > " + method).printStackTrace(System.out);
}
@Override
public void normal() {
traceCall("normal");
}
@Override
public void internalCall() {
traceCall("internalCall");
normal();
}
@Override
public void throwException() {
traceCall("throwException");
throw new RuntimeException("doB");
}
}
一个简单的 EDT 感知代理:
public class EdtHandler implements InvocationHandler {
private Object target;
public EdtHandler(Object target) {
this.target = target;
}
public static <T> T newProxy(Class<T> contract, T impl) {
return (T) Proxy.newProxyInstance(impl.getClass().getClassLoader(), new Class<?>[] { contract }, new EdtHandler(impl));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (SwingUtilities.isEventDispatchThread()) {
dispatch(method, args);
} else {
SwingUtilities.invokeLater(() -> dispatch(method, args));
}
return null;
}
protected void dispatch(Method method, Object[] args) {
try {
method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
onError(e);
}
}
protected void onError(Throwable e) {
throw new IllegalStateException(e);
}
}
现在主要检查:
public static void main(String[] args) {
UIManagerImpl impl = new UIManagerImpl();
UIManager edt = EdtHandler.newProxy(UIManager.class, impl);
Runnable block = () -> { System.out.println("---- Start ---- "); edt.normal(); edt.internalCall(); edt.throwException(); System.out.println("---- Stop ---- "); };
block.run();
SwingUtilities.invokeLater(block);
}
实施说明:
- 无论您是否使用美国东部夏令时间,始终拨打
invokeLater
可能会更好。 - 处理程序总是 returns
null
并且更适合void
方法。 - 您可以轻松实现
return
感知代理:- 使用 SynchronousQueue 等阻塞机制
- (使用当前实现)引入回调参数(即 Consumer)
在阅读了一些有关 Lambda 的资料后,我得出了以下解决方案。我使用与 SwingUtilites 同名的静态方法创建了一个简单的 class,例如 invokeLater:
public static void invokeLater(Runnable doRun)
{
if(SwingUtilities.isEventDispatchThread())
{
doRun.run();
}
else
{
SwingUtilities.invokeLater(() -> {
doRun.run();
});
}
}
现在,假设新的 class 是 SwingUtilities2(我仍在为新的 class 寻找一个好名字 :-)),将此函数与 Lambda 一起使用非常容易:
SwingUtilities2.invokeLater(() -> {
//here is the code which should run in the EDT
});