Spring基础系列12 -- Spring AOP AspectJ

Spring基础系列12 -- Spring AOP AspectJ

转载:http://www.cnblogs.com/leiOOlei/p/3613352.html

本文讲述使用AspectJ框架实现Spring AOP。

再重复一下Spring AOP中的三个概念,

  1. Advice:向程序内部注入的代码。
  2. Pointcut:注入Advice的位置,切入点,一般为某方法。
  3. Advisor:Advice和Pointcut的结合单元,以便将Advice和Pointcut分开实现灵活配置。

AspectJ是基于注释(Annotation)的,所以需要JDK5.0以上的支持。

AspectJ支持的注释类型如下:

  1. @Before
  2. @After
  3. @AfterReturning
  4. @AfterThrowing
  5. @Around

首先定义一个简单的bean,CustomerBo实现了接口ICustomerBo

ICustomerBo.java如下:

package com.lei.demo.aop.aspectj;

public interface ICustomerBo {
    void addCustomer();
    void deleteCustomer();
    String AddCustomerReturnValue();
    void addCustomerThrowException() throws Exception;
    void addCustomerAround(String name);

}

CustomerBo.java如下:

package com.lei.demo.aop.aspectj;

public class CustomerBo implements ICustomerBo {

    public void addCustomer() {
        System.out.println("addCustomer() is running ...");
    }

    public void deleteCustomer() {
        System.out.println("deleteCustomer() is running ...");
    }

    public String AddCustomerReturnValue() {
        System.out.println("AddCustomerReturnValue() is running ...");
        return "abc";
    }

    public void addCustomerThrowException() throws Exception {
        System.out.println("addCustomerThrowException() is running ...");
        throw new Exception("Generic Error");
    }

    public void addCustomerAround(String name) {
        System.out.println("addCustomerAround() is running ,args:"+name);

    }

}

一、      简单的AspectJ,Advice和Pointcut结合在一起

首先没有引入Pointcut之前,Advice和Pointcut是混在一起的

步骤,只需要两步,如下:

  1. 创建一个Aspect类
  2. 配置Spring配置文件

第一步,创建Aspect类

LoggingAspect.java如下:

package com.lei.demo.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {

    @Before("execution(public * com.lei.demo.aop.aspectj.CustomerBo.addCustomer(..))")
    public void logBefore(JoinPoint joinPoint){
        System.out.println("logBefore() is running ...");
        System.out.println("hijacked:"+joinPoint.getSignature().getName());
        System.out.println("**********");
    }

    @After("execution(public * com.lei.demo.aop.aspectj.CustomerBo.deleteCustomer(..))")
    public void logAfter(JoinPoint joinPoint){
        System.out.println("logAfter() is running ...");
        System.out.println("hijacked:"+joinPoint.getSignature().getName());
        System.out.println("**********");
    }
}

解释:

1.  必须使用@AspectLoggingAspect声明之前注释,以便被框架扫描到

2.  此例AdvicePointcut结合在一起,类中的具体方法logBeforelogAfter即为Advice,是要注入的代码,Advice方法上的表达式为Pointcut表达式,即定义了切入点,上例中@Before注释的表达式代表执行CustomerBo.addCustomer方法时注入logBefore代码。

3.  LoggingAspect方法上加入@Before或者@After等注释

4.  "execution(public * com.lei.demo.aop.aspectj.CustomerBo.addCustomer(..))"Aspect的切入点表达式,其中,*代表返回类型,后边的就要定义要拦截的方法名,这里写的的是com.lei.demo.aop.aspectj.CustomerBo.addCustomer表示拦截CustomerBo中的addCustomer方法,(..)代表参数匹配,此处表示匹配任意数量的参数,可以是0个也可以是多个,如果你确定这个方法不需要使用参数可以直接用(),还可以使用(*)来匹配一个任意类型的参数,还可以使用 (* , String),这样代表匹配两个参数,第二个参数必须是String 类型的参数

5.  AspectJ表达式,可以对整个包定义,例如,execution(* com.lei.service..*.*(..))表示切入点是com.lei.sevice包中的任意一个类的任意方法,具体的表达式请自行百度。

第二步,配置Spring配置文件,

配置Spring-AOP-AspectJ.xml文件,如下:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy/>

    <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>

    <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />

</beans>

解释:

1.      <aop:aspectj-autoproxy/>启动AspectJ支持,这样Spring会自动寻找用@Aspect注释过的类,其他的配置与spring普通bean配置一样。

测试:

执行App.java如下:

package com.lei.demo.aop.aspectj;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {

        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                new String[] { "Spring-AOP-AspectJ.xml" });
        ICustomerBo customer=(ICustomerBo)appContext.getBean("customerBo");

