Java Annotation - 王胜
Annotation是什么
维基百科: A form of syntactic metadaa that can be added to Java source code. 也就是说,Annotation的引入是为了从Java语言层面上,为Java源代码提供元数据的支持。参见维基百科
Annotation的用途
表象,替代之前JDK1.4开发中,大量繁琐的配置项,Annotation的出现其实可以极大简化配置文件的数量和需要关注配置的内容。但其实,注解带来的益处远不至于此。
Annotation的分类
文档标注型
主要是@Documented,用以标注是否在javadoc中
编译检查型
主要在编译过程中,给Java编译器若干指令,检查Java代码中是否存在若干问题, 改变编译器的动作或者行为,通过Annotation的使用,可以调整和控制编译器的使用以及让编译器提供关于代码的更多的检查和验证。主要有:@Override,@SuppressWarning
代码分析型
此类Annotation是在我们开发中使用最多的,主要是通过Annotation提供给代码更多的额外特性和设置,在Java运行过程中发挥作用。常见的是在Spring或者Hibernate等框架中出现的@Controller,@Service,@Bean, @Table, @Enitty等等.
自定义生命周期为Runtime类型Annotation
Runtime的处理主要依赖于反射的接口,在字节码中寻找Annotation的接口和输入参数,提取其内容和数值。大部分的情况下是基于代理模式,动态生成相应的代理类实例,然后通过代理类,调用相应的InvocationHandler,在InvocationHandler之内完成Annotation所要执行的动作;然后再继续调用原来的方法,继续执行。
用户在定义Runtime类型的Annotation时,需要的步骤:
- 定义Annotation
- 定义Annotation的处理器类『通过Reflect、InvocationHandler、Proxy.newProxyInstance()来处理具体的逻辑』
- 选择合适的调用Annotation的时机和切入点。
示例
自定义一个@OnClick注解,通过在方法前面加入这个注解,从而为指定的组件动态添加事件监听方法。
定义OnClick注解
1
2
3
4
5@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
String value();
}定义OnClickProxyFactory来处理动态绑定方法的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55public class OnClickProxyFactory {
/**
* 处理OnClick注解
*
* @param target 包含OnClick注解的目标对象
*/
public static void handleOnClickAnnotation(Object target) {
try {
Class<?> clsTarget = target.getClass();
// 检索目标对象的所有方法, 如果含有OnClick注解, 则为注解中声明的属性对象动态添加AddActionListener方法
for (Method method : clsTarget.getDeclaredMethods()) {
OnClick onClickAnnotation = method.getAnnotation(OnClick.class);
if (onClickAnnotation != null) {
Field field = clsTarget.getDeclaredField(onClickAnnotation.value());
field.setAccessible(true);
autoAddActionlistener(field.get(target), getActionListenerProxy(target, method));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 为目标对象添加addActionListener方法, 该方法的参数ActionListener为ActionListener代理对象
* @param target 被添加ActionListener方法的事件源对象
* @param actionListenerProxy ActionListener代理对象
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private static void autoAddActionlistener(Object target, Object actionListenerProxy) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 获取JButton的addActionListener方法对象
Method methodAddActionListener = target.getClass().getMethod("addActionListener", ActionListener.class);
// 为JButton对象添加addActionListener方法
methodAddActionListener.invoke(target, actionListenerProxy);
}
/**
* 获取ActionListener的代理对象
* @param target 指定的代理对象
* @param targetMethod 指定的代理对象的方法
* @return
*/
public static Object getActionListenerProxy(final Object target, final Method targetMethod) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return targetMethod.invoke(target);
}
};
return Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[] {ActionListener.class}, handler);
}
}在UI初始化时,调用注解的解析和动态处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class UI extends JFrame{
// 省略......
public UI() {
// 省略......
// 通过OnClickProxyFactory工厂来处理OnClick注解
OnClickProxyFactory.handleOnClickAnnotation(this);
}
@OnClick("btnBlue")
public void setBlueBackground() {
panel.setBackground(Color.BLUE);
}
@OnClick("btnRed")
public void setRedBackground() {
panel.setBackground(Color.RED);
}
@OnClick("btnSayHello")
public void sayHello() {
JOptionPane.showConfirmDialog(null, "Hello World!", "Tips", JOptionPane.YES_OPTION);
}
具体的示例源码,移步 Github-helloJavaSE
参考
Xcode MarkDown的代码文档 ( for swift )
@(iOS)[Markdown, Document]
markdown在swift中的应用
goals
- 描述各个属性、函数和类的真正用途
- 高亮函数的输入和输出(参数和返回值)
- 几个月后还能清晰地记得每个函数属性是为了什么
- 使用工具制作具有专业外观的使用手册(比如:使用 Jazzy)
- Xcode 里写的代码文档能被预览
markdown grammar
1 | #text#:文本标题 |
注释区域: 3 个斜线(///)或以下面的形式开头:1
2
3/**
*/
Case
1 | /** |
关键词
- Parameter
- Returns
- Remark
- SeeAlso
- Precondiction
- Requires
- Todo
- Version
- Author
- Note
1 | /** |
Jazzy 自动产生代码文档
Jazzy 是一款可以为 Swift 和 Objective-C 代码产生具有 Apple 风格的代码文档工具。
效果如下
下面以Alamofire
为例子:
jazzy —help 查看帮助
- cd Alamofire 的项目path
- jazzy —output /Users/xcodeyang/Desktop/jazzy_document
Android App共享文件Uri不能为file:// - 吴明
- 先看异常信息:
1 | E/StrictMode: null |
在StrictMode(严格)模式下,App之间共享资源报的异常。
- 出现这个异常的代码
1 | public void openFile(@NonNull Activity activity, @NonNull File file) { |
打印
1 | Uri.fromFile(file) |
输出信息
1 | Uri uri=file:///storage/emulated/0/download/9text.jpg |
- 各方说明
- Android N中说明Uri不能以file://的原因
- 传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。
- 访问权限控制
- Android N中说明Uri不能以file://的原因
- 解决办法
总结
开始搜索Uri.fromFile(file)并分析源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* Creates a Uri from a file. The URI has the form
* "file://<absolute path>". Encodes path characters with the exception of
* '/'.
*
* <p>Example: "file:///tmp/android.txt"
*
* @throws NullPointerException if file is null
* @return a Uri for the given file
*/
public static Uri fromFile(File file) {
if (file == null) {
throw new NullPointerException("file");
}
PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
return new HierarchicalUri(
"file", Part.EMPTY, path, Part.NULL, Part.NULL);
}没有找到相关解决方案。
- StrictMode的FileUriExposedException异常检测)中有提到FileProvider的使用
- 我写的一个demo
- 给以后提醒app之间共享数据传Uri不能传file://
参考资料
谓词 - 刘康
- NSPredicate是Foundation框架中的一个类。
- 作用:指定数据被获取和过滤的方式。提供了类似于自然语言一样定义一个集合被搜寻的逻辑条件。
为了证明NSPredicate的强大功能,先写一个Person类,做准备工作。
1 | class Person: NSObject { |
基本使用
找出年龄为24岁的人:
1 | let predicateByAge = NSPredicate(format: "age == 24") |
参数可以传入:
1 | let age = NSNumber(int: 25) |
也可传入要对比的属性,这里是age. 属性的key:
1 | let pridicateByAge1 = NSPredicate(format: "%K == %@", "age", NSNumber(int: 36)) |
指定通配的变量,这里用24来替代age:
1 | let pridicateByAge2 = NSPredicate (format: "age == $age") |
语法小结
- 使用
%@
对应数字,字符串,日期的替代值 - 使用
%K
对应要比较的属性,也就是KVC中的key - 使用$变量名来表示通配的变量,然后
predicateWithSubstitutionVariables
来决定具体的变量值
基本比较
找出年龄大于40岁的同事:
1 | let predicateAgeOver40 = NSPredicate(format: "age > 40") |
找出年龄在22岁~35岁的人:
1 | let minAge = NSNumber(int: 22) |
语法小结
>
大于>=
大于等于<
小于<=
小于等于==
等于!=
或者<>
不等于BETWEEN
介于两者之间,包括上下限
复合比较
&&
或者AND
逻辑与||
或者OR
逻辑或!
或者NOT
逻辑非
1 | let predicateByCompare = NSPredicate(format: "age < 30 OR age >= 40") |
字符串比较
BEGINSWITH
左边表达式以右边表达式开头CONTAINS
左边表达式包含右边表达式ENDSWITH
左边表达式以右边表达式结尾LIKE
左边表达式和右边表达式相似(简单的正则表达式匹配,?匹配一个字符,*匹配0个或者多个字符)MATCHES
可以实现较为复杂的正则表达式匹配- 用方括号加
cd
来不区分大小写和变音符号 IN
左边的表达式在右边的集合里
找出名字以“A”开头的同事:
1 | let pridivateByName1 = NSPredicate(format: "name BEGINSWITH %@", "A") |
名字里包含in,不区分大小写,并且年龄大于等于24:
1 | let pridivateByName2 = NSPredicate(format: "name CONTAINS %@ && age >= %@", "in", NSNumber(int: 24)) |
复合正则表达式T[a-z]*k
:
1 | let privatedivateByName3 = NSPredicate(format: "name MATCHES 'T[a-z]*k'") |
名字是两者中的一个:
1 | let privatedivateByName4 = NSPredicate(format: "name IN {'Bella', 'Jack Tomphon'}") |
基于Block的谓词
基于Block能够灵活的定制谓词,这里简单的Block定义age > 24
:
1 | let blockPredicate = NSPredicate { (person: AnyObject!, _) -> Bool in |