Spring之AOP

  什么是AOP?

  AOP即面向切面编程,比如某个项目有很多个模块,每个模块都为特定的业务领域提供服务,但是这些模块都需要一些辅助功能,例如安全和事务管理。

  和大多数技术一样,AOP有自己的术语。描述切面的常用术语有通知(advice)、切点(pointcut)、和连接点(join point)。

  通知(advice)定义了切面什么时候使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用于某个方法之前?之后?之前和之后?还是在方法发生异常时?

  Spring切面可以应用5种类型的通知:

    Before---在方法调用之前。

    After---在方法调用之后,无论是否发生异常。

    After-returning---在方法成功执行之后。

    After-throwing---在方法发生异常之后。

    Around---包裹被通知的方法,在方法调用之前和之后都执行。

  Spring提供了4种各具特色的AOP支持:

    基于代理的经典AOP

    @AspectJ注解驱动的切面

    纯POJO切面

    注入式AspectJ切面

  在Spring XML中声明切面

    <aop:advisor>    定义AOP通知器

    <aop:after>     定义AOP后置通知(不管方法是否执行成功)

    <aop:after-returning>定义AOP after-returning通知

    <aop:after-throwing>定义AOP after-throwing通知

    <aop:around>    定义AOP环绕通知

    <aop:aspect>    定义切面

    <aop:aspect-autoproxy>启动@Aspect注解驱动的切面

    <aop:before>    定义AOP前置通知

    <aop:config>    顶层的AOP配置元素,大多数的<aop:*>都需要包含在<aop:config>中

    <aop:declare-parents>为被通知的对象引入额外的接口,并透明的实现

    <aop:pointcut>   定义切点

定义一个主人类Person

复制代码
package com.lmy.spring;

import org.springframework.stereotype.Component;

@Component("xiaoming")
public class Person{
  //表演之前喂食
 public void beforePlay(){
    System.out.println("喂食");
 }
  //表演之后鼓掌
 public void afterPlay(){
  System.out.println("鼓掌");
 }  

 
}
复制代码

宠物狗类Dog

复制代码
package com.lmy.spring;

import org.springframework.stereotype.Component;

@Component("kala")
public class Dog{
 public void play(){
  System.out.println("做算术");
 }
}
复制代码

宠物在表演之前主人应该先喂食,通过注解@Component把两个类定义成Spring Bean。

然后在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-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
            
            >
            <context:component-scan base-package="com.lmy.spring"></context:component-scan>
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <aop:config>
     <aop:aspect ref="person">
         <aop:pointcut expression="execution(* com.lmy.spring.Dog.play(..))" id="dog" />
<aop:before method="beforePlay" pointcut-ref="dog" />
<aop:after method="afterPlay" pointcut-ref="dog" />

     </aop:aspect>
  </aop:config>
</beans>
复制代码

编写代码测试结果

    @Test
    public void test1(){
        ApplicationContext  ctx = new ClassPathXmlApplicationContext("spring.xml");
        Dog kala = (Dog)ctx.getBean("kala");
        kala.play();
    }

输出:

    喂食
    做算术
    鼓掌

两个类的代码从表面上没有任何关联的地方,但是通过AOP通知,在Dog类的Bean对象kala调用play方法时,会被Spring拦截,aop:before定义了匹配的切点方法之前执行的前置方法,aop:after定义了在切点匹配的方法结束之后执行的方法。

上面的例子虽然实现了表演前喂食和表演后鼓掌的功能,但是Person根本不知道是给谁喂食和鼓掌,所以代码还需要改进一下。

Dog在play的时候传一个名字进去

    public void play(String name) {
        System.out.println(name+"在做算术");
        
    }

修改Person类的两个方法,都加上参数String name

复制代码
    // 表演之前喂食
    public void beforePlay(String name) {
        System.out.println("给"+dog.getName()+"喂食");
        
    }

    // 表演之后鼓掌
    public void afterPlay(String name) {
        System.out.println("给"+dog.getName()+"鼓掌");
    }
复制代码

现在要做的就是在匹配的切点方法之前和之后执行的方法添加参数。

修改XML配置

复制代码
    <aop:config>
        <aop:aspect ref="xiaoming">
            <aop:pointcut expression="execution(* com.lmy.spring.Dog.play(com.lmy.spring.Dog)) and args(kala)"
                id="dog" />
            <aop:before method="beforePlay" pointcut-ref="dog" arg-names="kala" />
            <aop:after method="afterPlay" pointcut-ref="dog" arg-names="kala" />
        </aop:aspect>
    </aop:config>
复制代码

然后调用

    @Test
    public void test1(){
        ApplicationContext  ctx = new ClassPathXmlApplicationContext("spring.xml");
        Dog pf = (Dog)ctx.getBean("kala");
        pf.play("kala");
    }

这样就会输入带名字的信息了。

PS:为了简单,所以没有使用接口,严格来说应该有Animal接口,Dog实现该接口,切点的配置就应该配置成

<aop:pointcut expression="execution(* com.lmy.spring.Animal.play(String)) and args(kala)"
id="animal" />

所有实现了Animal接口的类当调用play方法时都会触发。

原文地址:https://www.cnblogs.com/bluedy1229/p/3604547.html