按照我的理解,Spring的注解是一个标签,为的是标注一下某个方法(目前我所经历的注解应用的最多的就是在方法名上),然后在某个地方找到有这个标记的方法,对这些方法做一些自定义的处理。
现在知道在我们项目中自定义的注解的处理方法的实现是通过写切面来实现的,比如有个@Log注解,它可以把一些关键点的日志保存到数据库里。于是就定义了一个切面,切面会找到被这个标签标注的类,然后为它们保存日志信息。
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();
// 执行方法
Object result = point.proceed();
// 执行时长(毫秒)
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());
}
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
// 请求的参数
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();
// 设置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);
// 系统当前时间
Date date = new Date();
sysLog.setGmtCreate(date);
// 保存系统日志
logService.save(sysLog);
}
}
那么Spring的这些注解理应在Spring框架中进行实现了,比如说RequestMapping这个注解,但是点击进去只能看到注解的定义,并不知道它的实现代码在哪里.
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.
如果你看到了一个注解,那么一定在某个地方有段代码去处理它。
在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);
}
根据反射方法寻找注解标记,如果找到了注解标记,这个被标记的方法被当做一个初始化方法存了下来。然后就会调用所有找到的初始化方法,给对象执行这些操作。
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);
}
}
隐隐的感觉到是这么处理的,感觉还没有理解很透彻,待我继续调查回来再更。。。
网友评论