使用注解方式实现Spring中AOP笔记(菜鸟)

AOP-面向切面编程

初学spring4.3,喜欢使用纯java代码和注解方式来进行Spring内的一些相关配置工作,AOP作为Sping框架的一个重要特点,需要深入学习,本问记录了一个菜鸟初次接触Sping-AOP的学习过程,多有谬误,还请各路大神多多指教:


什么是AOP?有什么用?

AOP即面向切面编程。在应用程序中,有一些功能会散布在应用中的多处(如日志/安全/事务),这些功能十分重要但却又与业务逻辑没有多大关联,通常我们称呼这些功能为“横切关注点”。)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP)所要解决的问题。

AOP术语

通知,切面,切点,连接点,织入,引入

切面 是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。

通知 切面的工作被称为通知。通知定义了切面是什么以及何时作用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。

切点 定义了应用通知的地点。

JUST DO IT!

1.通过切点来选择连接点

Spring只支持方法级别的连接点
1.1 定义一个接口,对于一场演出而言,表演是很重要的:

package concert;

public interface Performance {
    public void perform();
}

1.2 这次演出Duck将上台演出,他是接口的一个实现:

package concert;

public class Duckperform implements Performance{

    @Override
    public void perform() {
        System.out.println("Duck sings: I believe i can fly!");
    }
}

我们需要编写Performance中perform方法触发的通知,即当程序调用perform()时触发通知,需要通过该方法定义一个切点:

execution(* concert.Duckperform.perform(..))

我们使用execution()指示器选择Performance的perform()方法。方法表达式以“*”号开始,表明了我们不关心方法返回值的类型然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号(..)表明切点要选择任意的perform()方法,无论该方法的入参是什么。

现在假设我们需要配置的切点仅匹配concert包。在此场景下,可以使用within()指示器来限制匹配(与/或/非):

execution(* concert.Duckperform.perform(..))&&within(concert.*)

Spring还引入了一个新的bean()指示器,它允许我们在切点表达式中使用bean的ID来标识bean。bean()使用beanID或者bean名称作为参数来限制切点只匹配特定的bean(and和!and)。

execution(* concert.Duckperform.perform(..)) and bean('ID')

2.使用注解来创建切面

2.1 创建切面
如果一场演出没有观众的话,那不能称之为演出。从演出的角度来看,观众是非常重要的,但是对演出本身的功能来讲,它并不是核心,这是一个单独的关注点。因此,将观众定义为一个切面,并将其应用到演出上就是较为明智的做法.

package concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class Audience {
    //@Pointcut注解能够在一个@AspectJ切面内定义可重用的切点。
    @Pointcut("execution(* concert.Duckperform.perform(..))")
        public void performance(){}


    //实用一个环绕通知代替之前多个不同的前置通知和后置通知
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("Sliencing cell phones!");
            System.out.println("Taking seats!");
            jp.proceed();
            System.out.println("CLAP CLAP CLAP!!!");
        } catch (Throwable e) {
            System.out.println("Demanding a refund!");
        }
    }


    /*
    @Before("performance()")
    public void sillenceCellPhones(){
        System.out.println("Sillencing cell phones!");  
    }

    @Before("performance()")
    public void takeSeats(){
        System.out.println("Taking seats!");
    }

    @AfterReturning("performance()")
    public void applause(){
        System.out.println("CLAP CLAP CLAP!");
    }

    @AfterThrowing("performance()")
    public void demandRefund(){
        System.out.println("Demanding a refund!");
    }
    */
}

在这个切面中,我使用了@Around来申明通知方法,在Spring中使用注解来申明通知方法有以下几种:
@After:通知方法会在目标方法返回或抛出异常后调用
@AfterReturning:通知方法会在目标方法返回后调用
@AfterThrowing:通知方法会在目标方法抛出异常后调用
@Around:通知方法会将目标方法封装起来
@Before:通知方法会在目标方法调用之前执行

2.2 切面配置
使用注解方式来进行配置工作:

 package concert;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy   //启用自动代理
@ComponentScan
public class AopConfig {

    @Bean(name="Duck") 
    public Performance duckperform(){
        return new Duckperform();
    }

    @Bean
    public Audience audience(){
        return new Audience();
    }
}

一个简单的配置已经完成,接下来测试是否配置成功。

3.测试
测试代码如下:

package concert;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
        Performance dk=(Performance) context.getBean("Duck");
        dk.perform();
    }

}

测试结果:
Sliencing cell phones!
Taking seats!
Duck sings: I believe i can fly!
CLAP CLAP CLAP!!!

在测试代码中,有一句代码值得思考:

Performance dk=(Performance) context.getBean("Duck");

按自己的想法,如果将其改为:

Duckperform dk=(Duckperform) context.getBean("Duck");

程序运行结果应该也会相同,然而事实并非如此,在开启了自动代理(@EnableAspectJAutoProxy)的情况下,程序将报错;把“Duck”改为Duckperform.class也会报错。查资料得知,这与在Spring中设置的代理方式有关:基于接口的还是基于类的代理被创建。默认情况下基于接口代理,则修改后的代码不可用,如果改为基于类的代理( @EnableAspectJAutoProxy(proxyTargetClass=true)),程序可正常运行。关于代理方式问题,值得进一步理解学习。

注:在Spring本身是不支持@Aspect这种注解的,如需实现文中的配置,需要添加aspectj库,该库可以在ecilipse官网找到,贴下下载地址。
http://www.eclipse.org/aspectj/downloads.php

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值