1、AOP可配置元素

  使用XML配置开发Spring AOP需要引入AOP命名空间,所以首先了解一下AOP可配置的元素,具体如下表所示:

AOP配置元素

用途

备注

aop:advisor

定义AOP的通知器

一种较老的方式,目前很少使用

aop:aspect

定义一个切面

——

aop:before

定义前置通知

——

aop:after

定义后置通知

——

aop:around

定义环绕通知

——

aop:after-returning

定义返回通知

——

aop:after-throwing

定义异常通知

——

aop:config

顶层的AOP配置元素

AOP的配置是以它为开始的

aop:declare-parents

给通知引入新的额外接口,增强功能

——

aop:pointcut

定义切点

——

2、开发环境搭建

  在Spring_Demo项目中创建名为“XMLForAOP”的模块module,并在lib文件夹中导入需要的jar包,并Add as Library,具体如下所示:

springboot xml方式aop的多种方法 spring xml配置aop_SSM框架


  在com.ccff.spring.xml.aop.pojo包下创建名为“Role”的角色类,具体代码如下所示:

package com.ccff.spring.xml.aop.pojo;

public class Role {
    private Long roleId;
    private String roleName;
    private String roleNote;

    public Role() {
    }

    public Role(Long roleId, String roleName, String roleNote) {
        this.roleId = roleId;
        this.roleName = roleName;
        this.roleNote = roleNote;
    }

    public Long getRoleId() {
        return roleId;
    }

    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleNote() {
        return roleNote;
    }

    public void setRoleNote(String roleNote) {
        this.roleNote = roleNote;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleNote='" + roleNote + '\'' +
                '}';
    }
}

  然后在com.ccff.spring.xml.aop.service包下创建名为“RoleService”的接口,具体代码如下所示:

package com.ccff.spring.xml.aop.service;


import com.ccff.spring.xml.aop.pojo.Role;

public interface RoleService {
    void printRole(Role role);
}

  并在该包下创建名为“RoleServiceImpl”的实现类,实现RoleService接口,具体代码如下:

package com.ccff.spring.xml.aop.service;


import com.ccff.spring.xml.aop.pojo.Role;

public class RoleServiceImpl implements RoleService {
    @Override
    public void printRole(Role role) {
        System.out.println("【打印角色信息】"+role.toString());
    }
}

  最后,在com.ccff.spring.xml.aop.aspect包下创建名为“XMLRoleAspect”的切面类,具体代码如下所示:

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



public class XMLRoleAspect {
    public void before(){
        System.out.println("Before方法执行......");
    }

    public void after(){
        System.out.println("After方法执行......");
    }

    public void afterReturning(){
        System.out.println("AfterReturning方法执行......");
    }

    public void afterThrowing(){
        System.out.println("AfterThrowing方法执行......");
    }
}

3、前置通知、后置通知、返回通知和异常通知

  首先通过引入的XML定义AOP的命名空间,然后定义了一个RoleService类和切面XMLRoleAspect类,最后通过aop:config去定义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-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="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
    <bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />

    <aop:config>
        <!--引用XMLRoleAspect作为切面类-->
        <aop:aspect ref="xmlRoleAspect">
            <!--定义切点-->
            <aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
            <!--定义通知-->
            <aop:before method="before" pointcut-ref="printRole" />
            <aop:after method="after" pointcut-ref="printRole" />
            <aop:after-returning method="afterReturning" pointcut-ref="printRole" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
        </aop:aspect>
    </aop:config>

</beans>

  通过上面的配置就可以定义切点并进行引入,同时避免了多次书写同一正则表达式的麻烦,至此我们通过XML的配置成功定义了前置通知、后置通知、返回通知和异常通知。

4、环绕通知

  和其他通知一样,环绕通知也可以织入到约定的流程当中,修改com.ccff.spring.xml.aop.aspect包中的XMLRoleAspect切面类,为其增加环绕通知,具体修改代码如下所示:

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

import org.aspectj.lang.ProceedingJoinPoint;

public class XMLRoleAspect {
    public void before(){
        System.out.println("Before方法执行......");
    }

    public void after(){
        System.out.println("After方法执行......");
    }

    public void afterReturning(){
        System.out.println("AfterReturning方法执行......");
    }

    public void afterThrowing(){
        System.out.println("AfterThrowing方法执行......");
    }

    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("Around方法执行前......");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            new RuntimeException("回调原有流程,产生异常");
        }
        System.out.println("Around方法执行后......");
    }

}

  然后修改Spring的配置文件,为其增加环绕通知,具体修改的配置如下所示:

<?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-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="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
    <bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />

    <aop:config>
        <!--引用XMLRoleAspect作为切面类-->
        <aop:aspect ref="xmlRoleAspect">
            <!--定义切点-->
            <aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
            <!--定义通知-->
            <aop:before method="before" pointcut-ref="printRole" />
            <aop:after method="after" pointcut-ref="printRole" />
            <aop:around method="around" pointcut-ref="printRole" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
            <aop:after-returning method="afterReturning" pointcut-ref="printRole" />
        </aop:aspect>
    </aop:config>

</beans>

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

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

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

public class XMLAOPTest {
    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);
    }
}

  运行该测试类,查看输出到控制台上的日志信息如下:

springboot xml方式aop的多种方法 spring xml配置aop_AOP_02

5、给通知传递参数

  通过XML的配置,也可以引入参数到通知中,以前置通知为例,首先修改com.ccff.spring.xml.aop.aspect包下的切面类XMLRoleAspect中的before方法,具体修改如下所示:

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

import com.ccff.spring.xml.aop.pojo.Role;
import org.aspectj.lang.ProceedingJoinPoint;

