7、spring 入门—Spring对AspectJ的支持

来源:互联网 发布:手机防吸费软件 编辑:程序博客网 时间:2024/05/29 07:37
1.Spring对AspectJ的支持
@AspectJ的风格类似纯java注解的普通java类
Spring可以使用AspectJ来做切入点解析
AOP的运行时仍旧是纯的SpringAOP,对AspectJ的编译器或者织入无依赖性
2.对@AspectJ支持可以使用XML或Java风格的配置
@Configuration
@EnableAspectJAutoProxy
public class AppConfig{}
<aop:aspectj-autoproxy/>
3.@AspectJ切面使用@AspectJ注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用。
用@AspectJ注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明
4.@AspectJ注解是不能够通过类路径自动检测发现的,所以需要配合使用@Conponent注释或者在XML中配置bean
5.一个类中的@AspectJ注解标始它为一个切面,并且将自己从自动代理中排除
6.AspectJ是编译期的AOP,检查代码并匹配连接点与切入点的代价是昂贵的。
一个好的切入点应该包括以下;
①选择特定类型的连接点,如:execution,get,set,call,handler
②确定连接点范围,如:within,withincode

③匹配上下文信息,如;this.targe,@annotation

一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
定义一个名为“anyOldTransfer”方法,这个切入点将匹配任何名为“transfer”的方法的执行,如:
@Pointcut("execution(* transfer(..))")
private void anyOldTransfer(){}

组合pointcut:
切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式
通过组合,可以建立更加复杂的切入点表达式

通知
1.@Before("表示式、连接点方法名()")
2.@AfterReturing 可以使用returing绑定返回值 Object
3.@AfterThrowing throwing同上
4.@After finally
5.@Around 第一个参数必须是ProceedingJoinPoint proceed(),proceed(Object[])

//相关代码——(上):
@spring-aop-aspectj.xml
<context:component-scan base-package="com.imooc.aop.aspectj" />
<aop:aspectj-autoproxy />


@UnitTestBase.java
//省略代码...


@MoocBiz.java
package com.imooc.aop.aspectj.biz;
@Service
public class MoocBiz {
public String save(String arg){
System.out.println("MoocBiz save:"+arg);
// throw new RuntimeException("Save failed!");
return "Save success!";//作用在AfterReturning或Around Object obj=pjp.proceed();
}
}


@TestAspectJ.java
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAspectJ extends UnitTestBase{
public TestAspectJ(){
super("classpath:spring-aop-aspectj.xml");
}
@Test
public void test(){
MoocBiz biz=getBean("moocBiz");
biz.save("This is test.");
}
}


@MoocAspect.java
package com.imooc.aop.aspectj;
@Component
@Aspect
public class MoocAspect {
@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))")//方法二
public void pointcut(){}

@Pointcut("within(com.imooc.aop.aspectj.biz.*)")
public void bizPointcut(){}

//相关代码——(下):
// @Before("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))")//方法一
@Before("pointcut()")//方法二
public void before(){
System.out.println("Before.");
}

@AfterReturning(pointcut="bizPointcut()",returning="returnValue")
public void afterReturning(Object returnValue){
System.out.println("AfterReturning:"+returnValue);
}

@AfterThrowing(pointcut="pointcut()",throwing="e")
public void afterThrowing(RuntimeException e){
System.out.println("AfterThrowing:"+e.getMessage());
}

@After("pointcut()")
public void after(){
System.out.println("After.");
}

@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
syso("Around 1.");
Object obj=pjp.proceed();//调用真正的方法
System.out.println("Around 2.");
System.out.println("Around:"+obj);
return obj;
}
}


//输出:
Around 1.
Before.
MoocBiz save:This is test.
Around 2.
Around:Save success!
After.
AfterReturning:Save success!

Spring 中通知的类型:
Around     org.aopalliance.intercept.MethodInterceptor   栏截对目标对象方法的调用 
Before      org.springframework.aop.MethodBeforAdvice             在目标方法被调用之前调用     
After          org.springframework.aop.AfterReturningAdvice          当目标方法被调用之后调用
Thorws    org.springframework.aop.ThrowsAdvice                       当目标方法抛出异常时调用
程序正常执行顺序:
执行前 -环绕通知      Around
执行前 - 通知     Before
POINT - 切入点 方法前执行                               @Before
POINT - 切入点 环绕方法前执行                       @Around
切入点异常处理通知                                             @AfterThrowing
异常处理通知                                                          Thorws

给advice传递参数
1.args()
(即在通知中获取符合切入点的方法中的参数)
2.自定义注解
(即在通知中获取符合切入点的方法被注解标注的值)


使用args():


//定义目标对象
@Component
public class A{

//符合切入点的方法
public void print(String name){

System.out.println(name);


}
}


//定义切面
@Aspect
@Component
public class asp{

@Pointcut(execution(* com.own.A.*(..)) && args(arg))
public void pointcut1(String arg){}


//使用args()的方式要保证args()括号内的参数名要与定义切入点的参数名一致.


//@Before注解中引入切入点并配上参数(不需要类型).
    @Before("pointcut1(arg)")
    public void before(String arg){


    System.out.println("before:"+arg);
    
    }


 //或者
  @Before(execution(* com.own.A.*(..)) && args(arg))
  public void before(String arg){


  System.out.println("before:"+arg);


}
}


<beans..>
<aop:aspectj-autoproxy></aop:aspectj>
</beans>


测试类中:
A a=(A)context.getBean("a");
a.printl("param")


控制台显示:
before:param
param

给advice传递参数


@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}


@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}


@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
    AuditCode value();
}


@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}

Advice的参数及泛型
.Spring AOP可以处理泛型类的声明和使用方法的参数


public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection>T> param);
}


@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
    // Advice implementation
}


@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
    // Advice implementation
}

Advice参数名称
.通知和切入点注解有一个额外的“argNames”属性,它可以用来指定所注解的方法的参数名


@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code and bean
}
.如果第一参数是JoinPoint,ProceedingJoinPoint,JoinPoint.StaticPart,那么可以忽略它


@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
}

Introductions
.允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象
.Introduction使用@DeclareParents进行注解,这个注解用来定义匹配的类型拥有一个新的parent

Introductions
.例如:给定一个接口UsageTracking ,并且该接口拥有DefaultUsageTracked的实现,接下来的切面声明了所有的service接口的实现都实现了UsageTracked接口


@Aspect
public class UsageTracking {


    @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
    public static UsageTracked mixin;


    @Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
    public void recordUsage(UsageTracked usageTracked) {
        usageTracked.incrementUseCount();
    }


}

切面实例化模型
.这是一个高级主题
.“perthis”切面通过制定@Aspect注解perthis子句实现
.每个独立的service对象执行时都会创建一个切面实例
.service对象的每个方法在第一次执行的时候创建切面实例,切面service对象失效的同时失效


@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {


    private int someState;


    @Before(com.xyz.myapp.SystemArchitecture.businessService())
    public void recordServiceUsage() {
        // ...
    }


}

AspectJ小结
.配置@AspectJ
.Pointcut
.Adivce


0 0
原创粉丝点击