浅谈Java注解的原理与使用

时间:2020-12-17 01:17:01


 1、元注解

@Target:注解的作用目标
@Retention:注解的生命周期
@Documented:注解是否应当被包含在 JavaDoc 文档中
@Inherited:是否允许子类继承该注解

1.1、Target枚举值

ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
ElementType.FIELD:允许作用在属性字段上
ElementType.METHOD:允许作用在方法上
ElementType.PARAMETER:允许作用在方法参数上
ElementType.CONSTRUCTOR:允许作用在构造器上
ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
ElementType.ANNOTATION_TYPE:允许作用在注解上
ElementType.PACKAGE:允许作用在包上

1.2、Retention枚举值

RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
RetentionPolicy.RUNTIME:永久保存,可以反射获取

2、java内置的三大注解

@Override
@Deprecated
@SuppressWarnings

3、注解的本质

比如@Override注解

public interface Override extends java.lang.annotation.Annotation{
    
}

注解的本质就是一个继承了 Annotation 接口的接口。,你可以去反编译任意一个注解类,你会得到结果的。

一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。

比如@Override注解,编译期会直接扫描,如果这个方法不是重写父类的方法,则报错

4、自定义注解

4.1、定义注解

package com.asiainfo.group.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordLog {
	//是否记日志
	public boolean isRecord();
	//日志内容
    public String content() default "执行";
}

4.2、使用自定义注解

package com.asiainfo.group.annotation;

public class AnnotationDemo {

	@RecordLog(isRecord=true,content="保存用户")
	public void saveUser() throws Exception{
		//以下代码是获取方法注解的属性值,可以做成一个切面,而不必写在方法里
		Class c=AnnotationDemo.class;
		RecordLog r = c.getMethod("saveUser").getAnnotation(RecordLog.class);
		boolean b = r.isRecord();
		String content = r.content();
		System.err.println(b);
		System.err.println(content);
	}
	
	
	
	
	public static void main(String[] args) {
		try {
			new AnnotationDemo().saveUser();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

5、关于spring注解的一些误区

1、@Bean和@Compnent的区别? 

      @Bean必须配置@Configuration注解才能使用

2、@Controller、@Service等注解包含了@Compnent注解

3、项目A在com包下面定义了n个Compnent,项目A打jar包,项目B引入项目A的依赖,则只要项目B扫包能扫到项目A的

       com包,则项目A的n个Compnent都会生效,因为spring的@Compnent等注解的生命周期为RetentionPolicy.RUNTIME

6、自定义注解实战(spring aop+springboot)

6.1、项目结构

浅谈Java注解的原理与使用

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

6.2、定义注解

6.2.1、RecordLog

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordLog {
}

6.2.2、PrintAccess 

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAccess {
}

6.3、定义切面

6.3.1、PrintAcessAspect 

import com.asiainfo.com.springboottest.annotation.PrintAccess;
import com.asiainfo.com.springboottest.annotation.RecordLog;
import com.asiainfo.com.springboottest.util.AccessCounter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class PrintAcessAspect {

    /**
     * 定义切入点
     */
    @Pointcut("execution(* com.asiainfo.com.springboottest.service.*.*(..))")
    private void pointCut(){}


    @Before(value="pointCut()")
    public void printAccess(JoinPoint p) {
        //获取方法上的注解
        Class<?> targetClass = p.getTarget().getClass();
        String methodName=p.getSignature().getName();
        Class<?>[] parameterTypes = ((MethodSignature) p.getSignature()).getParameterTypes();
        Method method = null;
        try {
            method = targetClass.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        PrintAccess printAccess = null==method?null:method.getAnnotation(PrintAccess.class);

        //方法签名
        if(null != printAccess){
            AccessCounter.COUNT++;
            System.out.println("访问次数:"+AccessCounter.COUNT);
        }

    }

}

6.3.2、RecordLogAspect 

import com.asiainfo.com.springboottest.annotation.PrintAccess;
import com.asiainfo.com.springboottest.annotation.RecordLog;
import com.asiainfo.com.springboottest.util.AccessCounter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class RecordLogAspect {

    /**
     * 定义切入点
     */
    @Pointcut("execution(* com.asiainfo.com.springboottest.service.*.*(..))")
    private void pointCut(){}


    @Around(value="pointCut()")
    public Object recordLog(ProceedingJoinPoint p) {
        //获取方法上的注解
        Class<?> targetClass = p.getTarget().getClass();
        String methodName=p.getSignature().getName();
        Class<?>[] parameterTypes = ((MethodSignature) p.getSignature()).getParameterTypes();
        Method method = null;
        try {
            method = targetClass.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        RecordLog recordLog = null==method?null:method.getAnnotation(RecordLog.class);

        //方法签名
        String signature = p.getSignature().toString();

        if(null != recordLog){
            System.out.println(signature+" begin");
        }
        Object obj = null;
        try {
            //执行切入点自己的方法
            obj = p.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        if(null != recordLog){
            System.out.println(signature+" end");
        }
        return obj;
    }


}

6.4、service包

import org.springframework.stereotype.Service;

public interface IUserService {
    int addUser();
}

6.5、service.impl包

import com.asiainfo.com.springboottest.annotation.PrintAccess;
import com.asiainfo.com.springboottest.annotation.RecordLog;
import com.asiainfo.com.springboottest.service.IUserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements IUserService {


    @Override
    @RecordLog
    @PrintAccess
    public int addUser() {
        System.out.println("执行添加用户");
        return 1;
    }
}

6.6、启动类打上@EnableAspectJAutoProxy注解开启spring aop