public class XMLRoleAspect {
    public void before(Role role){
        System.out.println("Before方法执行,打印Role对象:"+role);
    }

    public void after(){
        System.out.println("After方法执行......");
    }

    public void afterReturning(){
        System.out.println("AfterReturning方法执行......");
    }

    public void afterThrowing(){
        System.out.println("AfterThrowing方法执行......");
    }

    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("Around方法执行前......");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            new RuntimeException("回调原有流程,产生异常");
            //throwable.printStackTrace();
        }
        System.out.println("Around方法执行后......");
    }

}

  然后再修改配置文件,具体配置修改如下:

<?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-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="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
    <bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />

    <aop:config>
        <!--引用XMLRoleAspect作为切面类-->
        <aop:aspect ref="xmlRoleAspect">
            <!--定义切点-->
            <aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
            <!--定义通知-->
            <aop:before method="before" pointcut="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..)) and args(role)" />
            <aop:after method="after" pointcut-ref="printRole" />
            <aop:around method="around" pointcut-ref="printRole" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
            <aop:after-returning method="afterReturning" pointcut-ref="printRole" />
        </aop:aspect>
    </aop:config>

</beans>

  这里需要注意的是,在通过注解的方式为通知传递参数时使用的是&,而通过XML配置为通知传递参数时,由于在XML中&符号具有特殊含义,因此采用and代替。

  最后再次运行测试类XMLAOPTest,查看输出到控制台上的日志信息如下:

springboot xml方式aop的多种方法 spring xml配置aop_SSM框架_03

6、引入

  在注解当中,无论是使用JDK动态代理,还是CGLIB动态代理都可以将代理对象下挂到多个接口之下,这样就能够引入新的方法了,注解能做到的事情通过XML也可以实现。

  仍然以在注解中使用的案例功能讲解,即对于printRole方法而言,如果角色为空时不再打印,那么要引入一个新的检测器对其进行检测。

  首先在com.ccff.spring.xml.aop.verifier包下创建名为“RoleVerifier”的验证器接口,具体代码如下所示:

package com.ccff.spring.xml.aop.verifier;


import com.ccff.spring.xml.aop.pojo.Role;

public interface RoleVerifier {
    boolean verify(Role role);
}

  然后在该包中创建名为“RoleVerifierImpl”的类实现RoleVerifier接口,具体代码如下所示:

package com.ccff.spring.xml.aop.verifier;


import com.ccff.spring.xml.aop.pojo.Role;

public class RoleVerifierImpl implements RoleVerifier {
    @Override
    public boolean verify(Role role) {
        return role != null;
    }
}

  接着修改com.ccff.spring.xml.aop.aspect包下的切面类XMLRoleAspect,为其添加一个新的属性RoleVerifier类的对象,具体代码如下所示:

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

import com.ccff.spring.xml.aop.pojo.Role;
import com.ccff.spring.xml.aop.verifier.RoleVerifier;
import org.aspectj.lang.ProceedingJoinPoint;

public class XMLRoleAspect {
    public RoleVerifier roleVerifier = null;

    public void before(Role role){
        System.out.println("Before方法执行,打印Role对象:"+role);
    }

    public void after(){
        System.out.println("After方法执行......");
    }

    public void afterReturning(){
        System.out.println("AfterReturning方法执行......");
    }

    public void afterThrowing(){
        System.out.println("AfterThrowing方法执行......");
    }

    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("Around方法执行前......");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            new RuntimeException("回调原有流程,产生异常");
            //throwable.printStackTrace();
        }
        System.out.println("Around方法执行后......");
    }

}

  最后,使用配置文件进行配置,配置的内容和注解引入的方法相当,它是使用aop:declare-parents去引入的,具体配置如下所示:

<?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-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="xmlRoleAspect" class="com.ccff.spring.xml.aop.aspect.XMLRoleAspect" />
    <bean id="roleService" class="com.ccff.spring.xml.aop.service.RoleServiceImpl" />

    <aop:config>
        <!--引用XMLRoleAspect作为切面类-->
        <aop:aspect ref="xmlRoleAspect">
            <!--定义切点-->
            <aop:pointcut id="printRole" expression="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..))" />
            <!--定义通知-->
            <aop:before method="before" pointcut="execution(* com.ccff.spring.xml.aop.service.RoleServiceImpl.printRole(..)) and args(role)" />
            <aop:after method="after" pointcut-ref="printRole" />
            <aop:around method="around" pointcut-ref="printRole" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="printRole" />
            <aop:after-returning method="afterReturning" pointcut-ref="printRole" />
            <!--通过XML引入新功能,对目标类进行加强-->
            <aop:declare-parents types-matching="com.ccff.spring.xml.aop.service.RoleServiceImpl+" implement-interface="com.ccff.spring.xml.aop.verifier.RoleVerifier" default-impl="com.ccff.spring.xml.aop.verifier.RoleVerifierImpl" />
        </aop:aspect>
    </aop:config>

</beans>

  修改com.ccff.spring.xml.aop.test包下的测试类XMLAOPTest,具体代码如下所示:

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

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

public class XMLAOPTest {
    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");
        RoleVerifier roleVerifier = (RoleVerifier) roleService;
        if (roleVerifier.verify(role))
            roleService.printRole(role);
        else
            System.out.println("Role对象为空");
        System.out.println("===============测试role为空========================");
        role = null;
        if (roleVerifier.verify(role))
            roleService.printRole(role);
        else
            System.out.println("Role对象为空");
    }
}

  运行该测试类后,查看输出到控制台的日志信息如下:

springboot xml方式aop的多种方法 spring xml配置aop_后台_04