注解Annontation(也叫元数据)分类
1.元注解(用于定义注解的注解)
2.内置注解,即java自带的标准注解
3.自定义注解
元注解
说明 | |
---|---|
@Target | 表示注解的应用范围,其中ElementType的取值为TYPE(表示该注解只能用在类、接口(注释声明)、enum实例)、FIELD(表示该注解只能用在字段上)、METHOD(方法)、PARAMETER(参数)、CONSTRUCTOR(构造器)、LOCAL_VARIABLE(本地变量)、ANNOTATION_TYPE(注解类型)、PACKAGE(包)、TYPE_PARAMETER(类型参数) |
@Retention | 表示需要在什么级别保存注解的信息,其中RetentionPolicy保留策略有SOURCE(源码中保留,在编译期间别编译器丢弃)、CLASS(在class文件中使用,会被JVM丢弃)、RUNTIME(程序运行期间也保留注解信息,这也是反射能读取的阶段,一般都是这个策略) |
@Documented | 将此注解包含于Javadoc中 |
@Inherited | 允许子类继承父类中的注解 |
内置注解
@Override子类对父类方法的重写
@Deprecated表示已经过时
@SuppressWarnings用来抑制编译时的警告信息==
想要了解java内置注解是如何解析的,可以参看这篇文章
重点:如何运用自定义注解
业务场景:在之前呢,我们用动态代理实现了业务功能的日志记录,现在我们来改进一下之前的代码,用spring AOP的知识结合注解来实现日志功能的记录。
spring AOP 相关知识
AOP概念
AOP(Aspect Oriented Programming),即面向切面编程,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。业务流程是一个纵向功能,而权限认证、日志、事物==是一个横向流程,没有横向的流程也不会影响业务功能的处理,但是为了系统的完善,这些横向的功能又是必须的。
AOP功能
让关注点代码与业务逻辑代码分离
AOP常用术语
连接点(Joinpoint)
增强程序执行的某个特定位置(要在哪个地方做增强操作)。Spring仅支持方法的连接点。
切点(Pointcut):执行目标对象方法,动态植入切面代码
切点是一组连接点的集合。AOP通过“切点”定位特定的连接点。通过数据库查询的概念来理解切点和连接点的关系再适合不过了:连接点相当于数据库中的记录,而切点相当于查询条件。
可以通过切入点表达式,指定拦截那些类的那些方法,给指定的类在运行的时候植入切面类代码。
增强(Advice):就是需要横向加入的重复代码
增强是织入到目标类连接点上的一段程序代码。表示要在连接点上做的操作。主要有如下几种增强方式:
前置增强(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
后置增强(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
返回后增强(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。
环绕增强(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。
切面(Aspect): 切点形成的类,就叫做切面(类),面向切面编程,就是指对很多功能都有的重复代码抽取,再在运行的时候往业务方法上动态植入"切面类代码"
切面由切点和增强(引介)组成(可以包含多个切点和多个增强),它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的链接点中。
本项目是在springBoot项目上进行整合的(很简单的,别被springboot吓到了)
第一步:引入依赖
第二步:创建出必要的类
2.1自定义注解
/**
* 自定义注解
* @create by 程二狗 on 2018/10/21 0021
**/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LogRecord {
String[] value() default {};
}
2.2定义业务功能接口
/**定义业务功能
* @create by 程二狗 on 2018/10/21 0021
**/
public interface UserService {
void add();
User get();
}
2.3实现业务接口(目标类)
/**
* 目标对象
* @create by 程二狗 on 2018/10/21 0021
**/
@Service //把该类加载Ioc容器中
@LogRecord(value = "user对象的操作")
public class UserServiceImpl implements UserService {
@Override
@LogRecord({"程二狗","18"})
public void add() {
System.out.println("user添加方法执行了----");
}
@Override
@LogRecord({"陈雪峰","20"})
public User get() {
System.out.println("user查找方法执行了----");
return null;
}
}
2.4核心:切面类(注解解析也在这儿)
package com.springboot.aop.aspect;
import com.springboot.aop.entity.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**切面类:业务无关的代码在这儿抽取封装成一个类
* 当业务执行时,动态的织入无关的业务代码
* 同时我也在这儿解析了自定义注解,动态地拼接了数据
* @create by 程二狗 on 2018/10/21 0021
**/
@Component//加入到IOC容器
@Aspect//指定当前类为切面类
public class SpringAop {
//-------------------第一种方式:前置后置方法,有局限性,共用类变量会出现线程安全问题,用的不多-----------------
//指定切入点表达式,拦截那些方法,即为那些类生成代理对象
// @Pointcut("execution(* com.springboot.aop.aspect.UserService.*(..))")
// public void pointCut() {
// }
// @Before("pointCut()")
// public void begin() {
// System.out.println("前置增强:log日志开始记录了------>");
// }
// @After("pointCut()")
// public void close() {
// System.out.println("后置增强:log日志完成记录了<------");
// }
//-------------------第二种方式:环绕织入,最常见,开发中用的最多-----------------
//@within(M)的匹配规则
//@within(注解类型全限定名)匹配所有持有指定注解的类里面的方法, 即要把注解加在类上.
// 在接口上声明不起作用,子孙类经测试匹配不到
//@annotation(M)的匹配规则
//当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配
@Around(value = "@annotation(LogRecord) || @within(LogRecord)")
public Object logRecord(ProceedingJoinPoint pjp) throws Throwable {
//1.获取类上的注解
LogRecord annoOnClass = (LogRecord) pjp.getSignature().getDeclaringType().getAnnotation(LogRecord.class);
//获取注解的信息value数组,即自定义注解中的String[] value() default {};
String[] megOnClass = null;
if (annoOnClass != null && annoOnClass.value().length > 0) {
megOnClass = annoOnClass.value();
}
//2.获取方法上定义的注解
Method mth = ((MethodSignature) pjp.getSignature()).getMethod();
LogRecord annoOnMehtod = mth.getAnnotation(LogRecord.class);
String[] megOnMethod = null;
if (annoOnMehtod != null && annoOnMehtod.value().length > 0) {
megOnMethod = annoOnMehtod.value();
}
//得到类上的注解信息
StringBuilder msg = new StringBuilder();
if (null != megOnClass) {
for (String onClass : megOnClass) {
msg.append(onClass + "===》");
}
}
//得到方法上的注解信息
if (null != megOnMethod) {
msg.append(":user的姓名是:" + megOnMethod[0] + ",年龄是" + megOnMethod[1]);
}
//相当于增强方法,和业务无关的代码(比如日志记录、权限校验)
System.out.println("(业务无关代码:)log日志开始记录了====》" +"(获取的注解信息:)"+ msg.toString() + "");
//执行目标对象的方法,即业务代码
Object obj = pjp.proceed();
//相当于增强方法,和业务无关的代码(比如日志记录、权限校验)
System.out.println("(业务无关代码)log日志完成了记录《====" +"(获取的注解信息:)"+ msg.toString() + "");
return obj;
}
}
小tips:关于spring Aop切点指示符更多详解,请点我
2.4测试
@Test
public void springAopTest() {
userService.add();
System.out.println("======================程二狗===========================");
userService.get();
//(业务无关代码:)log日志开始记录了====》(获取的注解信息:)user对象的操作===》:user的姓名是:程二狗,年龄是18
// user添加方法执行了----
//(业务无关代码)log日志完成了记录《====(获取的注解信息:)user对象的操作===》:user的姓名是:程二狗,年龄是18
// ======================程二狗===========================
//(业务无关代码:)log日志开始记录了====》(获取的注解信息:)user对象的操作===》:user的姓名是:陈雪峰,年龄是20
// user查找方法执行了----
//(业务无关代码)log日志完成了记录《====(获取的注解信息:)user对象的操作===》:user的姓名是:陈雪峰,年龄是20
}
}
总结:现在我们只要在业务功能的类或方法上加上一个小小的注解@logReord,在调用业务功能时,就会得到想要的注解信息
另一方面:定义好自己的切面类,程序在运行时,自动为业务功能添加增强的方法(日志记录、权限校验==)
这样一来是不是很方便呢?前端传递的参数,后台接受到后,进行校验的原理就是这个哦,你也可以自己去制定自己想要校验的规则。
网友评论