Spring AOP
2015-08-17 22:09
405 查看
Spring AOP
AOP(Aspect-OrientedProgramming),面向切面编程是对OOP编程方式的一种补充,可以为其他模块或组件提供通用的功能,如记录日志、性能监测、事务管理等。
在Java中代理分为静态代理和动态代理。静态代理需要定义代理类,不具有动态性。动态代理的常见方式有两种:一种是采用JDK的动态代理机制;另一种是采用cglib来生成代理类的字节码文件。
了解了如何为其他类添加功能后,下一步需要确定在那些类、类的那些方法,以及在方法执行前、执行后还是执行前后添加相应的功能。为了确定待增加功能的执行位置,引入了下列概念:
l Aspect(切面),如果说类是面向对象的基本编程单元,则切面就是面向切面编程的基本编程单元。在切面中,声明了为那些类、那些方法添加相应的功能;以及这些功能的执行位置,如方法执行前、方法执行后、方法执行前后等;
l Joint point(加入点),一个具体的执行点,它定义了在哪里加入相关的逻辑功能,如在方法执行时,异常处理时等;
l Advice,特定Joint point处运行的代码,也即为其他类增加的逻辑功能,如记录日志等;
l Pointcut(切入点),定义了一组JointPoint,也就是说一个Advice可以在多个地方织入;
l Weaving(织入)将Aspect加入到程序代码的时机,如编译时,加载时,运行时等。
Spring AOP既可以采用JDK动态代理机制,也可以采用cglib代理。在默认情况下,若一个类实现了一个或若干接口,SpringAOP采用JDK动态代理为该类生成代理类;若一个类没有实现任何接口,则SpringAOP 采用cglib代理(final类,final方法,static方法并不能被代理)。若要显示指定代理类为cglib代理,可在Spring配置文件中添加如下内容:
1) 基于XML配置
2) 基于AspectJ注解
Spring AOP具有以下特点:
l join point只能为方法;
l aop织入是在运行时完成的;
在类前面增加@Aspect注解就可以定义一个切面,如下
@Aspect
public
class PermissionInterceptor
{
}
注意:
l 虽然Spring AOP的注解是基于AspectJ PointcutExpression Language,但SpringAOP底层的机制并不是AspectJ,SpringAOP和AspectJ是两个不同的AOP框架;
l 只要一个bean被切入一个或多个切面,Spring容器会自动为该bean生成代理类,以确保织入的功能正常执行;
l 切面类是不能切入其他切面,也即切面类不能被自动代理。
l Before advice
l After return advice
l After throwing advice
l After finally advice
l Around advice
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修饰的方法。
@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;
}
}
@AfterReturning(pointcut="execution(*org.ssl.book.web.aop..*.*(..))",returning="retVal")
public
void afterReturning(String retVal)
{
System.out.println("returnvalue="+retVal);
}
public
void afterThrowing(Exception ex)
{
}
public
void afterFinally()
{
}
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;
}
@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);
}
}
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;
}
@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;
}
}
<!--通知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();
}
}
Spring AOP存在以下缺点:
l Spring AOP 是基于代理模式,所以final类、final方法、static方法是不能被覆盖的;
l Spring AOP的join point只能为方法;
l Spring AOP在运行时织入;
若存在以上情况,可以考虑使用AspectJ框架,因为AspectJ支持编译期织入且不需要生成代理。在使用AspectJ时,需要注意Advice循环,因为AspectJAdvice 可以应用于POJO之上,而它可能将Advice应用于一个已配置的切面之上,将会导致无限循环。
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应用于一个已配置的切面之上,将会导致无限循环。
相关文章推荐
- Java跨平台开发神器之JNI
- leetcode-110:判断平衡二叉树 Java
- leetcode-110:判断平衡二叉树 Java
- java中String的常用方法
- [笔记][Java7并发编程实战手册]3.3 资源的多副本并发访问控制Semaphore
- Java静态语句块、语句块、构造方法执行顺序
- Struts2中的ModelDriven机制及其运用
- Mark Knowledge of Java Thread (2): Notify and Wait
- Spring注解的步骤
- java并发-读书笔记
- java基础及多线程
- java内存管理:垃圾回收机制
- Java日志框架——Logback的Filter
- java实例 N的阶乘末尾有多少个0
- Java开发中的23种设计模式详解
- 【Java技术位】——代理模式及其事务包
- Eclipse maven 插件之helloword
- JavaEE细节问题05——Cookie和Session
- Spring MVC 教程,快速入门,深入分析
- java 之 语言基础