一.注解的作用:
在开发过程中时刻都会用到注解,它能够使得编程更加简洁,代码更加清晰。最近有用到自定义注解,记录一下注解的定义与使用。
二.应用:
打日志、分析代码执行时间、权限控制、事务处理、访问频率控制、异常处理等
三.自定义注解编写规则:
- Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation接口,并且不能再去继承别的类或接口.
- 参数成员只能用public或默认(default)修饰
- 参数成员只能用八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
- 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象
四.元注解
java中有四种元注解:@Retention、@Inherited、@Documented、@Target
- @Retention:注解的保留位置(枚举RetentionPolicy),RetentionPolicy可选值:
1.@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候
2.@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
3.@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时,
- @Inherited:声明子类可以继承此注解,如果一个类A使用此注解,则类A的子类也继承此注解
- @Documented:声明注解能够被javadoc等识别
- @Target:用来声明注解范围(枚举ElementType),ElementType可选值
取值 | 描述 |
CONSTRUCTOR | 用于描述构造器。 |
FIELD | 用于描述域。 |
LOCAL_VARIABLE | 用于描述局部变量。 |
METHOD | 用于描述方法。 |
PACKAGE | 用于描述包。 |
PARAMETER | 用于描述参数。 |
TYPE | 用于描述类或接口(甚至 enum )。 |
五.自定义注解实例
1.定义注解:以@interface修饰自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/** 模块 */
String title() default "";
/** 功能 */
String action() default "";
}
2.定义切面
@Aspect (说明此类为切面)
@component (把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>)
@Aspect
@Component("logAspect")
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 配置织入点(yuanlian.emailSubscription.inte.Log 为自定义注解的全路径)
@Pointcut("@annotation(yuanlian.emailSubscription.inte.Log)")
public void logPointCut() {
}
/**
* 前置通知 用于拦截操作,在方法返回后执行
* @param joinPoint 切点
*/
@Before("logPointCut()")
public void doa(JoinPoint joinPoint) {
System.out.println("这是前置通知");
handleLog(joinPoint, null);
}
/**
* 拦截异常操作,有异常时执行
*
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e);
}
private void handleLog(JoinPoint joinPoint, Exception e) {
try {
// 获得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null) {
return;
}
// 获得方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
String action = controllerLog.action();
String title = controllerLog.title();
//打印日志,如有需要还可以存入数据库
log.info("模块名称:{}",title);
log.info("操作名称:{}",action);
} catch (Exception exp) {
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 是否存在注解,如果存在就获取
*/
private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(Log.class);
}
return null;
}
}
3.在springMVC的xml文件中加入aop支持
<!-- 在头部加入命名空间 -->
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
<!-- 如果不加错误信息为: The prefix "aop" for element "aop:aspectj-autoproxy" is not bound. -->
<!-- 启动对@AspectJ注解的支持 -->
<!-- proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口 就走JDK代理,如果没有,走cglib代理 -->
<!-- 注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib -->
<!--如果不写proxy-target-class="true"这句话也没问题-->
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="logAspect" />
</aop:aspectj-autoproxy>
<!--切面 (yuanlian.emailSubscription.inte.LogAspect 为切面的全路径)-->
<bean id="logAspect" class="yuanlian.emailSubscription.inte.LogAspect"></bean>
4.完成以上编码即可使用
@Controller
@RequestMapping("/")
public class HelloController {
@RequestMapping("testAOP")
@Log(title="testTitle",action="testAction")
@ResponseBody
public void testAOP(){
System.out.println("aop");
}
}