Spring AOP

来源:互联网 发布:淘宝网址导航 编辑:程序博客网 时间:2024/06/05 15:05

面向切面编程(Spring AOP)

为了将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,Spring提供了面向切面的编程方式,也称Spring AOP,这有效的减少了系统间的重复代码,达到了模块间的松耦合目的。

AOP,Aspect Oriented Programing,即面向切面编程。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,主要体现在事务处理、日志管理、权限控制、异常处理等方面。

目前流行的AOP框架有两个,分别是Spring AOP和AspectJ。AspectJ是一个基于Java语言的AOP框架。

AOP术语

  • Joinpoint(连接点):是指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法。
  • Pointcut(切入点):是指要对哪些Joinpoint进行拦截,即被拦截的拦截点。
  • Advice(通知):是指拦截到Joinpoint之后要做的事情,即对切入点增强的内容。
  • Target(目标):是指代理的目标对象。
  • Weaving(织入):是指把增强代码应用到目标上,生成代理对象的过程。
  • Proxy(代理):是指生成的代理对象。
  • Aspect(切面):是切入点和通知的结合。

AspectJ开发

基于XML的声明式AspectJ

导入AOP的的jar包

  • spring-aop-3.2.0.RELEASE.jar:是Spring为AOP提供的实现。
  • com.springsource.org.aopalliance-1.0.0.jar:是AOP联盟提供的规范。

导入AsprctJ相关的jar包

  • spring-aspects-3.2.0.RELEASE.jar:Spring为AspectJ提供的实现。
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:AspectJ提供的规范。

建立UserDao.java

package cn.itcast.dao;public interface UserDao {    void save();    void update();    void delete();    void find();}

UserDaoImpl.java

package cn.itcast.dao;public class UserDaoImpl implements UserDao{    @Override    public void save() {        System.out.println("save添加用户");    }    @Override    public void update() {        System.out.println("update修改用户");    }    @Override    public void delete() {        System.out.println("delete删除用户");    }    @Override    public void find() {        System.out.println("find查询用户");    }}

MyAspect.java

package cn.itcast.aspectj.xml;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;/* * 切面类,在此编写通知 * 可以在XML配置文件中确定通知类型 */public class MyAspect {    //前置通知    public void myBefore(JoinPoint joinPoint){        System.out.println("前置通知,目标:");        System.out.println(joinPoint.getTarget()+",方法名称:");        System.out.println(joinPoint.getSignature().getName());    }    //后置通知    public void myAfterReturning(JoinPoint joinPoint){        System.out.println("后置通知,方法名称:"+joinPoint.getSignature().getName());    }    //环绕通知        //ProceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法        //1.必须返回Object类型值        //2.必须接收一个参数,类型为ProceedingJoinPoint        //3.必须throws Throwable    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{        //开始        System.out.println("环绕开始");        //执行当前目标方法        Object obj=proceedingJoinPoint.proceed();        //结束        System.out.println("环绕结束");        return obj;    }    //异常通知    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){        System.out.println("异常通知,出错了"+e.getMessage());    }    //最终通知    public void myAfter(){        System.out.println("最终通知");    }}

applicationContext.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:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans                            http://www.springframework.org/schema/beans/spring-beans.xsd                           http://www.springframework.org/schema/aop                            http://www.springframework.org/schema/aop/spring-aop.xsd">    <!-- 1.目标类 -->    <bean id="userDao" class="cn.itcast.dao.UserDaoImpl"></bean>    <!-- 2.切面 -->    <bean id="myAspect" class="cn.itcast.aspectj.xml.MyAspect"></bean>    <!-- 3.aop编程 -->    <aop:config>        <aop:aspect ref="myAspect">            <!-- 3.1 配置切入点,通知最后增强哪些方法 -->            <aop:pointcut expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/>            <!-- 3.2关联通知Advice和切入点pointCut -->            <!-- #1前置通知 -->            <aop:before method="myBefore" pointcut-ref="myPointCut"/>            <!-- #2后置通知,在方法返回之后执行,就可以获得返回值                returning属性:用于设置后置通知的第二个参数的名称,类型是Object              -->             <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/>             <!-- #3环绕通知 -->             <aop:around method="myAround" pointcut-ref="myPointCut"/>             <!-- #4抛出通知:用于处理程序发生异常,就可以接受当前方法产生的异常                注意:如果程序没有异常,将不会执行增强                throwing属性:用于设置通知第二个参数的名称,类型Throwable              -->              <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>              <!-- #5最终通知:无论程序发生任何事情,都将执行 -->              <aop:after method="myAfter" pointcut-ref="myPointCut"/>        </aop:aspect>    </aop:config></beans>

AspectJ表达式

execution() 用于描述方法

语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)

  • 修饰符:一般省略。
    • public:公共方法
    • *:任意
  • 返回值:不能省略
    • void:无返回值
    • String:返回值为字符串
    • *:任意(一般用这个)
  • 包:可以省略
    • com.eaglezsx.crm:固定包
    • com.eaglezsx.crm.*.service:crm包下面的任意子包,但要包含service。比如com.eaglezsx.crm.staff.service
    • com.eaglezsx.crm..:crm下面的任意子包(包括孙包)
    • com.eaglezsx.crm.*.service.. :crm包下面任意子包,固定目录service,service目录任意包
  • 类:可以省略
    • UserServiceImplement:指定类
    • *Impl:以Impl结尾的类
    • User*:以User开头的类
    • *:任意类
  • 方法名:不能省略
    • addUser:固定方法
    • add*:以add开头的方法
    • *Do:以Do结尾的方法
    • *:任意方法
  • (参数)
    • ():无参
    • (int):一个整型参数
    • (int,int):两个整型参数
    • (..):任意参数
  • throws:可省略,一般不写。
* cn.itcast.dao..*.*(..)意思是,返回值任意,cn.itcast.dao下的任意包,下的任意类,下的任意方法,方法的参数任意

TestXML.java

package cn.itcast.aspectj.xml;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.itcast.dao.UserDao;public class TestXML {    @Test    public void demo01(){        String xmlPath="applicationContext.xml";        ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);        //1.从Spring容器获得内容        UserDao userDao=(UserDao)applicationContext.getBean("userDao");        //2.执行方法        userDao.save();    }}

运行结果

前置通知,目标:cn.itcast.dao.UserDaoImpl@110fcd9,方法名称:save环绕开始save添加用户后置通知,方法名称:save环绕结束最终通知

基于Annotation的声明式AspectJ

  • @AspectJ:用于定义一个切面
  • @Before:用于定义前置通知,相当于BeforeAdvice
  • @AfterReturning:用于定义后置通知,相当于AfterReturningAdvice
  • @Around:用于定义环绕通知,相当于MethodInterceptor
  • @AfterThrowing:用于定义抛出通知,相当于ThrowAdvice
  • @After:用于定义最终final通知,不管是否异常,该通知都会执行
@Aspect@Componentpublic class MyAspect {    /*        用于取代<aop:pointcut expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/>        要求:方法必须是private没有值 名称自定义,没有参数    */    @Pointcut("execution(* cn.itcast.dao..*.*(..))")    private void myPointCut(){}    @Before("myPointCut()")    public void myBefore(JoinPoint joinPoint){        ...    }    @AfterReturning(value="myPointCut()")    public void myAfterReturning(JoinPoint joinPoint){        ...    }    @Around("myPointCut()")    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{        ...    }    @AfterThrowing(value="myPointCut()",throwing="e")    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){        ...    }    @After("myPointCut()")    public void myAfter(){        ...    }}
@Repository("userDao")public class UserDaoImpl implements UserDao{    ...}

applicationContext.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:aop="http://www.springframework.org/schema/aop"       xmlns:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans                            http://www.springframework.org/schema/beans/spring-beans.xsd                           http://www.springframework.org/schema/aop                            http://www.springframework.org/schema/aop/spring-aop.xsd                           http://www.springframework.org/schema/context                           http://www.springframework.org/schema/context/spring-context.xsd">     <!-- 扫描包:使注解生效 -->                         <context:component-scan base-package="cn.itcast"></context:component-scan>                         <!-- 使切面开启自动代理 -->                         <aop:aspectj-autoproxy></aop:aspectj-autoproxy>                       </beans>

TestAnnotation.java

public class TestAnnotation {    @Test    public void demo01(){        String xmlPath="applicationContext.xml";        ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);        //1.从Spring容器获得内容        UserDao userDao=(UserDao)applicationContext.getBean("userDao");        //2.执行方法        userDao.save();    }}