您的位置:首页 > 编程语言 > Java开发

用Annotation,Future,动态代理实现自己的JAVA方法运行超时应用

2011-04-08 12:50 1251 查看
设置一个方法的执行超时,通常用守护线程或FUTURE接口来实现,但是每次要写一堆来实现,这里用Annotation和动态代理来实现,简化开发。

设计原则:将需要超时的方法标记为@Timeout("5000"),表示该方法执行超过5000MS时,将抛出超时异常。

在系统加载类时,通过指定package或目录扫描类,找出有@Timeout标记的方法,将其类动态代理。在调用该方法时通过代理类来执行达到超时的效果。

本人一共设计了几个类,如下图:



@Timeout,关于Annotatiion的定义以及说明本文不做阐述了。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timeout {

String value() default "2000";
}


1.LocalTimeoutException.java

public class LocalTimeoutException extends RuntimeException{

/**
*
*/
private static final long serialVersionUID = 1L;

public LocalTimeoutException(){
super();
}

}


2.TimeoutBean,用来记录标记了@Timeout的方法以及其所属类对象

import java.lang.reflect.Method;

import com.system.annotation.Timeout;

public class TimeoutBean {

private Timeout timeout;

private Object object;

private Method method;

public TimeoutBean(){

}

public Timeout getTimeout() {
return timeout;
}

public void setTimeout(Timeout timeout) {
this.timeout = timeout;
}

public Object getObject() {
return object;
}

public void setObject(Object object) {
this.object = object;
}

public Method getMethod() {
return method;
}

public void setMethod(Method method) {
this.method = method;
}

}


3.TimeoutFactory.java,用来扫描类和生成代理类

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.system.annotation.Timeout;

