按照我的理解迈勋,Spring的注解是一個(gè)標(biāo)簽醋粟,為的是標(biāo)注一下某個(gè)方法(目前我所經(jīng)歷的注解應(yīng)用的最多的就是在方法名上)重归,然后在某個(gè)地方找到有這個(gè)標(biāo)記的方法,對(duì)這些方法做一些自定義的處理鼻吮。
現(xiàn)在知道在我們項(xiàng)目中自定義的注解的處理方法的實(shí)現(xiàn)是通過寫切面來實(shí)現(xiàn)的椎木,比如有個(gè)@Log注解博烂,它可以把一些關(guān)鍵點(diǎn)的日志保存到數(shù)據(jù)庫(kù)里漱竖。于是就定義了一個(gè)切面,切面會(huì)找到被這個(gè)標(biāo)簽標(biāo)注的類馍惹,然后為它們保存日志信息万矾。
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import com.kyee.common.service.LogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.kyee.common.annotation.Log;
import com.kyee.common.domain.LogDO;
import com.kyee.common.utils.HttpContextUtils;
import com.kyee.common.utils.IPUtils;
import com.kyee.common.utils.JSONUtils;
import com.kyee.common.utils.ShiroUtils;
import com.kyee.system.domain.UserDO;
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Autowired
LogService logService;
@Pointcut("@annotation(com.kyee.common.annotation.Log)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
// 執(zhí)行方法
Object result = point.proceed();
// 執(zhí)行時(shí)長(zhǎng)(毫秒)
long time = System.currentTimeMillis() - beginTime;
//異步保存日志
saveLog(point, time);
return result;
}
void saveLog(ProceedingJoinPoint joinPoint, long time) throws InterruptedException {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogDO sysLog = new LogDO();
Log syslog = method.getAnnotation(Log.class);
if (syslog != null) {
// 注解上的描述
sysLog.setOperation(syslog.value());
}
// 請(qǐng)求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
// 請(qǐng)求的參數(shù)
Object[] args = joinPoint.getArgs();
try {
String params = JSONUtils.beanToJson(args[0]).substring(0, 4999);
sysLog.setParams(params);
} catch (Exception e) {
}
// 獲取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
// 設(shè)置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
// 用戶名
UserDO currUser = ShiroUtils.getUser();
if (null == currUser) {
if (null != sysLog.getParams()) {
sysLog.setUserId(-1L);
sysLog.setUsername(sysLog.getParams());
} else {
sysLog.setUserId(-1L);
sysLog.setUsername("獲取用戶信息為空");
}
} else {
sysLog.setUserId(ShiroUtils.getUserId());
sysLog.setUsername(ShiroUtils.getUser().getUsername());
}
sysLog.setTime((int) time);
// 系統(tǒng)當(dāng)前時(shí)間
Date date = new Date();
sysLog.setGmtCreate(date);
// 保存系統(tǒng)日志
logService.save(sysLog);
}
}
那么Spring的這些注解理應(yīng)在Spring框架中進(jìn)行實(shí)現(xiàn)了后添,比如說RequestMapping這個(gè)注解薪丁,但是點(diǎn)擊進(jìn)去只能看到注解的定義,并不知道它的實(shí)現(xiàn)代碼在哪里.
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
看到一篇文章:https://dzone.com/articles/spring-annotation-processing-how-it-works
If you see an annotation, there must be some code somewhere to process it.
如果你看到了一個(gè)注解努溃,那么一定在某個(gè)地方有段代碼去處理它阻问。
在InitDestroyBeanPostProcessor類中有下面一段代碼:
private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
boolean debug = this.logger.isDebugEnabled();
LinkedList initMethods = new LinkedList();
LinkedList destroyMethods = new LinkedList();
Class targetClass = clazz;
do {
LinkedList currInitMethods = new LinkedList();
LinkedList currDestroyMethods = new LinkedList();
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
if(this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
InitDestroyAnnotationBeanPostProcessor.LifecycleElement element = new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method);
currInitMethods.add(element);
if(debug) {
this.logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if(this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method));
if(debug) {
this.logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return new InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz, initMethods, destroyMethods);
}
根據(jù)反射方法尋找注解標(biāo)記称近,如果找到了注解標(biāo)記,這個(gè)被標(biāo)記的方法被當(dāng)做一個(gè)初始化方法存了下來刨秆。然后就會(huì)調(diào)用所有找到的初始化方法,給對(duì)象執(zhí)行這些操作尸执。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata = this.findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
return bean;
} catch (InvocationTargetException var5) {
throw new BeanCreationException(beanName, "Invocation of init method failed", var5.getTargetException());
} catch (Throwable var6) {
throw new BeanCreationException(beanName, "Failed to invoke init method", var6);
}
}
隱隱的感覺到是這么處理的缓醋,感覺還沒有理解很透徹,待我繼續(xù)調(diào)查回來再更褪贵。。脆丁。