注解的使用案例有很多,比如:
- 使用注解来生成文档,如javadoc中的@author,@param等。
- 使用注解来跟踪代码依赖性,实现替代配置文件功能,如spring mvc中的@Controller,@RequestMapping等。
- 使用注解来编译时进行格式检查,如@Override,@Deprecated等。
- 使用注解来编译时进行代码生成补全,如lombok插件中的@Data,@Getter等。
- 使用注解来运行时进行反射操作,如Hibernate中的@Entity,@Column等。
- 使用自定义注解和拦截器实现登录校验。首先定义一个名为LoginRequired的注解,用来标记需要登录才能访问的方法。然后定义一个拦截器类,实现HandlerInterceptor接口,在preHandle方法中判断请求方法是否有LoginRequired注解,如果有则检查用户是否已经登录,如果没有则跳转到登录页面或返回错误信息。最后在spring mvc的配置文件中注册拦截器,并指定拦截路径。
// 定义一个名为LoginRequired的注解
@Target(ElementType.METHOD) // 作用于方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface LoginRequired {
// 定义一个布尔值参数,表示是否需要登录,默认为true
boolean value() default true;
}
// 定义一个拦截器类
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判断handler是否是处理器方法
if (handler instanceof HandlerMethod) {
// 强制转换为HandlerMethod对象
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取方法上的LoginRequired注解
LoginRequired loginRequired = handlerMethod.getMethodAnnotation(LoginRequired.class);
// 如果注解不为空且value为true,则表示需要登录
if (loginRequired != null && loginRequired.value()) {
// 获取session中的用户信息
User user = (User) request.getSession().getAttribute("user");
// 如果用户为空,则表示未登录
if (user == null) {
// 返回错误信息或跳转到登录页面
response.sendRedirect("/login");
return false;
}
}
}
return true;
}
}
// 在spring mvc的配置文件中注册拦截器,并指定拦截路径
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.example.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 使用自定义注解和AOP实现日志打印。首先定义一个名为Loggable的注解,用来标记需要打印日志的方法。然后定义一个切面类,使用@Aspect和@Component注解修饰,并使用@Before和@AfterReturning等通知注解来实现在目标方法执行前后打印日志的功能。最后在spring boot的配置类中开启AOP功能。
// 定义一个名为Loggable的注解
@Target(ElementType.METHOD) // 作用于方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface Loggable {
// 定义一个字符串参数,表示日志的级别,默认为info
String level() default "info";
}
// 定义一个切面类
@Aspect // 表示这是一个切面类
@Component // 表示这是一个组件类,交给spring管理
public class LogAspect {
// 定义一个日志对象
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
// 定义一个切点表达式,表示匹配有Loggable注解的方法
@Pointcut("@annotation(com.example.Loggable)")
public void logPointcut() {}
// 定义一个前置通知,表示在目标方法执行前打印日志
@Before("logPointcut() && @annotation(loggable)")
public void beforeLog(JoinPoint joinPoint, Loggable loggable) {
// 获取方法签名
Signature signature = joinPoint.getSignature();
// 获取方法名
String methodName = signature.getName();
// 获取日志级别
String level = loggable.level();
// 根据日志级别打印不同的日志信息
switch (level) {
case "info":
logger.info("开始执行方法:" + methodName);
break;
case "debug":
logger.debug("开始执行方法:" + methodName);
break;
case "error":
logger.error("开始执行方法:" + methodName);
break;
default:
logger.info("开始执行方法:" + methodName);
}
}
// 定义一个后置通知,表示在目标方法返回后打印日志
@AfterReturning(pointcut = "logPointcut() && @annotation(loggable)", returning = "result")
public void afterLog(JoinPoint joinPoint, Loggable loggable, Object result) {
// 获取方法签名
Signature signature = joinPoint.getSignature();
// 获取方法名
String methodName = signature.getName();
// 获取日志级别
String level = loggable.level();
// 根据日志级别打印不同的日志信息
switch (level) {
case "info":
logger.info("结束执行方法:" + methodName + ",返回结果:" + result);
break;
case "debug":
logger.debug("结束执行方法:" + methodName + ",返回结果:" + result);
break;
case "error":
logger.error("结束执行方法:" + methodName + ",返回结果:" + result);
break;
default:
logger.info("结束执行方法:" + methodName + ",返回结果:" + result);
}
}
}
// 在spring boot的配置类中开启AOP功能
@Configuration // 表示这是一个配置类
@EnableAspectJAutoProxy // 表示开启AOP功能
public class AopConfig {
}
为什么后面的功能中不适用getMethodAnnotation获取注解信息?
如果你指的是使用AOP的案例中,为什么不使用getMethodAnnotation获取注解信息,而是使用@annotation(loggable)这样的表达式,那么原因是这样的:
- 使用getMethodAnnotation需要先获取目标方法的Method对象,然后才能调用该方法获取注解信息。这样做的缺点是需要手动编写反射代码,而且可能会抛出异常。
- 使用@annotation(loggable)这样的表达式,可以直接将目标方法上的注解对象作为参数传递给通知方法,无需编写反射代码,也不会抛出异常。这样做的优点是更简洁、更安全、更高效。
那么@annotation(loggable)这样的表达式是怎么工作的呢?
@annotation表达式是一种切点表达式,用来匹配目标方法上有指定注解的连接点。1234 例如:
- @annotation(com.example.Loggable) 表示匹配有Loggable注解的方法。
- @annotation(appendAnnotation) && args(val) 表示匹配有appendAnnotation注解的方法,并且将注解对象和方法参数传递给通知方法。
@annotation表达式的作用是可以方便地使用自定义注解来标记需要切入的方法,而不需要写复杂的execution表达式。这样可以提高代码的可读性和可维护性。