您的位置:首页 > 编程语言 > Java开发

Spring AOP

2015-08-17 22:09 405 查看
Spring AOP

AOP(Aspect-OrientedProgramming),面向切面编程是对OOP编程方式的一种补充,可以为其他模块或组件提供通用的功能,如记录日志、性能监测、事务管理等。

1、 基本概念

AOP的思想主要是为其他类增加通用的功能,或者说增强原有类的相关功能,那么如何为原有类增加其他功能呢?一种简单有效的方式就是采用代理设计模式,在代理类中添加相应的功能。

在Java中代理分为静态代理和动态代理。静态代理需要定义代理类,不具有动态性。动态代理的常见方式有两种:一种是采用JDK的动态代理机制;另一种是采用cglib来生成代理类的字节码文件。

了解了如何为其他类添加功能后,下一步需要确定在那些类、类的那些方法,以及在方法执行前、执行后还是执行前后添加相应的功能。为了确定待增加功能的执行位置,引入了下列概念:

l Aspect(切面),如果说类是面向对象的基本编程单元,则切面就是面向切面编程的基本编程单元。在切面中,声明了为那些类、那些方法添加相应的功能;以及这些功能的执行位置,如方法执行前、方法执行后、方法执行前后等;

l Joint point(加入点),一个具体的执行点,它定义了在哪里加入相关的逻辑功能,如在方法执行时,异常处理时等;

l Advice,特定Joint point处运行的代码,也即为其他类增加的逻辑功能,如记录日志等;

l Pointcut(切入点),定义了一组JointPoint,也就是说一个Advice可以在多个地方织入;

l Weaving(织入)将Aspect加入到程序代码的时机,如编译时,加载时,运行时等。

2、 机制

Spring AOP是基于代理机制的,默认采用JDK动态代理机制来生成代理类。JDK动态代理机制是基于接口的,而不能为类产生代理;当为类生成代理时需要使用cglib代理。

Spring AOP既可以采用JDK动态代理机制,也可以采用cglib代理。在默认情况下,若一个类实现了一个或若干接口,SpringAOP采用JDK动态代理为该类生成代理类;若一个类没有实现任何接口,则SpringAOP 采用cglib代理(final类,final方法,static方法并不能被代理)。若要显示指定代理类为cglib代理,可在Spring配置文件中添加如下内容:

1) 基于XML配置

<aop:config proxy-target-class="true">

<!-- other beans defined here... -->

</aop:config>


2) 基于AspectJ注解

<aop:aspectj-autoproxy proxy-target-class="true"/>


Spring AOP具有以下特点:

l join point只能为方法;

l aop织入是在运行时完成的;

3、 Aspect

配置SpringAOP既可以采用xml配置文件,也可以采用基于AspectJ注解的方式(本文采用注解方式,xml配置方式请参考Spring帮助文档)。在使用AspectJ注解需要开启@AspectJ支持,在Spring配置文件中添加如下内容:

<aop:aspectj-autoproxy/>


在类前面增加@Aspect注解就可以定义一个切面,如下

@Aspect
public
class
PermissionInterceptor
{
}

注意:

l 虽然Spring AOP的注解是基于AspectJ PointcutExpression Language,但SpringAOP底层的机制并不是AspectJ,SpringAOP和AspectJ是两个不同的AOP框架;

l 只要一个bean被切入一个或多个切面,Spring容器会自动为该bean生成代理类,以确保织入的功能正常执行;

l 切面类是不能切入其他切面,也即切面类不能被自动代理。

4、 Advice

在SpringAOP中,Advice有:

l Before advice

l After return advice

l After throwing advice

l After finally advice

l Around advice

4.1、切入点

在声明Advice之前,必须要了解切入点的声明方式。在SpringAOP中切入点声明采用的是AspectJPointcut Expression Language,语法格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)             throws-pattern?)


l modifiers-pattern:方法操作的权限;

l ret-type-pattern:返回值;

l declaring-type-pattern:方法所在的包;

l name-pattern:方法名;

l param-pattern:参数名;

l throws-pattern:异常

其中,除了ret-type-pattern和name-pattern之外,其他都是可选的。一些常用的表达式如下:

l 包中的任意类,任意方法

execution(*org.ssl.service.*.*(..))

注意:(..)匹配任意多个参数,而(*)匹配任意一个参数;如(*,String)匹配两个参数,第一个为任意类型,第二个为String类型

l 包及其子包种的任意类、任意方法

execution(* org.ssl.service..*.*(..))

l 传递参数

execution(*org.ssl.service..*.*(..) && args(account,..))

在SpringAOP中除了可以使用execution表达式外,还可以使用@annotation、within等,如@annotation(org.springframework.transaction.Transactional)表示任何被@Transactional修饰的方法。

4.2、Before Advice

Before Advice 通过@Before来声明,如下:

@Aspect
public
class
LogInterceptor
{
@Before("execution(* org.ssl.service..*.*(..))")
public
void
log()
{

}
}

注:为简便起见,可以把一起声明切点和Advice,也可以采用@PointCut注解来单独声明切点,如下
@Component
@Aspect
public
class
LogInterceptor implements Ordered
{
@Pointcut("execution(*org.ssl.book.web.aop..*.*(..))")
public
void
cutpoint()
{

}
@Around("cutpoint()")
public
void
around(ProceedingJoinPoint pjp)
{

}

@Override
public
int
getOrder()
{
return 1;
}
}

