Spring使用篇(九)——经典Spring AOP与多切面开发

文章目录

  • 1、开发环境搭建
  • 2、经典Spring AOP应用程序
  • 3、多切面开发
    • 3.1 创建接口
    • 3.2 创建切面
    • 3.3 创建Bean管理
    • 3.4 创建测试类
    • 3.5 保证多切面的执行顺序
    • 3.6 进阶讨论

1、开发环境搭建

  在Spring_Demo项目中创建名为“ClassifyAOP”的模块module,并将如下jar包加入到lib文件夹中,并全部将其Add as Library。具体如下所示:
Spring使用篇(九)——经典Spring AOP与多切面开发_第1张图片

2、经典Spring AOP应用程序

  Sprig早期提供的AOP实现,在现在几乎已经被废弃了,不过具有一定的讨论价值,它需要通过XML的方式去配置。例如要完成printRole方法的切面前置通知的功能,这时可以把printRole称为AOP的连接点。首先在com.ccff.spring.classify.aop.aspect包下创建名为“ProxyFactoryBeanAspect”的前置通知类,并实现MethodBeforeAdvice接口的before方法,具体代码如下所示:

package com.ccff.spring.classify.aop.aspect;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class ProxyFactoryBeanAspect implements MethodBeforeAdvice {
    /**
     * 前置通知
     * @param method:被拦截的方法
     * @param objects:参数 数组[role]
     * @param o:被拦截的对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知!!!");
    }
}

  有了它还需要对Spring IoC容器描述对应的信息,这个时候需要一个XML文件去描述它,具体配置如下:


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

    <bean id="proxyFactoryBeanAspect" class="com.ccff.spring.classify.aop.aspect.ProxyFactoryBeanAspect" />

    
    <bean id="roleService" class="org.springframework.aop.framework.ProxyFactoryBean">
        
        <property name="proxyInterfaces">
            <value>com.ccff.spring.classify.aop.service.RoleServicevalue>
        property>

        
        <property name="target">
            <bean class="com.ccff.spring.classify.aop.service.RoleServiceImpl" />
        property>

        
        <property name="interceptorNames">
            <list>
                
                <value>proxyFactoryBeanAspectvalue>
            list>
        property>
    bean>

beans>

  然后在com.ccff.spring.classify.aop.test包下创建名为“Test”的测试方法,具体代码如下所示:

package com.ccff.spring.classify.aop.test;

import com.ccff.spring.classify.aop.pojo.Role;
import com.ccff.spring.classify.aop.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        RoleService roleService = (RoleService) context.getBean("roleService");
        Role role = new Role();
        role.setRoleId(1L);
        role.setRoleName("role-name-1");
        role.setRoleNote("role-note-1");
        roleService.printRole(role);
    }
}

  最后,运行上述测试方法,查看输出到控制台的日志信息如下:
Spring使用篇(九)——经典Spring AOP与多切面开发_第2张图片
  这样Spring AOP就被用起来了,它虽然很经典,但是已经不是主流方式了,所以至此不再赘述。

3、多切面开发

  在之前我们的讨论中,一个方法只有一个切面,而事实是Spring也能支持多个切面,当有多个切面时,在测试过程中发现它不会存在任何顺序,这些顺序代码会随机生成,但是有时候我们希望它按照指定的顺序运行。

3.1 创建接口

  第一步,在com.ccff.spring.classify.aop.multi包下创建名为“MultiBean”的接口,并提供接口方法,具体代码如下所示:

package com.ccff.spring.classify.aop.multi;

public interface MultiBean {
    void testMulti();
}

  然后在该包下继续创建名为“MultiBeanImpl”的实现类,实现MultiBean接口,具体代码如下所示:

package com.ccff.spring.classify.aop.multi;

public class MultiBeanImpl implements MultiBean {
    @Override
    public void testMulti() {
        System.out.println("test multi aspects!!!");
    }
}

3.2 创建切面

  至此,已经定义好了连接点,那么现在需要切面,因此在com.ccff.spring.classify.aop.aspect包下分别创建名为“Aspect1”、“Aspect2”和“Aspect3”的三个切面,具体代码如下:

package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class Aspect1 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 1 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 1 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 1 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 1 ......");
    }
}
package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class Aspect2 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 2 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 2 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 2 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 2 ......");
    }
}
package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;

@Aspect
public class Aspect3 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 3 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 3 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 3 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 3 ......");
    }
}

  至此,以上3个切面就拦截了连接点。

3.3 创建Bean管理

  在com.ccff.spring.classify.aop.config包下创建名为“MultiConfig”的Java配置类用于管理Bean,具体代码如下所示:

package com.ccff.spring.classify.aop.config;

import com.ccff.spring.classify.aop.aspect.Aspect1;
import com.ccff.spring.classify.aop.aspect.Aspect2;
import com.ccff.spring.classify.aop.aspect.Aspect3;
import com.ccff.spring.classify.aop.multi.MultiBean;
import com.ccff.spring.classify.aop.multi.MultiBeanImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.ccff.spring.classify.aop")
public class MultiConfig {

    @Bean
    public MultiBean multiBean(){
        return new MultiBeanImpl();
    }

    @Bean
    public Aspect1 aspect1(){
        return new Aspect1();
    }

    @Bean
    public Aspect2 aspect2(){
        return new Aspect2();
    }

    @Bean
    public Aspect3 aspect3(){
        return new Aspect3();
    }

}

3.4 创建测试类

  在com.ccff.spring.classify.aop.test包下创建名为“TestMultiBean”的测试类,具体代码如下所示:

package com.ccff.spring.classify.aop.test;

import com.ccff.spring.classify.aop.config.MultiConfig;
import com.ccff.spring.classify.aop.multi.MultiBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMultiBean {
    @Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MultiConfig.class);
        MultiBean multiBean = (MultiBean) context.getBean("multiBean");
        multiBean.testMulti();
    }
}

  至此,定义的连接点和3个切面已经可以执行了,但是此时的3个切面的执行顺序是无序的。

3.5 保证多切面的执行顺序

  如果使用注解的切面,为了保证多个切面之前执行是有序的,可以给切面加入注解@Order。例如修改上面定义的三个切面如下:

package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

@Aspect
@Order(1)
public class Aspect1 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 1 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 1 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 1 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 1 ......");
    }
}
package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

@Aspect
@Order(3)
public class Aspect2 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 2 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 2 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 2 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 2 ......");
    }
}
package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

@Aspect
@Order(2)
public class Aspect3 {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 3 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 3 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 3 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 3 ......");
    }
}

  此时,运行上面的测试方法后,查看输出在控制台上的日志信息如下:
Spring使用篇(九)——经典Spring AOP与多切面开发_第3张图片
  除了使用注解@Order外,还可以让切面实现Ordered(org.springframework.core.Ordered)接口,它定义了一个getOrder方法。例如修改切面Aspect2如下:

package com.ccff.spring.classify.aop.aspect;

import org.aspectj.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Aspect
//@Order(3)
public class Aspect2 implements Ordered {
    @Pointcut("execution(* com.ccff.spring.classify.aop.multi.MultiBeanImpl.testMulti(..))")
    public void print(){}

    @Before("print()")
    public void before(){
        System.out.println("before 2 ......");
    }

    @After("print()")
    public void after(){
        System.out.println("after 2 ......");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing 2 ......");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning 2 ......");
    }

    @Override
    public int getOrder() {
        return 3;
    }
}

  再次运行上面的测试方法后,查看输出在控制台上的日志信息如下:
Spring使用篇(九)——经典Spring AOP与多切面开发_第4张图片

3.6 进阶讨论

  众所周知,Spring AOP的实现方法是动态代理,在多个代理的情况下,涉及到了责任链模式,为了更好的理解,给出多个切面的执行顺序如下:
Spring使用篇(九)——经典Spring AOP与多切面开发_第5张图片
  上图展示了一条责任链,换句话说,Spring底层也是通过责任链模式来处理多个切面的。

你可能感兴趣的:(Java)