customer.addCustomer();

        System.out.println("-------------------------------------------");

customer.deleteCustomer();

   }
}

结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

-------------------------------------------

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

二、      将Advice和Pointcut分开

需要三步,

  1. 创建Pointcut
  2. 创建Advice
  3. 配置Spring的配置文件

第一步,PointcutsDefinition.java定义了Pointcut,如下:

package com.lei.demo.aop.aspectj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class PointcutsDefinition {

    @Pointcut("execution(* com.lei.demo.aop.aspectj.CustomerBo.*(..))")
    public void customerLog() {
    }
}

解释:

1. 类声明前加入@Aspect注释,以便被框架扫描到。

2. @Pointcut是切入点声明,指定需要注入的代码的位置,如上例中指定切入点为CustomerBo类中的所有方法,在实际业务中往往是指定切入点到一个逻辑层,例如 execution (* com.lei.business.service.*.*(..)),表示aop切入点为service包中所有类的所有方法,具体的表达式后边会有介绍。

3. 方法customerLog是一个签名,在Advice中可以用此签名代替切入点表达式,所以不需要在方法体内编写实际代码,只起到助记功能,例如此处代表操作CustomerBo类时需要的切入点。

第二步,创建Advice类

LoggingAspect.java如下:

package com.lei.demo.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {

    @Before("com.lei.demo.aop.aspectj.PointcutsDefinition.customerLog()")
    public void logBefore(JoinPoint joinPoint){
        System.out.println("logBefore() is running ...");
        System.out.println("hijacked:"+joinPoint.getSignature().getName());
        System.out.println("**********");
    }

@After("com.lei.demo.aop.aspectj.PointcutsDefinition.customerLog()")
    public void logAfter(JoinPoint joinPoint){
        System.out.println("logAfter() is running ...");
        System.out.println("hijacked:"+joinPoint.getSignature().getName());
        System.out.println("**********");
    }
}

注释:

1.       @Before@After使用PointcutsDefinition中的方法签名代替Pointcut表达式找到相应的切入点,即通过签名找到PointcutsDefinitioncustomerLog签名上的Pointcut表达式,表达式指定切入点为CustomerBo类中的所有方法。所以此例中AdviceLoggingAdvice,为CustomerBo中的所有方法都加入了@Before@After两种类型的两种操作。

2.       对于PointcutsDefinition来说,主要职责是定义Pointcut,可以在其中第一多个切入点,并且可以用便于记忆的方法签名进行定义。

3.       单独定义Pointcut的好处是,一是通过使用有意义的方法名,而不是难读的Pointcut表达式,使代码更加直观;二是Pointcut可以实现共享,被多个Advice直接调用。若有多个Advice调用某个Pointcut,而这个Pointcut的表达式在将来有改变时,只需修改一个地方,维护更加方便。

第三步,配置Spring配置文件,配置文件并没有改变

配置Spring-AOP-AspectJ.xml文件,如下:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy/>

    <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>

    <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />

</beans>

App.java不变,运行测试代码App.java

输出结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

logAfter() is running ...

hijacked:addCustomer

**********

-------------------------------------------

logBefore() is running ...

hijacked:deleteCustomer

**********

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

三、     切入点表达式

Spring3.0.5帮助文档中的切入点表达式如下:

Some examples of common pointcut expressions are given below.

the execution of any public method:

execution(public * *(..))

the execution of any method with a name beginning with "set":

execution(* set*(..))

the execution of any method defined by the AccountService interface:

execution(* com.xyz.service.AccountService.*(..))

the execution of any method defined in the service package:

execution(* com.xyz.service.*.*(..))

the execution of any method defined in the service package or a sub-package:

execution(* com.xyz.service..*.*(..))

any join point (method execution only in Spring AOP) within the service package:

within(com.xyz.service.*)

any join point (method execution only in Spring AOP) within the service package or a sub-package:

within(com.xyz.service..*)

any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:

this(com.xyz.service.AccountService)

‘this‘ is more commonly used in a binding form :- see the following section on advice for how to make the proxy object available in the advice body.

any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:

target(com.xyz.service.AccountService)

‘target‘ is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:

args(java.io.Serializable)
‘args‘ is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

@target(org.springframework.transaction.annotation.Transactional)

‘@target‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:

@within(org.springframework.transaction.annotation.Transactional)

‘@within‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:

@annotation(org.springframework.transaction.annotation.Transactional)

‘@annotation‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has [email protected] annotation:

@args(com.xyz.security.Classified)

‘@args‘ can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