4.3、After returning advice

After return advice 通过@AfterReturning来声明,如下:

@AfterReturning(pointcut="execution(*org.ssl.book.web.aop..*.*(..))",returning="retVal")
public
void
afterReturning(String retVal)
{
System.out.println("returnvalue="+retVal);
}

4.4、After throwing advice

@AfterThrowing(pointcut="execution(*org.ssl.book.web.aop..*.*(..))",throwing="ex")
public
void
afterThrowing(Exception ex)
{
}

4.4、After finally advice

@After("execution(* org.ssl.book.web.aop..*.*(..))")
public
void
afterFinally()
{
}

4.5、Around Advice

Around Advice,在方法执行前和执行后都有机会工作,而且可以阻止方法的执行。

Around Advice通过@Around来声明,被@Around修饰的方法必须满足:方法的第一个参数为ProceedingJoinPoint,调用ProceedingJoinPoint实例的proceed方法,可以执行被代理实例的相应方法,如下

@Around("execution(* org.ssl.book.web.aop..*.*(..))")
public Objectaround(ProceedingJoinPoint
pjp)
{
ObjectretVal=null;
try
{
System.out.println("before----------");
retVal =
pjp.proceed();
System.out.println("after------------");
}
catch (Throwable
e)
{
e.printStackTrace();
}
return
retVal;
}

4.6、传递参数

@Component
@Aspect
public
class
LogInterceptor
{
@Before("execution(*org.ssl.book.web.aop..*.*(..))&&args(account,..)")
public
void
log(Account account)
{
System.out.println("-------------loging-------");
System.out.println("para="+account);
}
}

4.7、获取JoinPoint

任何类型的Advice在声明时第一个参数都可以为org.aspectj.lang.JoinPoint的实例(注意,aroundadvice 第一参数必须要求为ProceedingJoinPoint,其为JoinPoint的子类)。JointPoint提供了一些有用的方法,如下:

lgetArgs,获得方法的参数

lgetThis,获取代理对象

lgetTarget,获取被代理的对象

lgetSignature,获取方法的签名,以便通过MethodSignature获取Method

@Around("execution(* org.ssl.book.web.aop..*.*(..))")
public Object around(ProceedingJoinPoint
pjp)
{
ObjectretVal=null;
try
{
//获取方法签名
MethodSignature signature=(MethodSignature)
pjp.getSignature();
//获取方法
Method method=signature.getMethod();
System.out.println(method.getName());
retVal =
pjp.proceed();
}
catch (Throwable
e)
{
e.printStackTrace();
}
return
retVal;
}

4.8、Advice Order

若一个joinpoin有多个切面,那么切面执行的顺序是如何确定的?切面类可以实现org.springframework.core.Ordered接口,如下

@Component
@Aspect
public
class
LogInterceptor implements Ordered
{

@Around("execution(* org.ssl.book.web.aop..*.*(..))")
public Object around(ProceedingJoinPoint
pjp)
{
ObjectretVal=null;
try
{
System.out.println("loginterceptor......");
//获取方法签名
MethodSignature signature=(MethodSignature)
pjp.getSignature();
//获取方法
Method method=signature.getMethod();
System.out.println(method.getName());
retVal =
pjp.proceed();
}
catch (Throwable
e)
{
e.printStackTrace();
}
return
retVal;
}

@Override
public
int
getOrder()
{
return 1;
}
}

4.9、@Controller AOP Proxy

有的时候可能会需要@Controller的AOP代理类,此时需要设置代理为cglib代理,否则aop并不会起作用,如下

<!--通知spring使用cglib而不是jdk的来生成代理方法
AOP可以拦截到Controller -->
<aop:aspectj-autoproxy proxy-target-class="true" />

若采用Spring AOP拦截控制器类,往往需要在Advice方法体内访问Servlet API,可采用RequestContextHolder来获取,如下

@Around("execution(*org.ssl.book.web.controller..*.*(..))")
public
void
handler(ProceedingJoinPoint pjp)
{
HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
System.out.println(request);
MethodInvocationProceedingJoinPoint
methodJoinPoint=(MethodInvocationProceedingJoinPoint)pjp;
MethodSignature methodSignature=(MethodSignature)methodJoinPoint.getSignature();
Method method=methodSignature.getMethod();
System.out.println(method.getName());
System.out.println("aop-----------------");
try
{
pjp.proceed();
}
catch (Throwable
e)
{
e.printStackTrace();
}
}

5、 AspectJ

Spring AOP和AspectJ是两个不同的AOP框架。虽然Spring可以与AspectJ无缝集成(参考Spring文档),但是若SpringAOP可以完成的功能,首选仍然是SpringAOP。当SpringAOP无法满足时,才考虑使用AspectJ。

Spring AOP存在以下缺点:

l Spring AOP 是基于代理模式,所以final类、final方法、static方法是不能被覆盖的;

l Spring AOP的join point只能为方法;

l Spring AOP在运行时织入;

若存在以上情况,可以考虑使用AspectJ框架,因为AspectJ支持编译期织入且不需要生成代理。在使用AspectJ时,需要注意Advice循环,因为AspectJAdvice 可以应用于POJO之上,而它可能将Advice应用于一个已配置的切面之上,将会导致无限循环。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: