使用Spring进行面向切面(AOP)编程

http://www.java63.com/spring/annotation_aop.html
使用Spring进行面向切面(AOP)编程


要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:

<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-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
</beans>



如果要使用切面编程(AOP),还需要下列jar文件
lib/aspectj/aspectjweaver.jar和aspectjrt.jar
lib/cglib/cglib-nodep-2.1_3.jar


我们使用Spring框架进行AOP编程的时候呢,Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:
1.基于XML配置方式进行AOP开发。
2.基于注解方式进行AOP开发。

对这两种方式都进行下介绍,先学习基于注解方式进行AOP开发
先是beans.xml里,要引入AOP的命名空间,打开配置项

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
        <aop:aspectj-autoproxy/>
        <!--
            打开配置项,这个配置项是对@Aspectj这个注解进行支持
            前面已经说过了,注解本身是不能干活的,注解之所以能干活是因为后面有处理器对其进行处理
            这个配置相当于为我们将要使用的@Aspectj注解提供了解析的功能
        -->
</beans>



接下来的工作是,定义一个切面,在切面里面定义切入点,所谓切入点就是说我们要对业务bean里面的哪些方法进行拦截,我们还要定义通知,所谓通知,就是说我们拦截到方法后我们需要做的工作。

新建业务bean用作测试,PersonService.java

package cn.itcast.service; 
public interface PersonService { 
    public void save(String name); 
    public void update(String name, Integer id); 
    public String getPersonName(Integer id); 
}


PersonServiceBean.java

package cn.itcast.service.impl; 
 
import cn.itcast.service.PersonService; 
 
public class PersonServiceBean implements PersonService { 
 
    public String getPersonName(Integer id) { 
        return "xxx"
    } 
 
    public void save(String name) { 
        System.out.println("我是save()方法"); 
    } 
 
    public void update(String name, Integer id) { 
        System.out.println("我是update()方法"); 
    } 
 
}


用作测试用的业务bean已经开发好了,接着就要进行AOP的开发,因为我们采用的是基于注解的方式来实现AOP功能,先了解一些概念

基于注解方式声明切面


@Aspect 
public class LogPrint { 
    @Pointcut("execution(* cn.itcast.service..*.*(..))"
    private void anyMethod() {}//声明一个切入点 
    @Before("anyMethod() && args(userName)")//定义前置通知 
    public void doAccessCheck(String userName) { 
    } 
    @AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知 
    public void doReturnCheck(String revalue) { 
    } 
    @AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知 
    public void doExceptionAction(Exception ex) { 
    } 
    @After("anyMethod()")//定义最终通知 
    public void doReleaseAction() { 
    } 
    @Around("anyMethod()")//环绕通知 
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { 
        return pjp.proceed(); 
    } 
}


在这里@Pointcut("execution(* cn.itcast.service..*.*(..))")
private void anyMethod() {}//声明一个切入点
切入点用来定义我们要拦截的方法,因为Spring只支持方法的拦截,所以这里说的是拦截方法
切 入点的定义里面,使用了一个AOP的表达式语言@Pointcut("execution(* cn.itcast.service..*.*(..))"),这个语言是比较灵活的,功能也是比较强大的,在实际使用中呢几乎80%多我们使用的表达式 都是类似这种写法,给大家介绍下切入点的表达式的含义。
execution代表执行,执行业务方法的时候我要进行拦截;

第一个*号代表的是返回值的类型,通配符*号表示的话就代表任何的返回值类型;
cn.itcast.service代表的是包名,也就是说你要对哪些包底下的类进行拦截;
然后是两个点..,这两个点代表对cn.itcast.service的子包底下的类也要进行拦截,如果不定义这两个点的话,那么iFiona祛痘CC霜它只会对service包底下的类进行拦截,如果定义了两个点,那么即会对service包底下的类进行拦截,也会对service子包底下的类进行拦截;
第二个*号代表的是类,你要对哪个类进行拦截,*号代表所有类,也就是说service子包底下的所有类进行拦截
第三个*号代表方法,代表所有的方法;
最后的括号里面有两个点(..),这两个点代表方法的参数可以任意,有可以,没有也可以。一个也可以,多个也可以,代表随意


MyInterceptor.java

package cn.itcast.service; 
 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
/** 
 * 切面 
 */
 
@Aspect //用来指明这个类是一个切面 
public class MyInterceptor { 
    @Pointcut("execution (* cn.itcast.service.impl.PersonServiceBean.*(..))"
    //切入点的表达式写完了,接下来要为切入点定义一个名称,这个名称的定义有点怪怪的 
    //采用的是方法定义的形式来定义它的名称的 
    private void anyMethod() {}//声明一个切入点,anyMethod()就是切入点的名称,括号也带上 
 
    @Before("anyMethod()")  //里面应该填入切入点的名称 
    public void doAccessCheck() { 
        System.out.println("前置通知"); 
    } 
}


这样切面就定义好了,在切面里面有我们关注的切入点,并且有我们关注的通知,我们的代码很简洁,当我们定义好切面之后就要注意了,要把MyInterceptor这个切面交给Spring容器管理,如果你不交给Spring管理的话,你定义完了它也不会起效果。
要交给Spring管理的话,有两种方式,一种方式是在配置文件里面通过元 素把它交给Spring管理,还有种方式是通过扫描的方式,加一个注解@Component在public class MyInterceptor{上面的方式把这个bean交给Spring管理。现在并没有开启自动扫描的方式,所以这里就采用基于XML配置的方式把 bean交给Spring管理

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
        <aop:aspectj-autoproxy/>
        <!--
            打开配置项,这个配置项是对@Aspectj这个注解进行支持
            前面已经说过了,注解本身是不能干活的,注解之所以能干活是因为后面有处理器对其进行处理
            这个配置相当于为我们将要使用的@Aspectj注解提供了解析的功能
        -->
        <bean id="myInterceptor" class="cn.itcast.service.MyInterceptor"/>
        <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
</beans>


现在就来开发客户端,在客户端里面我们调用PersonServiceBean这个业务bean的save方法,看一下MyInterceptor这个切面能否帮我们拦截到这个方法,然后在拦截到之前先执行前置通知代码,然后再执行业务方法save()

新建一个单元测试
SpringAOPTest.java

package junit.test; 
 
import org.junit.BeforeClass; 
import org.junit.Test; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
import cn.itcast.service.PersonService; 
 
public class SpringAOPTest { 
 
    @BeforeClass 
    public static void setUpBeforeClass() throws Exception { 
    } 
 
    @Test public void interceptorTest(){ 
        ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); 
        PersonService personService = (PersonService)cxt.getBean("personService"); 
        personService.save("xx"); 
    } 
}



运行单元测试代码,控制台输出
前置通知
我是save()方法

posted @ 2012-10-31 11:36  haiwei.sun  阅读(255)  评论(0编辑  收藏  举报
返回顶部