any join point (method execution only in Spring AOP) on a Spring bean named ‘tradeService‘:

bean(tradeService)

any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression ‘*Service‘:

bean(*Service)

 

时间: 2024-10-25 19:27:11

Spring基础系列12 -- Spring AOP AspectJ的相关文章

Spring基础系列9 -- Spring AOP

Spring基础系列9 -- Spring AOP 转载:http://www.cnblogs.com/leiOOlei/p/3556054.html Spring AOP即Aspect-oriented programming,面向切面编程,是作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题.简单地说,就是一个拦截器(interceptor)拦截一些处理过程.例如,当一个method被执行,Spring AOP能够劫持正在运行的method,在met

Spring基础系列10 -- Spring AOP-----------Pointcut, Advisor

Spring基础系列10 -- Spring AOP-----------Pointcut, Advisor 转载:http://www.cnblogs.com/leiOOlei/p/3557643.html 上一篇的Spring AOP Advice例子中,Class(CustomerService)中的全部method都被自动的拦截了.但是大多情况下,你只需要一个方法去拦截一两个method.这样就引入了Pointcut(切入点)的概念,它允许你根据method的名字去拦截指定的method

Spring基础系列8 -- Spring自动装配bean

Spring基础系列8 -- Spring自动装配bean 转载:http://www.cnblogs.com/leiOOlei/p/3548290.html 1.      Auto-Wiring ‘no’ 2.      Auto-Wiring ‘byName’ 3.      Auto-Wiring ‘byType 4.      Auto-Wiring ‘constructor’ 5.      Auto-Wiring ‘autodetect’ Spring Auto-Wiring Be

Spring基础系列6 -- Spring表达式语言(Spring EL)

Spring基础系列6 -- Spring表达式语言(Spring EL) 转载:http://www.cnblogs.com/leiOOlei/p/3543222.html 本篇讲述了Spring Expression Language —— 即Spring3中功能丰富强大的表达式语言,简称SpEL.SpEL是类似于OGNL和JSF EL的表达式语言,能够在运行时构建复杂表达式,存取对象属性.对象方法调用等.所有的SpEL都支持XML和Annotation两种方式,格式:#{ SpEL exp

Spring基础系列14 -- Spring MVC 请求参数的几种获取方法

Spring MVC 请求参数的几种获取方法 转载:http://www.cnblogs.com/leiOOlei/p/3658147.html 一.      通过@PathVariabl获取路径中的参数 @RequestMapping(value="user/{id}/{name}",method=RequestMethod.GET) public String printMessage1(@PathVariable String id,@PathVariable String n

Spring基础系列11 -- 自动创建Proxy

Spring基础系列11 -- 自动创建Proxy 转载:http://www.cnblogs.com/leiOOlei/p/3557964.html 在<Spring3系列9- Spring AOP——Advice>和<Spring3系列10- Spring AOP——Pointcut,Advisor拦截指定方法>中的例子中,在配置文件中,你必须手动为每一个需要AOP的bean创建Proxy bean(ProxyFactoryBean). 这不是一个好的体验,例如,你想让DAO层

Spring基础系列7 -- 自动扫描组件或者bean

Spring基础系列7 -- 自动扫描组件或者bean 转载:http://www.cnblogs.com/leiOOlei/p/3547589.html 一.      Spring Auto Scanning Components —— 自动扫描组件 1.      Declares Components Manually——手动配置component 2.      Auto Components Scanning——自动扫描组件 3.      Custom auto scan comp

Spring基础系列5 -- bean的基本用法

Spring基础系列5 -- bean的基本用法 转载:http://www.cnblogs.com/leiOOlei/p/3532604.html 本篇讲述了Bean的基本配置方法,以及Spring中怎样运用Bean. 主要内容如下: 一.      Spring中Bean的相互引用 二.      Spring中给Bean属性注入value 三.      Spring Inner Bean—内部嵌套的Bean 四.      Spring Bean Scopes—Bean的作用域 五.  

Spring Data 系列(三) Spring+JPA(spring-data-commons)

本章是Spring Data系列的第三篇.系列文章,重点不是讲解JPA语法,所以跑开了JPA的很多语法等,重点放在环境搭建,通过对比方式,快速体会Spring 对JPA的强大功能. 准备代码过程中,保持了每个例子的独立性,和简单性,准备的源码包,下载即可使用.如果,对JPA语法想深入研究的话,直接下载在此基础上进行测试. 前言 Spring Data 系列(一) 入门:简单介绍了原生态的SQL使用,以及JdbcTemplate的使用,在这里写SQL的活还需要自己准备. Spring Data 系