第八讲 面向切面编程(AOP)

本文深入解析Spring AOP的概念、术语及其在Spring框架中的作用。详细介绍了AOP在Spring中的三种实现方式:通过Spring API、自定义类以及注解实现。同时,文章提供了具体的代码示例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。

二、AOP在Spring中的作用

     提供声明式服务(最重要的服务是声明式事务管理)

     允许用户实现自定义切面

三、AOP:在不改变原有代码的情况下,增加新的功能。

     传统的编程模式:


     AOP的编程模式:横向的编程


   AOP就是通过代理(Proxy)的方式来实现的。

四、AOP的好处(就是使用代理的好处)

   使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。
   公共的业务由代理来完成 --- 实现了业务的分工。
   公共业务发生扩展时变得更加集中和方便。

五、名词解释

     关注点:增加的某个业务。如:日志、安全、缓存、事务、异常处理等。
     
- 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于@Aspect注解的方式来实现。

- 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。

- 通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

- 切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

- 引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。

- 目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。

- AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

- 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知类型:

- 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。

- 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

- 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。

- 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

- 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

五、使用Spring实现AOP

     第一种实现方式:通过Spring API来实现

     导包:

           <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.2.1.RELEASE</version>
           </dependency>
           <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.10</version>
           </dependency>

     目标类:

publicclass UserServiceImpl implementsUserService {
     publicvoid add() {
           System.out.println("增加用户");
     }
     publicvoid update() {
           System.out.println("修改用户");
     }
     publicvoid delete() {
           System.out.println("删除用户");
     }
     publicvoid search() {
           System.out.println("查询用户");
     }
}

     通知:

          前置通知:

publicclass Log implementsMethodBeforeAdvice {
     
     /**
      *@param method 被调用的方法对象
      *@param args 被调用的方法的参数
      *@param target 被调用的方法的目标对象
      */
     publicvoid before(Method method, Object[]args, Objecttarget)
                throwsThrowable {
           System.out.println(target.getClass().getName() + "的" + method.getName() +"方法被执行");
     }
     
}

          后置通知:

publicclass AfterLog implementsAfterReturningAdvice {
     
     /**
      * 目标方法执行后执行的通知
      *@param returnValue 被调用的方法对象的返回值
      *@param method 被调用的方法对象
      *@param args 被调用的方法对象的参数
      *@param target 被调用的方法对象的目标对象
      */
     publicvoid afterReturning(ObjectreturnValue, Methodmethod, Object[]args, Objecttarget)
                throwsThrowable {
           System.out.println(target.getClass().getName() + "的" + method.getName()
           +"被成功执行,返回值是:" + returnValue);
     }
}

     Spring配置:

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <beanid="afterLog"class="com.liujie.log.AfterLog"></bean>
     
     <aop:config>
           <aop:pointcutexpression="execution(* com.liujie.service.impl.*.*(..))"
           id="pointcut"/>
           <aop:advisoradvice-ref="log"pointcut-ref="pointcut"/>
           <aop:advisoradvice-ref="afterLog"pointcut-ref="pointcut"/>
     </aop:config>
     
</beans>

     测试:

publicclass Test {
     publicstatic void main(String[]args) {
           ApplicationContextcontext = newClassPathXmlApplicationContext("beans.xml");
           
           UserServiceuserService = (UserService)context.getBean("userService");
           userService.add();
     }
}

   AOP的重要性:很重要。

   Spring AOP就是将公共的业务(如:日志、安全等)和领域业务结合。当执行领域业务时,将会把公共业务加进来。实现公共业务的重复
   利用。领域业务更纯粹。程序员专注于领域业务。其本质还是动态代理

     第二种实现方式:自定义类来实现

publicclass UserServiceImpl implementsUserService {
     publicvoid add() {
           System.out.println("增加用户");
     }
     publicvoid update() {
           System.out.println("修改用户");
     }
     publicvoid delete() {
           System.out.println("删除用户");
     }
     publicvoid search() {
           System.out.println("查询用户");
     }
}

publicclass Log {
     
     publicvoid before() {
           System.out.println("---------方法执行前--------");
     }
     
     publicvoid after() {
           System.out.println("---------方法执行后--------");
     }
     
}

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <aop:config>
           <aop:aspectref="log">
                <aop:pointcutexpression="execution(* com.liujie.service.impl.*.*(..))"
                id="pointcut"/>
                <aop:beforemethod="before"pointcut-ref="pointcut"/>
                <aop:aftermethod="after"pointcut-ref="pointcut"/>  
           </aop:aspect>
     </aop:config>
     
</beans>

publicclass Test {
     publicstatic void main(String[]args) {
           ApplicationContextcontext = newClassPathXmlApplicationContext("beans.xml");
           
           UserServiceuserService = (UserService)context.getBean("userService");
           userService.add();
     }
}

     第三种实现方式:通过注解来实现

@Aspect
publicclass Log {
     
     @Before("execution(* com.liujie.service.impl.*.*(..))")
     publicvoid before() {
           System.out.println("---------方法执行前--------");
     }
     
     @After("execution(* com.liujie.service.impl.*.*(..))")
     publicvoid after() {
           System.out.println("---------方法执行后--------");
     }
     
     @Around("execution(* com.liujie.service.impl.*.*(..))")
     publicObject around(ProceedingJoinPoint joinPoint) throws Throwable {
           System.out.println("还绕前");
           System.out.println("签名:"+ joinPoint.getSignature());
           //执行目标方法
           Objectresult = joinPoint.proceed();
           System.out.println("还绕后");
           returnresult;
     }
     
}

<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="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">
     
     <beanid="userService"class="com.liujie.service.impl.UserServiceImpl"></bean>
     
     <beanid="log"class="com.liujie.log.Log"></bean>
     
     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
     
</beans>


     使用第一种方式实现异常通知的AOP:

Spring提供类型检查的异常通知(typed throws advice),这意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法:它只是一个标记接口用来标识所给对象实现了一个或者多个针对特定类型的异常通知方法。这些方法应当满足下面的格式:

afterThrowing([Method, args, target],subclassOfThrowable)

只有最后一个参数是必须的。根据异常通知方法对方法及参数的需求,方法的签名可以有一个或者四个参数。

publicclass ExceptionLog implementsThrowsAdvice {
     
     publicvoid afterThrowing(Exception ex) throws Throwable {
           
     }
     
}

下面是一些异常通知的例子。

当一个RemoteException(包括它的子类)被抛出时,下面的通知会被调用:

publicclass RemoteThrowsAdvice implementsThrowsAdvice {
     
   public void afterThrowing(RemoteExceptionex)throws Throwable {
       // Do something with remote exception
    }
     
}

当一个ServletException被抛出,下面的通知将被调用。 和上面的通知不同,它声明了4个参数,因此它可以访问被调用的方法,方法的参数以及目标对象:

publicclass ServletThrowsAdviceWithArguments implements ThrowsAdvice {
     publicvoid afterThrowing(Method m, Object[]args, Objecttarget, ServletExceptionex) {
           // Do something with all arguments
     }
}

最后一个例子说明怎样在同一个类里使用两个方法来处理 RemoteException和ServletException。可以在一个类里组合任意数量的异常通知方法。

publicstatic class CombinedThrowsAdviceimplementsThrowsAdvice {
     publicvoid afterThrowing(RemoteException ex) throws Throwable {
           // Do something with remote exception
     }
     publicvoid afterThrowing(Method m, Object[]args, Objecttarget, ServletExceptionex) {
           // Do something with all arguments
     }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值