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、项目结构
<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;
}
}