天天看点

Spring-AOP结合自定义注解的使用

最近在项目(IDEA+Spring+JDK1.6+)中,为了控制用户的读写权限,使用了AOP技术,将权限控制定义成了一个切面(Aspect)。

1、AOP的基本概念

AOP是spring框架中的重要特性,英文为Aspect Oriented Programming ,意思是面向切面编程。

我们在系统开发中可以提取出很多共性的东西作为一个Aspect,可以理解为在系统中,我们需要很多次重复实现的功能。

比如日志打印,判断用户是否已登录,判断页面的读写权限等等。

1.1 AOP 重要的概念

Join Point:表示在程序中明确定义的执行点,典型的 Join Point 包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 Join Point。

PointCut:表示一组 Join Point,这些 Join Point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice:Advice 定义了在 PointCut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 Join Point 之前、之后还是代替执行的代码。

1.2 通知(Advice)类型

前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。

后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。

环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。

抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。

本项目中使用了环绕通知,因为我们需要判断权限之后选择切点函数是否执行。如果权限满足,那么执行切点函数,如果不满足直接返回权限不够的消息。

2、基于自定义Annotation 的Spring AOP 权限验证方法的实现

2.1 配置AOP

在项目中首先按照通常的做法在applicationContext.xml 中配置了代理

在webmvc.xml中配置了<aop:aspectj-autoproxy/>后成功支持AOP。

使用的IDEA编程环境,aop的依赖配置都会被自动载入。

2.2 自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Auth {
 
Level level() default Level.ANON;
public static enum Level {
ANON(0),
LOGIN(1),
READ(2),
EDIT(3),
ADMIN(4);
 
private int value;
 
Level(int value) {
this.value = value;
}
 
public int getValue() {
return value;
}
}
           

2.3实现切面

再利用spring的AOP特性关联一个切面到Auth切点Level。

@Pointcut("@annotation(Auth) || within(@Auth *)")
public void requireAuth() {}
           

定义环绕通知。

@Around("requireAuth()")
public Object auth(ProceedingJoinPoint point) throws Throwable {
Auth auth = getAuth(point);
if (auth != null) {
// 当前level
Integer level = auth.level().getValue();
System.out.println(level);
// TODO 获取用户信息、当前appkey信息
// 获取用户信息和appkey
User user = UserUtils.getUser();
String appkey = (String)ThreadContext.get("appkey");
Boolean hasAuth = authDenied.hasAuth(appkey, level, user.getLogin());
if (hasAuth) {
System.out.println("-----beforeAdvice().invoke-----" + point.getSignature());
Object response = point.proceed(point.getArgs());
System.out.println("-----afterAdvice().invoke-----" + point.getSignature());
System.out.println(response);
return response;
} else {
// throw exception or goto require auth page
return "redirect:/perDenied?appkey=" + appkey;
}
} else {
return point.proceed(point.getArgs());
}
}
           

2.4注解切点

最后在需要权限控制的controller函数上使用注解。

@Auth(level = Auth.Level.EDIT)

3、总结

通过这种AOP的环绕通知的方式,可以非常方便地实现系统的权限控制,开发成本低,并且注解的使用非常灵活,很方便区分是否要进行权限控制。