Java注解大家基本都用过,但实际怎么生效的就是另外一回事了。你去看源代码发现其实就是一个接口再添加两个注解就完事了。本质就是一个接口,那对应的实现在哪里,具体逻辑是什么样的,就找不到了。
Java注解一般来说常见的可以分为几类,一种是过去的Java EE也就是现在的Jarkatar的包里面的注解。这种你可以理解为是大家制定了一个规范,定义了接口,输入参数和返回参数这些。具体实现要看各自的框架怎么落实,比如说tomcat这些web服务器。这些注解比如说@PostConstruct,@Resource这些。
另外一个是框架的注解,使用最多的就是Spring的注解,@Component,@Autowire这些,是Spring定义的注解。
还有就是我们自己定义的注解,比如说我们定义一个注解,以下是一个自定义的注解
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) // 必须为 RUNTIME,否则反射无法读取
public @interface MyAnnotation {
String value() default "默认值";
int priority() default 1;
}
但实际你定义这个注解,只是一个接口,只是指定了ElementType.METHOD,在方法上使用。RetentionPolicy.RUNTIME指定了反射。但你定义,怎么使用又是一回事。
public class DemoService {
@MyAnnotation(value = "高优先级任务", priority = 10)
public void doImportantTask() {
System.out.println("执行高优先级任务...");
}
@MyAnnotation("普通任务")
public void doNormalTask() {
System.out.println("执行普通任务...");
}
public void unannotatedMethod() {
System.out.println("无注解方法");
}
}
我们在方法上使用这个注解,但是到这部还没有用,你用反射可以有两种方式,一个是动态代理和cglib
JDK动态代理(基于接口)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
public static void main(String[] args) {
DemoService target = new DemoService();
// 创建代理对象
DemoService proxy = (DemoService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 检查方法是否有注解
if (method.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("前置处理...");
}
Object result = method.invoke(target, args);
if (method.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("后置处理...");
}
return result;
}
});
proxy.doImportantTask(); // 触发代理逻辑
}
}
cglib(基于类)
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(DemoService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("CGLIB 前置处理...");
}
Object result = proxy.invokeSuper(obj, args);
if (method.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("CGLIB 后置处理...");
}
return result;
}
});
DemoService proxy = (DemoService) enhancer.create();
proxy.doImportantTask();
}
}
当然我们在项目中一般不会直接用动态代理(JDK或者cglib),我们一般用框架Spring框架自动化处理。
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) // 必须为 RUNTIME,否则 AOP 无法读取
public @interface MyCustomAnnotation {
String value() default "";
int timeout() default 1000;
}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component // 确保切面被 Spring 容器管理
public class MyAspect {
// 环绕通知,匹配所有被 @MyCustomAnnotation 注解的方法
@Around("@annotation(myAnnotation)") // 通过注解参数绑定
public Object aroundAdvice(ProceedingJoinPoint joinPoint, MyCustomAnnotation myAnnotation) throws Throwable {
// 获取注解参数值
String value = myAnnotation.value();
int timeout = myAnnotation.timeout();
System.out.println("注解参数 value: " + value);
System.out.println("注解参数 timeout: " + timeout);
// 执行目标方法
Object result = joinPoint.proceed();
return result;
}
}
import org.springframework.stereotype.Service;
@Service
public class MyService {
@MyCustomAnnotation(value = "重要任务", timeout = 5000)
public void doTask() {
System.out.println("执行任务...");
}
}