注解是一種能被添加到j(luò)ava源代碼中的元數(shù)據(jù)堵第,方法、類隧出、參數(shù)和包都可以用注解來修飾踏志。注解可以看作是一種特殊的標(biāo)記,可以用在方法胀瞪、類针余、參數(shù)和包上,程序在編譯或者運(yùn)行時可以檢測到這些標(biāo)記而進(jìn)行一些特殊的處理凄诞。
1.基本使用
java.lang.annotation中提供了元注解圆雁,可以使用這些注解來定義自己的注解。
1.1 聲明定義一個注解
(1)五要素:
修飾符:訪問修飾符必須為public,不寫默認(rèn)為pubic帆谍;
關(guān)鍵字:關(guān)鍵字為@interface
注解名:注解名稱為自定義注解的名稱
注解類型元素:注解類型元素是注解中內(nèi)容伪朽,可以理解成自定義接口的實(shí)現(xiàn)部分;
元注解:使用元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OAuth
{
String loginUrl() default WapUrl.LOGINPAGE;
}
1.2 常用元注解
(1)Target
描述了注解修飾的對象范圍汛蝙,取值在java.lang.annotation.ElementType定義烈涮,常用的包括:
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述方法變量
TYPE:用于描述類朴肺、接口或enum類型
(2)Retention
表示注解保留時間長短。取值在java.lang.annotation.RetentionPolicy中坚洽,取值為:
SOURCE:在源文件中有效戈稿,編譯過程中會被忽略
CLASS:隨源文件一起編譯在class文件中,運(yùn)行時忽略
RUNTIME:在運(yùn)行時有效
只有定義為RetentionPolicy.RUNTIME時讶舰,我們才能通過注解反射獲取到注解
1.3 獲取注解
可以通過反射獲取注解鞍盗,比如獲取@ResponseBody注解
// 判斷是否是AJAX請求,AJAX請求一般使用@ResponseBody
private boolean isJsonRequest(HandlerMethod handler)
{
ResponseBody responseBody = handler.getMethodAnnotation(ResponseBody.class);
boolean isJsonRequest = false;
if (responseBody != null)
{
isJsonRequest = true;
}
return isJsonRequest;
}
2.應(yīng)用場景
2.1 自定義注解+攔截器 實(shí)現(xiàn)登錄校驗(yàn)
使用spring攔截器實(shí)現(xiàn)這樣一個功能,如果方法上加了@OAuth绘雁,則表示用戶該接口需要登錄才能訪問(沒有登錄直接跳轉(zhuǎn)到登錄頁面)橡疼,否則不需要登錄。
(1)先定義一個OAuth注解庐舟,默認(rèn)值是登錄頁面地址
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OAuth
{
String loginUrl() default WapUrl.LOGINPAGE;
}
(2)添加登錄注解
接口方法添加我們的登錄注解@OAuth欣除,要求登錄才能訪問
// 添加購物車商品
@OAuth
@ResponseBody
@RequestMapping(value = WebURL.CART_ADD, method = RequestMethod.POST)
public ResponseJSON actionCartAdd(@RequestBody @Valid CartAddModRequest cartAddModRequest, BindingResult bindingResult)
{
}
(3)實(shí)現(xiàn)登錄攔截邏輯
如果當(dāng)前用戶沒有登錄,直接跳轉(zhuǎn)到登錄頁面
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 反射獲取方法上的OAuth注解
OAuth oAuth = handlerMethod.getMethodAnnotation(OAuth.class);
// 登錄用戶信息
OAuthUser oAuthUser = authService.getOAuth(sid);
if (oAuth != null) {
// 不處于登錄狀態(tài)
if (oAuthUser == null || oAuthUser.getCustId() == null) {
// 判斷是否是AJAX請求
boolean isJsonRequest = isJsonRequest((HandlerMethod) handler);
// redirect到登錄界面
String baseUrl = Constants.BASE_SERVER;
String returnUrl = baseUrl + request.getRequestURI() + (request.getQueryString() != null ?
"?" + request.getQueryString() :
"");
response.setStatus(403);
if (Strings.isNullOrEmpty(oAuth.loginUrl())) {
response.sendRedirect(
baseUrl + WapUrl.LOGINPAGE + "?returnUrl=" + URLEncoder.encode(returnUrl, "utf-8"));
} else {
response.sendRedirect(
baseUrl + oAuth.loginUrl() + "?returnUrl=" + URLEncoder.encode(returnUrl, "utf-8"));
}
return false;
}
}
return true;
}
2.2 自定義注解+攔截器 實(shí)現(xiàn)限制訪問
(1)先定義一個Follow注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Follow
{
// 觸屏版(域名配置)是否允許訪問
boolean isWapAccess() default false;
// APP是否允許訪問
boolean isAppAccess() default false;
// 只需要openId, 并不強(qiáng)制用戶關(guān)注
boolean onlyOpenId() default false;
}
(2)添加觸屏版是否允許訪問注解
@Follow(isWapAccess = false)
@RequestMapping(value = WapUrl.TODAY_SHOW_LIST_FOLLOW, method = RequestMethod.GET)
public String todayWxList(@ModelAttribute("model") ModelMap model)
{
return todayList(model);
}
(3)實(shí)現(xiàn)登錄攔截邏輯
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 反射獲取方法上的Follow注解
Follow follow = handlerMethod.getMethodAnnotation(Follow.class);
if (follow != null) {
if (follow.isAppAccess() && WebCookiesUtil.getIsAppLogin()) {
return true;
}
if (!follow.isWapAccess()) {
redirectWechatPage(request, response);
return false;
} else {
// 有follow標(biāo)簽+非微信訪問+允許wap訪問
}
}
}
2.3 自定義注解+AOP 實(shí)現(xiàn)日志保存
此例子使用Spring boot 實(shí)現(xiàn)
(1)導(dǎo)入切面需要的依賴包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)定義一個注解@SysLog
// 系統(tǒng)日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
(3)方法上添加日志注解
// 獲取費(fèi)用信息挪略,匯總后的
@SysLog("查詢費(fèi)用信息历帚,主頁面")
@PostMapping("getExpense")
public ResultDto getExpense(@RequestBody Map<String, Object> map)
{
return sExpenseService.getExpense(map);
}
(4)定義一個切面類,實(shí)現(xiàn)攔截邏輯
/**
* 系統(tǒng)日志杠娱,切面處理類
*/
@Aspect
@Component
@Slf4j
public class SysLogAspect {
private final SysLogService sysLogService;
@Autowired
public SysLogAspect(SysLogService sysLogService) {
this.sysLogService = sysLogService;
}
//PointCut表示這是一個切點(diǎn)挽牢,@annotation表示這個切點(diǎn)切到一個注解上,后面帶該注解的全類名,切面最主要的就是切點(diǎn)摊求,所有的故事都圍繞切點(diǎn)發(fā)生禽拔。
//logPointCut是一個切點(diǎn)名字,@Around注解使用室叉,表示圍繞該切點(diǎn)發(fā)生
@Pointcut("@annotation(com.hao24.common.annotation.SysLog)")
public void logPointCut() {
}
// logPointCut1是另外一個切點(diǎn)名字
@Pointcut("execution(* com.hao24.modules..*.*Controller.*(..))")
public void logPointCut1()
{
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//執(zhí)行方法
Object result = point.proceed();
//執(zhí)行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}
//保存到數(shù)據(jù)庫中
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}
//請求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//請求的參數(shù)
Object[] args = joinPoint.getArgs();
try{
String params = new Gson().toJson(args);
sysLog.setParams(params);
}catch (Exception ignored){
}
//獲取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//設(shè)置IP地址
sysLog.setIp(IpUtils.getIpAddr(request));
//用戶名
String username = ((TblSysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
sysLog.setUsername(username);
sysLog.setTime(time);
sysLog.setCreateDate(new Date());
//保存系統(tǒng)日志
sysLogService.save(sysLog);
}
}