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

Spring切面

2016-07-05 11:43 337 查看

AOP基本概念

通知(Advice):定义了一个切面该干什么(what)以及什么时候应用(when).spring的通知类型有五种:

before:在方法调用之前.

after:在方法调用之后,无论方法是否正常返回.

after-returning:在方法正常返回之后应用.

after-throwing:在方法出现异常时调用.

around:该通知将包住整个方法.

连接点(join point):所有可能应用切面的点,包括方法调用,实例域改变等等.

切点(pointcut):定义了切面的具体应用位置,哪些方法使用了通知之类的.(where)

切面(aspect):是advice与pointcut的结合,它定义了对哪些方法(where)在哪些情况下(when)执行什么操作(what).

引入(INTRODUCTION):用于向已有类中加入新的实例域/方法,使用动态代理实现.

织入(WEAVING):将切面/引入应用到目标对象,并创建代理对象的过程.织入的时机主要有以下3个:

编译期:在目标类编译时将切面织入,需要使用特别的编译器,AspectJ使用该方式.

类加载时期:在类加载器加载字节码时,对字节码进行修改,实现织入.需要特殊的ClassLoader,AspectJ 5支持该织入方式.

运行期:通过创建动态代理(JDK/CGLib),在运行期基于目标对象动态生成代理类,并最终创建代理实例,更多的是复合的意味.这是spring的织入模式.

Spring AOP支持

spring对于AOP的支持主要有以下三种方式:

1. 利用xml的aop命名空间,将一个纯pojo转换为切面.

2. 利用@AspectJ驱动的切面.

3. 直接注入AspectJ的切面.

tips:1,2两种方式,Spring是通过动态代理来实现的.

jdk动态代理的应用如下:将Advice对象以及目标对象注入到InvocationHandler实例中,并在invoke方法中实现切面.(该InvocationHandler实例会传递给代理对象).



spring只支持使用方法作为切点,如果需要对构造器/实例域应用切面只能使用3的AspectJ.

Spring AOP

Spring使用AspectJ表达式定义切点,但是只支持AspectJ的子集。

Spring支持的AspectJ符号如下表

AspectJ符号含义
execution(expression)用于真正筛选方法切点,其他符号基于该结果进行进一步限定,所以是必须的。
this(fullClassName)用于选择那些代理是基于fullClassName的bean(切点/代理对象)
bean(id)限定只对特定id的bean应用切面
@args(AnnotationTypes)用于限定那些参数的类型使用AnnotationTypes进行注解的切点
args(name)用于传递实际参数到通知
within(package.*)限定切点的bean的类型在package.*的范围内
@within(AT)/@target(AT)限定切点所在类必须由@AT进行注解
@annotation(AT)限定切点对应的方法必须有@AT注解
target(targetClass)限定切点所在的类必须是targetClass

基于注解的Spring AOP

必须在配置类中使用@EnableAspectJAutoProxy,允许自动使用切面代理。

通过@Aspect声明一个切面并通过@Component/@Bean创建该切面的实例,使其可以使用。

package high.perform;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Aspect
@Component
public class Audience {
//这里声明了切点表达式,用于匹配切点
//*表示匹配任意返回类型
//+表示匹配Perform及其子类
//..表示参数任意
@Pointcut(value = "execution(* high.perform.Perform+.play(..)) && " +
// args用于传递来自切点的参数,在这里即为Perform.play的参数,
// 需要与playX的参数同名传递(出于多参数考虑)
"args(song) && " +
// 限定为被@T注解的类作为切点所在的类
"@target(TX) && " +
// 选择id为mp的bean,若id不存在则无论bean(id)/!bean(id)过滤结果都为false.
"bean(mp) && " +
// 用于选择那些被@TX注解的切点(方法)
"@annotation(TX)",
// 当使用在非debug模式编译,需要使用argNames属性,该属性必须与playX的参数名一致
argNames = "song")
public void playX(String song){}

//在这里声明一个前置通知,使用前面定义的切点
@Before("playX(song)")
public void beforePerform(String song){
System.out.println(song+" is ready to play.");
System.out.println("Please sit down.");
}
}

interface Perform {
void play(String x);
}
@Component("mp")
@TX
//这里是一个切点
class MusicPerform implements Perform{
@Override
@TX
public void play(@TX String x) {
System.out.println("Sing "+x+" out.");
}
}
@Target({ElementType.TYPE,ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TX{}


环绕通知(Aroud)是最强大的通知,其对应的实现方法需要有一个ProceedingJoinPoint参数.

package high.perform;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Aspect
@Component
public class Audience {
@Pointcut("execution(* *.play(..)) && args(song)")
public void playX(String song){}

@Around("playX(song)")
//ProceedingJoinPoint不需要加入到args中
public void aroundPerform(ProceedingJoinPoint pjp,String song){
try{
System.out.println("this is before advice.");
Object[] params = {song};
//目标方法调用
pjp.proceed(params);
System.out.println("this is after advice.");
}catch (Throwable e){
System.out.println("this is after-throwing.");
}
}

}

interface Perform {
void play(String x);
}
@Component("mp")
@TX
//这里是一个切点
class MusicPerform implements Perform{
@Override
@TX
public void play(@TX String x) {
System.out.println("Sing "+x+" out.");
}
}
@Target({ElementType.TYPE,ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TX{}


利用切面实现引入

@Aspect
@Component()
public class IAIB{
//这里的value必须是一个全限定类名,否则不成功.
//对所有B的子类进行代理,引入接口A的功能,具体实现使用AI.
//这部分引入的功能基于动态代理很容易实现,只要在创建代理时传进去引入的接口即可.
@DeclareParents(value = "high.perform.B+",defaultImpl = AI.class)
public static A a;
}


注入AspectJ切面

wait for need.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 切面 AOP