public class TimeoutFactory {

//一个类可能有多个@Timeout
private static Map<String,List<TimeoutBean>> map = new HashMap<String,List<TimeoutBean>>();
static String target = "";
static{

search();
}
/**
*  WEB-INF/classes
*/
private static void search(){
String classPath = System.getProperty("java.class.path");
target = classPath.split(";")[0];
File file = new File(target);
getAll(file);

}
/**
* 这里只是本地测试环境,我的是一个WEB工程,所以在/web-inf/classes目录下面去找,各位可以自行设计成其他扫描方式
* @param file
*/
private static void getAll(File file){
if(!file.isDirectory()){
if(file.getName().endsWith("class")){
String pkg = file.getAbsolutePath();

pkg = pkg.replaceAll(target.replaceAll("////", "////////"), "");
pkg = pkg.replaceAll("////|//.//$?class$", ".");
pkg = pkg.replaceAll("^//.|//.$", "");

if(!map.containsKey(pkg)){
createInstance(pkg);
}
}
}else{
File[] files = file.listFiles();
for(File f : files){
getAll(f);
}
}
}

/**
* 查找@timeout方法
* @param pkg
*/
private static void createInstance(String pkg){
try {
Class<?> cls = Class.forName(pkg);
//System.out.println(cls.getName());
Method[] m = cls.getMethods();
List<TimeoutBean> list = new ArrayList<TimeoutBean>();
for(Method method : m){

if(method.isAnnotationPresent(Timeout.class)){
Timeout to = (Timeout) method.getAnnotation(Timeout.class);
TimeoutBean tb = new TimeoutBean();
tb.setTimeout(to);
tb.setObject(cls);
tb.setMethod(method);
list.add(tb);

}
}
if(list.size() > 0){
map.put(pkg, list);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static List<TimeoutBean> getTimeoutBean(String key){
return map.get(key);
}
public static Object get(Class<?> clazz){
Object obj = null;
TimeoutHandler th =  new TimeoutHandler();
try {
obj = Class.forName(clazz.getCanonicalName()).newInstance();
th.setTarget(obj);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return Proxy.newProxyInstance(
TimeoutTest.class.getClassLoader(),
TimeoutTest.class.getInterfaces(),
th);
}

}


4.TimeoutHandler.java,实现Future接口(java.util.concurrent)功能,也就是超时

public class TimeoutHandler implements InvocationHandler {

private Object target;

private TimeoutBean timeoutBean;

public Object getTarget() {
return target;
}

public void setTarget(Object target) {
this.target = target;
}

@Override
public Object invoke(Object arg0, Method method, Object[] arg2)
throws Throwable {
// TODO Auto-generated method stub

timeoutBean = get(method);
Object obj = null;

if(timeoutBean != null){
obj = execute(target,arg2,method);
}else{
obj = method.invoke(target, arg2);
}
return obj;
}

/**
* 根据METHOD获取 对应的 TIMEOUTBEAN
* @param method
* @return
*/
private TimeoutBean get(Method method){
List<TimeoutBean> list = TimeoutFactory.getTimeoutBean(target.getClass().getName());

for(TimeoutBean tb : list){
if(methodEquals(tb.getMethod(),method)){
return tb;
}
}
return null;
}
private boolean methodEquals(Method m1,Method m2){
if(!m1.getName().equals(m2.getName())){
return false;
}
Class<?>[] cls1 = m1.getParameterTypes();
Class<?>[] cls2 = m1.getParameterTypes();
if(cls1.length != cls2.length){
return false;
}
int len = cls1.length;
//防止多态

for(int i = 0; i < len; i++){
Class<?> clazz1 = cls1[i];
Class<?> clazz2 = cls2[i];
if(!clazz1.getName().equals(clazz2.getName())){
return false;
}
}
return true;
}

private Object execute(Object target,Object[] args,Method method)
throws Throwable{
Object ret = null;
TimeoutCallable tc = new TimeoutCallable();
tc.setArgs(args);
tc.setMethod(method);
tc.setTarget(target);
ExecutorService executor = Executors.newSingleThreadExecutor();

FutureTask<Object> future = new FutureTask<Object>(tc);

executor.execute(future);
try {
ret = future.get(Integer.parseInt(timeoutBean.getTimeout().value()), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
future.cancel(true);
throw new LocalTimeoutException();
} catch (ExecutionException e) {
future.cancel(true);
throw new LocalTimeoutException();
} catch (TimeoutException e) {
future.cancel(true);
throw new LocalTimeoutException();
} finally {
executor.shutdown();
}

return ret;
}
}


5.TimeoutCallable.java,Future接口所学的Callable

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

public class TimeoutCallable implements Callable<Object> {

private Object[] args;

private Method method;

private Object target;

public Object[] getArgs() {
return args;
}

public void setArgs(Object[] args) {
this.args = args;
}

public Method getMethod() {
return method;
}

public void setMethod(Method method) {
this.method = method;
}

public Object getTarget() {
return target;
}

public void setTarget(Object target) {
this.target = target;
}

@Override
public Object call() throws Exception {
// TODO Auto-generated method stub
return method.invoke(target, args);
}

}


6.TimeoutTest.java,效果测试类与接口类

import com.system.annotation.Timeout;

public class TimeoutTest implements ITimeout {

public TimeoutTest() {

}

@Timeout("2000")
public void execute() throws Throwable {

System.out.println("execute");

Thread.sleep(3000);

System.out.println("complete execute");
}

@Override
@Timeout("2000")
public void test() throws Throwable {
// TODO Auto-generated method stub
System.out.println("test");

Thread.sleep(1000);

System.out.println("complete test");
}

@Override
@Timeout("2000")
public void connect(){
// TODO Auto-generated method stub
System.out.println("connect");
try{
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("complete connect");
}

public void print(){
System.out.println("normal method print");
}
}

//接口
public interface ITimeout {
public void execute() throws Throwable;

public void test() throws Throwable;

public void connect() ;

public void print();
}


调用演示:仔细观察几个方法,你会发现其输出的结果又所不同。

如:execute()方法在超时后就不会执行后面的语句,而connect()方法即使超时后还是会执行后面的语句,并且会抛出InterruptedException,而execute()方法只会抛出LocalTimeoutException.

print()是普通方法。test()方法则不会抛出任何异常。

通常采用execute()方法这种写法就可以达到我们想要的效果了。。。

public static void main(String...args) throws Throwable{
ITimeout it = (ITimeout) TimeoutFactory.get(TimeoutTest.class);
it.print();
it.test();
it.connect();
it.execute();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: