jdk动态代理,统计某个方法的执行时间
来源:互联网 发布:php程序员简历范文 编辑:程序博客网 时间:2024/05/29 12:59
1、需求
统计某个方法的执行时间,写个demo模拟三层结构,dao层,service层,action层,比如要统计UserServiceImpl里面的getUser方法执行了多长时间,代码可能会写成这样:
public void testTime(){ long startTime = System.currentTimeMillis(); UserService service = new UserSrviceImpl(); service.getUser(); System.out.println("耗时"+(System.currentTimeMillis()-startTime)); }
但是,我们要求,service层,只关心业务上的东西,类似统计执行时间,打日志之类的不想该层里面写。JDK的动态代理可以实现这样的需求、
2、实现思路
1.扫描指定包名下的所有class.
2.写个Component注解,可以过滤一些不符合jdk动态代理要求的class
3.写个Statistics注解,只有标记了这个注解的方法才会被统计。
4.把所有创建好的代理对象存放起来。(模拟Spring ApplicationContext),通过getBean方法获得.
5.调用代理对象的时候,如果对象上的方法被标记了Statistics,同时注解的值为true,则会进行统计。
3、扫描所有类
3.1、在网上找到的一段代码,出处链接找不到了。通过这个方法,可以获得指定包的所有class的全路径
/** * 从包package中获取所有的Class * @param pack * @return */ public static List<Class<?>> getClasses(String packageName){ //第一个class类的集合 List<Class<?>> classes = new ArrayList<Class<?>>(); //是否循环迭代 boolean recursive = true; //获取包的名字 并进行替换 String packageDirName = packageName.replace('.', '/'); //定义一个枚举的集合 并进行循环来处理这个目录下的things Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); //循环迭代下去 while (dirs.hasMoreElements()){ //获取下一个元素 URL url = dirs.nextElement(); //得到协议的名称 String protocol = url.getProtocol(); //如果是以文件的形式保存在服务器上 if ("file".equals(protocol)) { //获取包的物理路径 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); //以文件的方式扫描整个包下的文件 并添加到集合中 findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)){ //如果是jar包文件 //定义一个JarFile JarFile jar; try { //获取jar jar = ((JarURLConnection) url.openConnection()).getJarFile(); //从此jar包 得到一个枚举类 Enumeration<JarEntry> entries = jar.entries(); //同样的进行循环迭代 while (entries.hasMoreElements()) { //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 JarEntry entry = entries.nextElement(); String name = entry.getName(); //如果是以/开头的 if (name.charAt(0) == '/') { //获取后面的字符串 name = name.substring(1); } //如果前半部分和定义的包名相同 if (name.startsWith(packageDirName)) { int idx = name.lastIndexOf('/'); //如果以"/"结尾 是一个包 if (idx != -1) { //获取包名 把"/"替换成"." packageName = name.substring(0, idx).replace('/', '.'); } //如果可以迭代下去 并且是一个包 if ((idx != -1) || recursive){ //如果是一个.class文件 而且不是目录 if (name.endsWith(".class") && !entry.isDirectory()) { //去掉后面的".class" 获取真正的类名 String className = name.substring(packageName.length() + 1, name.length() - 6); try { //添加到classes classes.add(Class.forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } } catch (IOException e) { e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; }
3.2、编写Component注解
注解需要作用在类上面,所以Target需要ElementType.TYPE
package com.junjiex.action.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Component {}
3.3、编写Statistics注解
这个注解需要作用在方法上,所以是@Target(ElementType.METHOD)
package com.junjiex.action.annotation;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 Statistics { boolean count() default false;}
3.4、模拟Spring,编写ApplicationContext
这里模拟一下Spring的调用方法,当然,Spring没有这么简单.
package com.junjiex.action.aspect;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.List;import java.util.Map;import com.junjiex.action.annotation.Component;import com.junjiex.utils.ClassUtil;public class ApplicationContext { private static ApplicationContext instance = null; private Map<String,Object> objsMap = null; public static ApplicationContext getInstance() throws InstantiationException, IllegalAccessException{ if(instance==null){ synchronized (ApplicationContext.class) { if(instance == null){ instance = new ApplicationContext(); } } } return instance; } private ApplicationContext() throws InstantiationException, IllegalAccessException{ objsMap = new HashMap<String, Object>(); init(); } public void init() throws InstantiationException, IllegalAccessException{ //查找com.junjiex这个包和子包里面的方法 List<Class<?>> classes = ClassUtil.getClasses("com.junjiex"); for (Class<?> clas :classes) { //只有Component的类才放到bean缓存里 if(clas.isAnnotationPresent(Component.class)){ if(!clas.isAnnotation() && !clas.isInterface() && !clas.isEnum()){ Object instance = clas.newInstance(); Object obj = Proxy.newProxyInstance(instance.getClass().getClassLoader(), instance.getClass().getInterfaces(), new AspectHandler(instance)); objsMap.put(instance.getClass().getSimpleName(),obj); } } } } public Object getBean(String className){ if(!objsMap.containsKey(className)){ throw new RuntimeException("the bean not found!"); } return objsMap.get(className); }}
在init方法里面通过ClassUtil.getClasses(“com.junjiex”);查询这个包下面的所有class ,getClass的具体实现在前面有。
然后遍历拿到的所有class,通过if(clas.isAnnotationPresent(Component.class))来过滤,只有Component注解的类才调用代理类去处理。而代理类的处理过程在AspectHandler里面。
3.5、AspectHandler的实现
package com.junjiex.action.aspect;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import com.junjiex.utils.AnnotationUtil;public class AspectHandler implements InvocationHandler { //被代理的目标对象 private Object target; public AspectHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method loggerMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes()); if(AnnotationUtil.isAnnotation(loggerMethod)){ long startTime = System.currentTimeMillis(); Object ret = method.invoke(target, args); System.out.println(String.format("方法%s执行的时间%d", method.getName(),System.currentTimeMillis()-startTime)); return ret; } return method.invoke(target, args); }}
在invoke方法里,首先会判断,method是否有Statistics注解,并且count为true,如果成立,则才会进行时间统计,
AnnotationUtil.isAnnotation(loggerMethod)是用于判断是否符合这些要求的,符合,则会进行统计:
long startTime = System.currentTimeMillis(); Object ret = method.invoke(target, args); System.out.println(String.format("方法%s执行的时间%d", method.getName(),System.currentTimeMillis()-startTime));
3.6、AnnotationUtil的实现
package com.junjiex.utils;import java.lang.annotation.Annotation;import java.lang.reflect.Method;import com.junjiex.action.annotation.Statistics;public class AnnotationUtil { public static boolean isAnnotation(Method method) { if (method.isAnnotationPresent(Statistics.class)) { Annotation anot = method.getAnnotation(Statistics.class); Statistics log = (Statistics) anot; if (log.count()) { return true; } } return false; }}
4.测试使用
UserSrviceImpl 标记上了Component,getUser方法标记上了@Statistics(count=true),则getUser方法会执行统计。
package com.junjiex.dao.service.impl;import java.util.List;import com.junjiex.action.annotation.Component;import com.junjiex.action.annotation.Statistics;import com.junjiex.bean.User;import com.junjiex.dao.UserDao;import com.junjiex.dao.impl.UserDaoImpl;import com.junjiex.dao.service.UserService;@Componentpublic class UserSrviceImpl implements UserService{ private UserDao userDao = new UserDaoImpl(); @Statistics(count=true) @Override public User getUser() { try { //执行太快,延时一下 Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } return userDao.getUser(); } @Override public List<User> listUser() { return userDao.listUser(); } @Override public void deleteUser(User user) { userDao.deleteUser(user); } @Override public void updateUser(User user) { userDao.updateUser(user); }}
单元测试,测试两个方法,一个有统计信息输出,一个没有。而有统计输出的getUser仅需要配置一个@Statistics(count=true)
@org.junit.Test public void testProx() throws InstantiationException, IllegalAccessException{ UserService service = (UserService) ApplicationContext.getInstance().getBean("UserSrviceImpl"); service.getUser(); service.listUser(); }
输出结果:
- jdk动态代理,统计某个方法的执行时间
- c++执行时间的统计方法
- JDK的动态代理
- JDK的动态代理
- JDK的动态代理
- JDK的动态代理
- JDK的动态代理
- Jdk的动态代理
- C++执行时间统计的一种方法
- jdk动态代理拦截方法
- JDK的动态代理实现调用拦截器中的方法
- unity获取某个方法执行时间
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理原理
- JDK的动态代理机制
- JDK的动态代理机制
- JDK的动态代理机制
- Java Collection
- 如何在“运行”里打开软件
- VB百例(一)
- 批处理文件
- VB百例(二)
- jdk动态代理,统计某个方法的执行时间
- VB中spc(n)函数、space(n)函数和tab(n)函数区别
- 浏览器内核、渲染引擎、js引擎
- single和double
- 在VB中如何把开始和暂停放在同一个按钮里
- 半年总结
- 区块链的四类应用场景
- 陈伟视频总结
- 系统关系