代码阅读小记(一)
最近在阅读公司内部一套UI 自动化框架的代码,市面上的UI自动框架,八九不离十都是采用WebDriver+TestNG,这个框架也不例外。这篇文章先不讨论整个UI自动化框架的实现,只是记录其中碰到的一些编码知识。看代码的过程中也有一些要吐槽的,比如一个类文件有2k多行,一个类到最上端的基类,经历五次继承。看也有很多值得借鉴的地方
一、 直接访问抽象类中静态方法 - 日志类的实现
之前写代码一般会将日志类写成单例模式,保证只实例化一次。在这次的代码里,作者用了另一种方式,即将日志类定义成抽象类,然后直接访问抽象类中的静态方法。
public abstract class LoggerFactory extends LogFactory{ public static Logger getLogger(Class clazz){ return getLogger(getLog(clazz)); }}
因为静态方法属于类,不需要实例
public class A { protected static Logger logger= LoggerFactory.getLogger(A.class); public void method(){ logger.info("This is a test!"); }}
二、ThreadLocal的使用
ThreadLocal,是Thread Local Variable,即线程的局部变量的意思,从字面理解就是为每一个使用该变量的线程提供一个变量副本。
ThreadLocal提供常用方法:
T get(): 返回线程局部变量中当前线程副本中的值
void remove(): 删除线程局部变量中当前线程副本中的值
void set(T value): 设置此线程局部变量中当前线程副本中的值
线程中的变量只对本线程可见,避免了多线程对同一变量的访问冲突。
场景:有一Servlet调用一个business method,有一需求,每个对Servert的request但要有一个唯一的transaction id, 并将这个id传给business method以便记录日志。有一个解决方法是讲id作为参数传递给business method,但这并不是个good ideal !
这时候就该ThreadLocal上场了。产生id ,并将它保存在ThreadLocal中,这样buisness method就可以直接访问了。
package com.veerasundar; public class Context { private String transactionId = null; /* getters and setters here */ }
MyThreadLocal作为一个容器,来保存Context Object. 同时,注意ThreadLocal是作为静态变量以便调用的。
package com.veerasundar; /** * this class acts as a container to our thread local variables. * @author vsundar * */ public class MyThreadLocal { public static final ThreadLocal<Context> userThreadLocal = new ThreadLocal(); public static void set(Context user) { userThreadLocal.set(user); } public static void unset() { userThreadLocal.remove(); } public static Context get() { return userThreadLocal.get(); } }
package com.veerasundar; public class ThreadLocalDemo extends Thread { public static void main(String args[]) { Thread threadOne = new ThreadLocalDemo(); threadOne.start(); Thread threadTwo = new ThreadLocalDemo(); threadTwo.start(); } @Override public void run() { // sample code to simulate transaction id Context context = new Context(); context.setTransactionId(getName()); // set the context object in thread local to access it somewhere else MyThreadLocal.set(context); /* note that we are not explicitly passing the transaction id */ new BusinessService().businessMethod(); MyThreadLocal.unset(); } }
package com.veerasundar; public class BusinessService { public void businessMethod() { // get the context from thread local Context context = MyThreadLocal.get(); System.out.println(context.getTransactionId()); } }
三、Java Annotations and Java Reflection
在框架中看到了自定义的Annotation,之前对于Annotation的理解也是很模糊的,在网上看到一篇很通俗的文档,翻译过来。
- Java中的Annotations
Annotations,中文字面意思注释,其本身对其所注释的code , 代码逻辑并没有直接影响,annotations提供了一些额外的信息。这些额外的信息可以被其他comonents所用。比如,常见的@Override,当Java编译器看到这个annotation时,它就会去检查@Override所注释的方法有没有真的覆盖接口或父类中的方法。
- 定义自己的Annotations
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface InstallerMethod { }
和接口的定义有点象哦,@Retention定义annotation的适用范围,如RetentionPolicy.ClASS,
注释保留在class file中,但是VM不会处理 。@Target定义被用在,class,fileds,还是methods上
- 通过反射机制来处理自定义Annotations
可以通过java反射机制来处理自定义的Annotations,看例子
package com.vogella.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) public @interface CanRun { }
package com.vogella.annotations; import java.lang.reflect.Method; public class AnnotationRunner { public void method1() { System.out.println("method1"); } @CanRun public void method2() { System.out.println("method2"); } @CanRun public void method3() { System.out.println("method3"); } public void method4() { System.out.println("method4"); } public void method5() { System.out.println("method5"); } }
package com.vogella.annotations; import java.lang.reflect.Method; public class MyTest { public static void main(String[] args) { AnnotationRunner runner = new AnnotationRunner(); Method[] methods = runner.getClass().getMethods(); for (Method method : methods) { CanRun annos = method.getAnnotation(CanRun.class); if (annos != null) { try { method.invoke(runner); } catch (Exception e) { e.printStackTrace(); } } } } }
参考文档:
1. http://www.cnblogs.com/hoojo/archive/2011/05/05/2038111.html
2. http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/
3. http://www.vogella.com/articles/JavaAnnotations/article.html
4. http://hi.baidu.com/firnice/item/a9604f14f4d94e0ad1d66dc8
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?