Java注解使用的幾個(gè)小技巧:
最近經(jīng)常用到注解,總結(jié)了幾個(gè)注解使用的小技巧,現(xiàn)整理如下:
一、使用默認(rèn)的注解參數(shù):
使用default關(guān)鍵字可以指定注解的默認(rèn)參數(shù):
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 SQL {
String sql() default "";
}
如果我們在使用注解@SQL的時(shí)候沒有顯式的去指定sql參數(shù),那么就默認(rèn)采取default關(guān)鍵字所指定的值
二、用value指定默認(rèn)值
我們經(jīng)常看到很多注解是這種形式,例如:@SQL("select email from user")
這個(gè)注解里面的參數(shù)為什么沒有帶名稱呢?其實(shí)它的注解是這樣定義的:
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 SQL {
String value() default "";
}
三、在注解中使用數(shù)組參數(shù)
如果你用過Struts2的注解來配置Action,那么你會(huì)看到如下形式的注解:
@Results({
@Result(name="success",value=xxx
.jsp",type=org.apache.struts2.dispatcher.ServletRedirectResult.class),
@Result(name="input",value="/xxx
.jsp",type=org.apache.struts2.dispatcher.ServletRedirectResult.class)
})
怎么來創(chuàng)建這種注解呢?
其實(shí)可以把@Results的參數(shù)看作是一個(gè)@Result的數(shù)組
定義如下:
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 Result {
}
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 Results {
Result[] value(); //沒什么特別的,只是接受了一個(gè)數(shù)組參數(shù)而已
}
四、讓注解可以去修飾多個(gè)Target類型
我么使用@Target注解來指定某個(gè)注解可以修飾的類型,實(shí)際上,同上面一樣,@Target接受的是一個(gè)數(shù)組參數(shù),利用這一特性我們可以讓注解來修飾多個(gè)類型。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
}
OK!這個(gè)注解既可以修飾參數(shù)也可以修飾類的屬性
使用Java來開發(fā)JSP標(biāo)記
最近復(fù)習(xí)JSP中。我們知道JSP是Java WEB的一個(gè)規(guī)范。在這個(gè)規(guī)范之內(nèi)我們可以自己去做很多事情。JSP API提供了接口允許我們自己去自定義JSP標(biāo)簽和EL表達(dá)式。自定義JSP標(biāo)簽的大致思想是這樣的,實(shí)現(xiàn)一個(gè)包含標(biāo)簽參數(shù)和行為邏輯的類,這個(gè)類我們稱它為標(biāo)記處理器類,它實(shí)質(zhì)上是一個(gè)Bean,我們設(shè)置標(biāo)簽的屬性實(shí)際上是調(diào)用它的setter方法。接下來通過使用標(biāo)記庫描述文件(TLD)來描述標(biāo)簽的相關(guān)信息,以便在JSP頁面中使用taglib指令時(shí)能順利的找到它。下面我們來看看詳細(xì)應(yīng)該怎么去做。
從JSP2.0開始引入了一個(gè)簡單標(biāo)記處理器接口——javax.servlet.jsp.tagext.SimpleTag接口,這個(gè)接口取代了原先JSP1.0提供的3個(gè)標(biāo)記處理器接口,我們只需要使用SimpleTarget接口就可以實(shí)現(xiàn)所有類型的JSP標(biāo)記。之所以稱之為簡單,是因?yàn)檫@樣設(shè)計(jì)讓開發(fā)者在編寫程序上省了不少力氣,但是它可以實(shí)現(xiàn)很復(fù)雜的功能,并不是說它的功能很簡單。
該接口包含了如下5個(gè)方法:
void doTag(); //該方法包含了標(biāo)簽執(zhí)行的業(yè)務(wù)邏輯
JspTag getParent(); //獲取該標(biāo)簽的父標(biāo)簽
setJspBody(JspFragment body); //設(shè)置JSP標(biāo)簽體,關(guān)于JspFragment類我們稍后再討論
setJspContext(JspContext ctx);//設(shè)置JSP上下文
setParent(JspTag parent); //設(shè)置父標(biāo)簽
但是多數(shù)情況下我們不需要去親自實(shí)現(xiàn)這個(gè)接口,因?yàn)樽鳛殚_發(fā)者,我們關(guān)心的是業(yè)務(wù)邏輯,也就是doTag()方法的內(nèi)容,這種情況我們只需要繼承javax.servlet.jsp.tagext.SimpleTagSupport類即可,這個(gè)類為我們實(shí)現(xiàn)了SimpleTag接口,我們只需要去重寫它的doTag()方法。下面我們來實(shí)現(xiàn)一個(gè)最簡單的標(biāo)簽:給標(biāo)簽傳遞一個(gè)參數(shù),讓它在頁面上顯示出來.
首先我們要做的是寫一個(gè)標(biāo)記處理器類:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport{
private String name;
public void setName(String name){
this.name = name;
}
@Override
public void doTag()throws IOException{
this.getJspContext().getOut().print("Hello! "+name);
}
}
嗯,這是一個(gè)繼承了SimpleTagSupport類的Bean,一樣擁有屬性、setter方法。唯一特別的是重寫了父類的doTag()方法。在這個(gè)doTag()中,我們通過獲得一個(gè)JSP上下文對象來向頁面輸出一句話:Hello!XXX。
下面是該介紹JspContext對象的時(shí)候了,這是一個(gè)抽象類,它唯一的子類是PageContext。這么說剛才所返回的實(shí)際對象是PageContext類型的。我們可以對其進(jìn)行強(qiáng)制轉(zhuǎn)換:PageContext pageCtx = (PageContext)this.getJspContext();
通過PageContext對象我們就可以訪問到Request和Response對象了,如:
HttpServletRequest request = (HttpServletRequest)pageCtx.getRequest();
HttpServletResponse response = (HttpServletResponse)pageCtx.getResponse();
通過這種方式我們就可以在標(biāo)簽處理器中操作WEB應(yīng)用各個(gè)作用域的數(shù)據(jù)了。
我們都知道在使用JSP標(biāo)簽時(shí)要在JSP頁面開頭使用taglib指令進(jìn)行聲明,如:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
taglib指令通過URL來對標(biāo)簽進(jìn)行定位。但是它是如何做到的呢?這就要靠TLD了,TLD就是標(biāo)記庫描述文件(Tag Library Descriptor,TLD),我們看JSTL的JAR包中,META-INF文件夾下每個(gè)庫都有一個(gè)對應(yīng)的部署描述文件。TLD實(shí)際上是一個(gè)XML文件,里面描述了標(biāo)記庫和其中標(biāo)記的信息。一個(gè)完整的TLD文件結(jié)構(gòu)如下所示:
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.1</tlib-version>
<short-name>test</short-name>
<uri>http://test</uri>
<tag>
<name>hello</name>
<tag-class>test.jsp.tag.simple.HelloTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
</tag>
<tag>
<name>if</name>
<tag-class>test.jsp.tag.simple.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>foreach</name>
<tag-class>test.jsp.tag.simple.ForeachTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>items</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
</attribute>
</tag>
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
</taglib>
起始標(biāo)簽是taglib,<tlib-version>定義了taglib的版本號(hào),<short-name>定義了標(biāo)簽庫的簡稱,<uri>定義了URI標(biāo)識(shí),taglib就是通過此URI來找到相關(guān)的標(biāo)記庫的。
接下來的<tag>標(biāo)簽便是定義JSP標(biāo)簽的屬性了:
以下是必須的屬性
<name>標(biāo)簽名稱
<tag-class>標(biāo)記器類
<body-content>動(dòng)作體類型,JSP2.0支持如下動(dòng)作體:
empty 空標(biāo)記
jsp:可以包含標(biāo)簽、EL和Scriptlet
scriptless:不允許Java腳本內(nèi)容
tagdependent:對體不進(jìn)行處理
<attribute>標(biāo)簽定義了標(biāo)簽參數(shù)。
其中:
<name>標(biāo)簽指定了參數(shù)的名稱。
<required>指定了參數(shù)是否是必要的。true是必要,false是不必要
<rtexprvalue>指定了是否允許使用表達(dá)式(包括EL表達(dá)式和Java表達(dá)式),true為允許,false為不允許
<funtion>標(biāo)簽定義了EL表達(dá)式,我們稍后再做介紹。
如何執(zhí)行動(dòng)作體?
我們在編寫JSP頁面時(shí)經(jīng)常會(huì)用到<c:if>和<c:forEach>之類的標(biāo)簽
<c:if test="true">
<p>條件是真的,執(zhí)行!</p>
</c:if>
只要滿足條件,就會(huì)去執(zhí)行動(dòng)作體,那么我們怎么在自定義JSP標(biāo)記中去執(zhí)行動(dòng)作體呢?
首先我們先要來研究一個(gè)叫做JspFragment的類。JspFragment代表了JSP標(biāo)簽的動(dòng)作體,我們通過它的invoke方法就可以執(zhí)行動(dòng)作體,下面我們來實(shí)現(xiàn)一個(gè)類似于<c:if>的標(biāo)簽:
package test.jsp.tag.simple;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class IfTag extends SimpleTagSupport{
private boolean test;
public void setTest(boolean test){
this.test = test;
}
@Override
public void doTag()throws IOException,JspException{
JspFragment body = getJspBody();
//如果成立則執(zhí)行相應(yīng)邏輯
if(test){
body.invoke(null);
}
}
}
只要滿足了條件,標(biāo)簽動(dòng)作體就會(huì)執(zhí)行。
invoke方法接受一個(gè)java.io.Writer對象,使用這個(gè)對象將內(nèi)容輸出到頁面,如果不輸出則可以給它傳遞一個(gè)null參數(shù)。
JSP中引入了EL表達(dá)式給我們的開發(fā)帶來了很大的方便,同自定義標(biāo)簽一樣,允許開發(fā)者去自定義EL表達(dá)式函數(shù),定義EL表達(dá)式函數(shù)要比定義標(biāo)簽簡單多了,我們甚至不需要去實(shí)現(xiàn)任何接口,假設(shè)我們要實(shí)現(xiàn)一個(gè)簡單的EL表達(dá)式函數(shù)來計(jì)算兩個(gè)數(shù)的和,就可以這樣:
package test.jsp.tag.simple;
public class Calculator {
public static int add(int a,int b){
return a+b;
}
public static int minus(int a,int b){
return a-b;
}
public static int multiply(int a,int b){
return a*b;
}
public static int divide(int a,int b){
return a/b;
}
}
這個(gè)類中全部是static方法,這是一個(gè)工具類。
然后我們只需要在TLD聲明即可:
<function>
<description>
Just for test
</description>
<name>add</name>
<function-class>test.jsp.el.AddEl</function-class>
<function-signature>int add(int,int)</function-signature>
</function>
function-signature類似于C語言中的函數(shù)聲明,通過這個(gè)找到到底調(diào)用類中哪個(gè)工具方法。
使用的時(shí)候只需要這樣就可以了:${test:add(1,2)}運(yùn)行時(shí)輸出結(jié)果為3