注解

定义注解

​ Java语言使用@interface语法来定义注解(Annotation

public @interface MyAnno {

}

@Targe

​ 如果你什么都不加限制,就可以在ElementType里面的所有声明的地方使用;如果你想让注解的使用有限制,就罗列可以放置的地方。

@Target(ElementType.METHOD)
public @interface MyAnno {

}
说明
TYPE 类、接口、注解、枚举
FIELD 属性
MEHOD 方法
PARAMETER 方法参数
CONSTRUCTOR 构造函数
LOCAL_VARIABLE 局部变量(如循环变量、catch参数)
ANNOTATION_TYPE 注解
PACKAGE
TYPE_PARAMETER 泛型参数 jdk1.8
TYPE_USE 任何元素 jdk1.8

@Retention

自定义注解的生命周期
从编写Java代码到运行主要周期为源文件Class文件运行时数据,@Retention则标注了自定义注解的信息要保留到哪个阶段,分别对应的value取值为SOURCECLASSRUNTIME

  • SOURCE 源代码java文件,生成的class文件中就没有该信息了
  • CLASS class文件中会保留注解,但是jvm加载运行时就没有了
  • RUNTIME 运行时,如果想使用反射获取注解信息,则需要使用RUNTIME,反射是在运行阶段进行反射的
@Target(ElementType.METHOD)
@Retention(RUNTIME)
public @interface MyAnno {

}

@Inherited

是否可以被标注类的子类继承。被@Inherited修饰的注解是具有继承性的,在自定义的注解标注到某个类时,该类的子类会继承这个自定义注解。这里需要注意的是只有当子类继承父类的时候,注解才会被继承,类实现接口,或者接口继承接口,都是无法获得父接口上的注解声明的。

@MyAnno
class A{
    private int a;

    public A(int a){
        this.a = a;
    }

    public void f1(){
        System.out.println("f1方法");
    }
}
class B extends A{

    public B(int a) {
        super(a);
    }
}
@Retention(RUNTIME)
@Inherited
public @interface MyAnno {

}

A类加了@MyAnno注解,而@MyAnno开启了可继承,则B类也相当于加s了@MyAnno注解

@Repeatable

是否可以重复标注。

@Documented

是否在生成的JavaDoc文档中体现,被标注该注解后,生成的javadoc中,会包含该注解

使用注解

​ 注解的功能就是一个打上标签,便于我们对打标签的类或方法进行逻辑处理。我们可以使用动态代理来精确的对这些方法进行预操作处理或善后处理。下面是一个例子:

public class CglibProxy implements MethodInterceptor {
    // 创建Enhancer实例用于生成代理类
    private final Enhancer enhancer = new Enhancer();

    // 生成代理对象
    public Object getProxy(Class<?> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    // 方法拦截器实现
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        boolean flag = false;
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof MyAnno){
                flag = true;
            }
        }
        if (flag){
            System.out.println("检查权限");
            // 调用目标方法并获取返回值
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("善后处理");
            return result;
        }
        // 返回实际结果而非null
        Object result = proxy.invokeSuper(obj, args);
        return result;
    }

}
public class ForumServiceImpl{
    @MyAnno
    public void removeTopic(int topicId){
        System.out.println("模拟测除Topic记录:"+topicId);
    }
    public void removeForum(int forumId){
        System.out.println("模拟删除rorum记录:"+forumId);
    }
}
CglibProxy proxy = new CglibProxy();
ForumServiceImpl forumService=(ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);

​ 在这个例子中,先定义一个CglibProxy代理类,这个代理类中的intercept方法,用来进行过滤操作,其中,我们将带有MyAnno注解的方法进行处理。ForumServiceImpl类就是真正干活的类,就是我们平常所写的业务逻辑。这种方式就是aop编程(切面编程)。

AOP

动态代理的比较

图片

  • 动态代理类型:JDK动态代理、CGLIB动态代理、AspectJ动态代理
  • JDK动态代理特点:基于接口实现,运行时在内存中生成代理类
  • CGLIB动态代理特点:通过生成子类实现代理,不需要接口
  • AspectJ动态代理特点:通过编译器或类加载器直接修改字节码实现
  • 静态代理与动态代理区别:静态代理在编译时确定,动态代理在运行时生成
  • 动态代理实现原理:运行时操作字节码,内存中生成代理类或修改原有类
  • 动态代理应用场景:Spring AOP底层实现,主要用于框架开发
  • 开发选择建议:日常开发通常使用框架提供的代理机制,无需直接实现

Spring AOP的支持

  • Spring AOP实现原理:基于动态代理技术实现
  • 默认代理方式:Spring默认使用JDK动态代理
  • JDK代理限制:只能为接口创建代理,无法代理final类/方法
  • CGLIB代理特点:可代理无接口类,但实现更复杂
  • 代理配置灵活性:可通过配置切换JDK代理或CGLIB代理
  • AOP执行时机:在目标方法前后通过动态代理植入增强逻辑
  • 容器管理:Spring IOC容器负责AOP代理的生成和依赖管理

Spring AOP注解

使用

xml文件中配置

<aop:aspectj-autoproxy/>

类上加@Aspect注解,方法上加上写入点,定义写入到哪个方法

@Aspect
@Component
public class MyAspect {
 	 	// 指定的方法
    	@Pointcut("execution(* testExecution(..))")
    	public void anyTestMethod() {}
}

Spring AOP支持五种类型的通知,它们分别是:前置通知(Before)、后置通知(After)、返回通知(After Returning)、异常通知(After Throwing)和环绕通知(Around)。