- 浏览: 197792 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (121)
- java_基础 (26)
- java_web (15)
- javascript (9)
- extJS (0)
- dwr (0)
- guobin_ETL (1)
- guobin_ssh (17)
- guobin_webservice (1)
- guobin_jbpm (0)
- guobin_jquery (0)
- guobin_acegi (1)
- guobin_poi/jxl (2)
- guobin_multiThread (0)
- guobin_名称解释 (0)
- guobin_FLEX (0)
- guobin_php (0)
- guobin_ORACLE (1)
- guobin_mysql (0)
- guobin_linux (0)
- guobin_powerDesigner (0)
- guobin_visol (0)
- guobin_ER/STUDIO (0)
- guobin_jmesa (0)
- guobin_weblogic (0)
- guobin_kettle (0)
- guobin_一路风雨 (5)
- guobin_spark (0)
- java_xml (7)
- guobin_规则引擎 (0)
- 插件开发 (2)
- 软件管理 (3)
- spring mvc (2)
- java_jni (1)
- eclipse (1)
- oracle (0)
- 项目学习笔记 (1)
- java多线程学习记录 (9)
- websphere性能设置和日常维护 (2)
- (1)
- 系统软件架构设计 (1)
- ces (1)
- 需求分析 (2)
- IBM-CICS GATEWAY (1)
- 工具使用 (1)
- 网络信息安全编程 (1)
最新评论
-
yzh__:
学习一个
Hibernate 自关联注解方式 -
xiaoyahuang:
"2)将Sysdeo Plugin下的DevLoad ...
eclipse项目引用 -
guobin6125:
guobin6125 写道sfasshahhah评论的评论
欢迎使用Kit! -
guobin6125:
sfass
欢迎使用Kit! -
guobin6125:
tst23
欢迎使用Kit!
背景
最近在为公司的技术改造做准备,我设计了一个提高 Web 开发效率的技术框架,为了增加框架的友好性和易用性,决定采用注解来代替配置文件,于是我查询了很多的资料,进行整理和学习。
² 概念
注解是 JDK5 引入的新特性,最初衍生自代码注释,但现在早已经超出了注释的范畴,以至于我很惶恐,不敢使用注释这个词汇来描述他,尽管现有的很多资料里仍然称其为注释。如果说反射使得很多技术实现(动态代理、依赖注入等)有了基础,那么注解就是使这些技术实现变得平民化的基础。
从 class 文件规范中可以看出, JDK5 开始, class 文件已经引入了注解描述片段。站在 java 虚拟机的角度来看, class 保留和运行时保留的注解已经和 java 二进制码放在了同等的地位。虚拟机在加载 class 文件时,会为注解内容分配空间并进行解析,最终还会为注解和对应的二进制码建立关联。尽管这些注解不会被运行,但其对代码的说明能力,结合反射技术已经足够我们做太多的事情。
我们知道, java 除了内置的注解( @Override 、 @Deprecated 等)以外,还支持自定义注解( Struts 、Hibernate 等很多框架甚至 java 自身都实现了很多自定义注解)。当然,更为厉害的是元注解,元注解是用来描述注解的注解(光听着就觉得厉害了吧)。
要实现一个自定义注解,必须通过 @interface 关键字来定义。且在 @interface 之前,需要通过元注解来描述该注解的使用范围( @Target )、生命周期( @Retention )及其他(其他的不重要,所以领盒饭了)。
@Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方),其取值有:
取值 |
描述 |
CONSTRUCTOR |
用于描述构造器(领盒饭)。 |
FIELD |
用于描述域(领盒饭)。 |
LOCAL_VARIABLE |
用于描述局部变量(领盒饭)。 |
METHOD |
用于描述方法。 |
PACKAGE |
用于描述包(领盒饭)。 |
PARAMETER |
用于描述参数。 |
TYPE |
用于描述类或接口(甚至 enum )。 |
@Retention 用于描述注解的生命周期(即:被描述的注解在什么范围内有效),其取值有:
取值 |
描述 |
SOURCE |
在源文件中有效(即源文件保留,领盒饭)。 |
CLASS |
在 class 文件中有效(即 class 保留,领盒饭)。 |
RUNTIME |
在运行时有效(即运行时保留)。 |
根据上述介绍,如果我需要定义一个用于对方法进行描述,且能在运行时可以读取到的自定义注解(假定我希望这个注解的名字是 Sample )。那么,我就应该这样:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Sample {
public String value() default "";
}
OK ,自定义注解已经写好了,那我们就可以在代码中使用这个注解了,如:
@Sample(value="I'm here.")
public void anyName() {
... ...
}
值得一提的是,在网上能搜索到的资料(中文的)几乎都是到此为止了。给人的感觉就像看美国大片,每到结束的时候总会给你一种未完待续的意味。事实上,我能容忍电影给我这样的感觉,因为这样会让我充满期待。而从技术的角度来说,我很厌恶这种感觉。
事实上,事情远没有结束。如果自定义注解以这样的形式存在,那么这种存在是没有任何实际意义的。
那么,我们接下来该做什么呢?
接下来我们应该编写自己的注解处理器。
嗯,再啰嗦一下,提到注解处理器,我又被 N 多资料误导了。很多资料都提到 APT ,或者AbstractProcessor 。但事实上,我的理解是 APT 或者 AbstractProcessor 更多的用于:在非运行时进行增强处理(如:分析逻辑 BUG ,分析代码结构等等)。
回到注解处理器,注释处理器其实就是一段用于解释或处理自定义注解的代码而已,没有太多复杂的概念或者技术(嗯,先卖个关子,后面的实例会细说注解处理器的)。
² 实践
通过前文对自定义注解的了解,我猜想我应该这样做:
1. 结合实际需求规划注解的功能,以及定义如何解析注解
先说说我的需求吧:框架会把页面划分成 N 个分块,而每个分块都需要不同的类来处理输出内容,处理到不同的分块是,框架会自动创建对应的类实例(目前为止,没有任何问题)。接下来的问题就来了,每个分块处理类处理分块内容时,所需要的参数是不一样的(参数类型以及参数个数都不一样);因此,也不好定义一个固定的接口。当然,肯定有人会说可以把参数改成 map ,或 Object 数组。是的,这是一种解决办法,但是如果我用自定义注解,会不会能更好的完成这项工作呢?是的,答案在你我心中。
我们不妨设想一下:
如果处理类需要获取参数,那么这个处理类就给我注解某个方法(方法名任意,前文提到过:虚拟机会做好二者之间的关联),以说明该方法需要被框架预先调用一次(类似初始化方法)。同样的道理,在注解这个方法时,加入所需要的参数注解。
然后,在框架的处理程序中,我们先根据注解查找方法,如果该方法存在,则再次根据注解把对应的参数准备好,然后反射调用 invoke 方法。
OK ,这样的设想应该是行得通的。
2. 定义并构造自定义注解
前文提到了我们需要对方法进行注解,而且注解中还需要包含参数信息。好吧,我的设想是定义两个注解:
@RenderParameter 用于描述方法的参数,包括参数类型、参数来源等。
@RenderMethod 用于描述方法(主要描述方法的参数列表)。
这里要提到一个小技巧:即注解可以使用数组(嗯嗯,待会会看到的)。
先来定义一下 @RenderParameter 吧:
… …
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target({ElementType.METHOD}) // 注解对象为方法
public @interface RenderParameter {
// 参数类型
public enum ParameterType { STRING, SHORT, INT, BOOL, LONG, OBJECT };
// 参数值的来源
public enum ScopeType { NORMAL, SESSION, COOKIE, ATTRIBUTE, CUSTOM };
public String name(); // 参数名
public boolean ignoreCase() default false; // 匹配时是否忽略大小写
public ParameterType type() default ParameterType.STRING; // 参数类型
public ScopeType scope() default ScopeType.NORMAL; // 参数值来源
}
再看看 @RenderMethod 的定义:
… …
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target({ElementType.METHOD}) // 注解对象为方法
public @interface RenderMethod {
public enum MethodType { INQUIRE };
public MethodType method() default MethodType.INQUIRE;
public RenderParameter[] parameters(); // 参数列表
}
至此,两个自定义注解已经完成,看看我应该如何使用他们:
@RenderMethod(parameters={@RenderParameter(name="logined", scope=ScopeType.SESSION),@RenderParameter(name="loginedUser", scope=ScopeType.SESSION)})
public void inquire(String logined, String loginedUser) {
if("true".equals(logined)) {
write(loginedUser + " is logined.");
} else {
write("No user logined.");
}
}
3. 构造自定义注解的处理方法(即注解处理器)
终于又说到注解处理器了,其实很简单:
… …
// 此处的 renderer 就是采用了自定义注解的类实例
for(Method method : renderer.getClass().getDeclaredMethods()) {
RenderMethod rm = (RenderMethod)method.getAnnotation(RenderMethod.class);
if(rm != null) {
int length = rm.parameters().length;
Object[] parameters = length > 0 ? buildParameters(rm.parameters()) : null;
try {
method.invoke(renderer, parameters);
} catch (IllegalArgumentException e) {
log.error(e.getMessage());
} catch (IllegalAccessException e) {
log.error(e.getMessage());
} catch (InvocationTargetException e) {
log.error(e.getMessage());
}
break;
}
}
… …
// 根据注解数组创建参数对象列表,供 invoke 使用
private Object[] buildParameters(RenderParameter[] parameters) {
Object[] objs = new Object[parameters.length];
int i = 0;
for(RenderParameter parameter : parameters) {
ScopeType scope = parameter.scope();
// 参数值来自 request.getParameter
if(scope == ScopeType.NORMAL) {
String temp = request.getParameter(parameter.name());
String value = null;
if(temp != null && !"".equals(temp)) {
try {
byte[] bytes = temp.getBytes("iso-8859-1");
value = new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage());
}
}
objs[i ++] = value;
// 参数值来自 Session
} else if(scope == ScopeType.SESSION) {
objs[i ++] = request.getSession().getAttribute(parameter.name());
// 参数值来自 Cookie
} else if(scope == ScopeType.COOKIE) {
for(Cookie cookie : request.getCookies()) {
if(cookie.getName().equals(parameter.name())) {
objs[i ++] = cookie.getValue();
break;
}
}
// 参数值来自 request. getAttribute
} else if(scope == ScopeType.ATTRIBUTE) {
objs[i ++] = request.getAttribute(parameter.name());
}
}
return objs;
}
下面例子为注解生成sql 语言例子
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Tractate {
/*
* 语言国家化表后缀
* <p>在当前表名的基础上加上此后缀</p>
* <p>例如:在产品表为PRODUCT 那么语言表为PRODUCT_ML</p>
*/
public String value() default "ML";
}
/**
* 定义国际化查询字段
* @author 张森
*/
@Target(value = {ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TractateField {
}
/**
* 定义国际化查询语言
* @author 张森
*/
@Documented
@Target(value = {ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TractateLocale {
}
/**
* 欲查询表别名
*/
private String tableAliax = "ATN";
/**
* 语言表别名
*/
private String textAliax = "ETN";
private SessionFactory sessionFactory;
private String createMutiQuery(Object entity) {
//查询SQL语句
StringBuilder sql = new StringBuilder();
try {
boolean isFristColumn = true;
sql.append("SELECT ");
//当前实体对应表名
String tableName = null;
//定义国际化表名前缀
String suffix = null;
Locale locale = null;
Table t = (Table) entity.getClass().getAnnotation(Table.class);
tableName = t.name();
Tractate tp = (Tractate) entity.getClass().getAnnotation(Tractate.class);
if (tp != null) {
suffix = tp.value();
}
for (Method method : entity.getClass().getDeclaredMethods()) {
//如果有语言国际化
if (suffix != null) {
//获取语言值
TractateLocale tl = (TractateLocale) method.getAnnotation(TractateLocale.class);
if (tl != null) {
locale = (Locale) method.invoke(entity);
}
}
//获取字段
Column c = (Column) method.getAnnotation(Column.class);
if (c != null) {
if (!isFristColumn) {
sql.append(",");
} else {
isFristColumn = false;
}
//如果有语言国际化
if (suffix != null) {
//获取文本
TractateField tf = (TractateField) method.getAnnotation(TractateField.class);
if (tf != null) {
sql.append(textAliax);
} else {
sql.append(tableAliax);
}
}
sql.append(".");
sql.append(c.name());
}
}
sql.append(" ");
sql.append(tableName);
sql.append(" AS ");
sql.append(tableAliax);
//如果有国际化
if (suffix != null) {
sql.append(" JOIN ");
sql.append(" ");
sql.append(tableName);
sql.append("_");
sql.append(suffix);
sql.append(" AS ");
sql.append(tableAliax);
sql.append(" ON ");
sql.append(tableAliax);
sql.append(".ID = ");
sql.append(textAliax);
sql.append(".NO");
sql.append(" AND ");
sql.append(textAliax);
sql.append(".LANG = '");
sql.append(locale.toString());
sql.append("'");
}
} catch (IllegalAccessException e) {
sql = null;
} catch (IllegalArgumentException e) {
sql = null;
} catch (InvocationTargetException e) {
sql = null;
}
return sql == null ? null : sql.toString();
}
发表评论
-
从 iBatis 到 MyBatis - MyBatis 简明学习教程
2012-11-19 17:22 1175从 iBatis 到 MyBatis ... -
spring注解式事务管理实例@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
2012-07-17 21:58 2406spring注解式事务管理实例@Transactio ... -
MyEclipse内存不足以及参数配置“ -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M ”
2012-07-17 16:00 1057MyEclipse内存不足以 ... -
StringUtils 工具类的常用方法
2012-05-20 22:33 4576StringUtils 源码,使用的是commons-lang ... -
获得List<T> T的类型
2012-05-20 21:45 841public class GenericTest { ... -
可以使用java.util.Calendar来取得相关日期信息
2012-04-19 20:31 1027import java.text.Parse ... -
提供一个JAVA过滤HTML代码和截取字符串的类(包括测试用例)
2012-03-07 22:23 1054package com.aodun.puhbs.blog ... -
ceshhisdf
2012-02-20 16:45 869ceshi -
JDKjdk1.4、jdk5.0、JDK6.0、 JDK7.0 新特性
2012-02-19 11:45 3003jdk1.4、jdk5.0、JDK6.0、 ... -
为什么Java中的long与double不是线程安全的?
2012-02-19 11:22 1878Long与double操作并 ... -
线程安全与线程不安全的区别
2012-02-19 11:15 697线程安全就是多线程访问时,采用了加锁机制(如synchr ... -
JConsole手册
2012-02-18 11:06 902http://blog.csdn.net/ajian005/a ... -
JVM调优总结(精)
2012-02-18 10:42 1994转自:http://pengjiaheng.javaey ... -
Java内存Dump文件查看和分析工具介绍
2012-02-16 23:43 1315为了查red5-0.7.0的内存泄露问题,由于dump文 ... -
探究JVM内存泄露
2012-02-16 23:32 1243WEB 服务总是莫名其妙的运行一段时间后 JVM 直接 ... -
设置JVM内存
2012-02-16 23:00 1174这里向大家简单介绍一下巧借MyEclipse设置JVM突 ... -
spring配置ibatis的jdbc方式和proxool、c3p0连接池方式
2011-06-30 10:19 1540spring配置ibatis的jdbc方式和proxool、c ... -
spring 事务管理配置-ibatis
2011-05-26 15:12 2382xml配置 Xml代码 1.<!-- 默认的数据 ... -
struts 格式化金额,日期
2011-04-11 17:50 13872010-07-21 Struts2中对数字进行格式化,1、将 ... -
log4j的日志处理
2011-03-12 12:34 929log4j的日志处理2009-06-11 ...
相关推荐
java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解代码java注解...
### Eclipse Java注释模板知识点详解 #### 一、概述 在进行Java开发的过程中,良好的代码注释习惯不仅能帮助自己快速回顾代码逻辑,还能方便其他开发者理解代码意图,从而提高整个团队的工作效率。Eclipse作为一款...
Java注解是Java编程语言中的一个重要特性,它允许在代码中添加元数据,为编译器、解释器或任何其他工具提供额外的信息。在Java Web服务(WebService)开发中,注解的应用极大地简化了服务的创建和配置。本文将深入...
Java注解是Java编程语言中的一个重要特性,它们提供了一种元数据的方式,允许程序员在代码中插入信息,这些信息可以被编译器或运行时环境用于处理代码。注解不仅简化了开发,还增强了代码的可读性和维护性。本资料...
除了上述框架特定的注解,还有一些通用的Java注解,如@Override表明方法重写父类方法,@Deprecated标记已废弃的方法,@ SuppressWarnings抑制编译器警告。理解并熟练运用这些注解,能够提升代码质量,减少出错可能性...
Java注解是Java编程语言中的一个重要特性,它允许程序员在代码中嵌入元数据,这些元数据可以被编译器或运行时环境用来执行各种任务,例如代码分析、验证、生成代码、部署等。注解提供了更加灵活和可扩展的方式来处理...
Java注解是Java编程语言中的一个重要特性,它提供了一种元数据的方式,允许程序员在代码中插入额外的信息,这些信息可以被编译器或者运行时环境用来执行特定的任务。注解在现代Java开发中扮演了不可或缺的角色,尤其...
java注解和反射的个人学习笔记
本文将深入探讨Java注释的类型、用途,以及如何创建和使用注释模板,以帮助您和您的团队提升代码质量。 1. **Java注释类型**: - **单行注释**:以`//`开头,常用于快速添加临时性的注释或简单的解释。 - **多行...
Java注解是自JDK1.5版本引入的一种元数据机制,它允许程序员在源代码的各个层次(类、方法、变量等)嵌入信息,这些信息可以被编译器或者运行时系统用来进行各种处理。注解简化了配置文件的使用,提高了代码的可读性...
Java注解是Java编程语言中的一个重要特性,它允许程序员在代码中嵌入元数据(metadata),这些元数据不直接影响程序的执行,但可以被编译器或运行时环境用来进行各种处理,如验证、动态代理、持久化、资源管理等。...
本篇文章将深入探讨Java注解的使用及其在实际开发中的应用。 首先,我们了解Java注解的基本结构。一个注解以`@`符号开头,后面跟着注解类型,如`@Override`或`@Deprecated`。注解可以带有参数,参数以键值对的形式...
Java注解(Annotation)是Java语言的一个重要特性,它为元数据提供了强大的支持。元数据是一种描述数据的数据,它提供了一种安全的方式,使我们可以在不改变程序代码本身的情况下,向编译器或运行时环境提供额外的...
Java注解(Annotation)是Java语言的一个重要特性,它为元数据提供了一种结构化的方式。元数据是在程序代码中包含的关于程序本身的信息,但这些信息并不直接影响程序的执行。注解允许开发者向编译器、JVM或工具提供...
Java注解、反射、字节码和类加载机制是Java编程中的核心概念,它们在实际开发中扮演着重要角色。让我们深入探讨这些知识点。 **Java注解(Annotation)**: Java注解是一种元数据,它提供了在编译时或运行时处理代码的...
超级好用的 java 注释模板,吐血总结,整理。吐血总结,整理。
在本教程中,我们将深入探讨如何使用Spring 4框架,特别是其基于Java注解的配置方式,来构建一个高效、可维护的系统。Spring 4是Java企业级应用开发的首选框架,它提供了广泛的功能,包括依赖注入、AOP(面向切面...
Eclipse作为一款强大的Java集成开发环境(IDE),提供了许多便捷的功能,其中之一就是“Eclipse Java 注释模板”。通过配置和使用这些模板,程序员可以快速地为代码添加注释,提高代码的可读性和维护性。下面我们将...