Spring AOP 之AspectJ注解和XML配置两种实现(Maven构建)

xml配置

1.接口和实现类

public interface UserManager {
    public String findUserById(int userId);
}
@Service
public class UserManagerImpl implements UserManager {
    @Override
    public String findUserById(int userId) {
        System.out.println("---------UserManagerImpl.findUserById()--------");
        if (userId <= 0) {
            throw new IllegalArgumentException("该用户不存在!");
        }
        return "张三";
    }
}

2.单独写一个Advice通知类进行测试

public class XMLAdvice {

    /**
     * 在核心业务执行前执行 不能阻止核心业务的调用
     */
    private void doBefore(JoinPoint joinPoint) {
        System.out.println("---doBefore().invoke--");
        System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doBefore()------");
    }

    /**
     * 手动控制调用核心业务逻辑,以及调用前和调用后的处理
     * <p>
     * 注意:当核心业务抛异常后 立即退出 转向AfterAdvice
     * 执行完AfterAdvice 再转向Throwing Advice
     */
    private Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("-----doAround().invoke-----");
        System.out.println(" 此处可以做类似于Before Advice的事情");

        //调用核心逻辑
        Object retVal = pjp.proceed();

        System.out.println(" 此处可以做类似于After Advice的事情");
        System.out.println("-----End of doAround()------");
        return retVal;
    }

    /**
     * 核心业务逻辑退出后(包括正常执行结束和异常退出),执行此Advice
     */
    private void doAfter(JoinPoint joinPoint) {
        System.out.println("-----doAfter().invoke-----");
        System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doAfter()------");
    }

    /**
     * 核心业务逻辑调用正常退出后,不管是否有返回值,正常退出后,均执行此Advice
     */
    private void doReturn(JoinPoint joinPoint) {
        System.out.println("-----doReturn().invoke-----");
        System.out.println(" 此处可以对返回值做进一步处理");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doReturn()------");
    }

    /**
     * 核心业务逻辑调用异常退出后,执行此Advice,处理错误信息
     */
    private void doThrowing(JoinPoint joinPoint, Throwable ex) {
        System.out.println("-----doThrowing().invoke-----");
        System.out.println(" 错误信息:" + ex.getMessage());
        System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of doThrowing()------");
    }
}

3.在Spring配置文件中配置

<bean id="xmlHandler" class="aop.XMLAdvice"/>
<aop:config>
        <aop:aspect id="aspect" ref="xmlHandler">
            <aop:pointcut id="pointUserMgr" expression="execution(* intergrate.service.*.find*(..))"/>

            <aop:before method="doBefore" pointcut-ref="pointUserMgr"/>
            <aop:around method="doAround" pointcut-ref="pointUserMgr"/>
            <aop:after method="doAfter" pointcut-ref="pointUserMgr"/>
            <aop:after-returning method="doReturn" pointcut-ref="pointUserMgr"/>
            <aop:after-throwing method="doThrowing" throwing="ex" pointcut-ref="pointUserMgr"/>
        </aop:aspect>
    </aop:config>

4.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-service.xml")
public class UserManagerImplTest {

    @Resource
    private UserManager userManager;

    @Test
    public void test() throws Exception {
        //正常运行
        String user = userManager.findUserById(1);
        System.out.println("user:" + user);

        System.out.println("=====华丽丽的分割线=======");

        //抛异常
        try {
            userManager.findUserById(0);
        } catch (Exception e) {
        }
    }
}

输出结果

---doBefore().invoke--
 此处意在执行核心业务逻辑前,做一些安全性的判断等等
 可通过joinPoint来获取所需要的内容
-----End of doBefore()------
-----doAround().invoke-----
 此处可以做类似于Before Advice的事情
---------UserManagerImpl.findUserById()--------
 此处可以做类似于After Advice的事情
-----End of doAround()------
-----doAfter().invoke-----
 此处意在执行核心业务逻辑之后,做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of doAfter()------
-----doReturn().invoke-----
 此处可以对返回值做进一步处理
 可通过joinPoint来获取所需要的内容
-----End of doReturn()------
user:张三
=====华丽丽的分割线=======
---doBefore().invoke--
 此处意在执行核心业务逻辑前,做一些安全性的判断等等
 可通过joinPoint来获取所需要的内容
-----End of doBefore()------
-----doAround().invoke-----
 此处可以做类似于Before Advice的事情
---------UserManagerImpl.findUserById()--------
-----doAfter().invoke-----
 此处意在执行核心业务逻辑之后,做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of doAfter()------
-----doThrowing().invoke-----
 错误信息:该用户不存在!
 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of doThrowing()------

值得注意的是Around与Before和After的执行顺序。3者的执行顺序取决于在xml中的配置顺序

如果配置顺序是aop:after -> aop:around ->aop:before,那么before和after都会包含在around中。这种情况的产生是由于Around的特殊性,它可以做类似于Before和After的操作。当安全性的判断不通过时,可以阻止核心业务逻辑的调用,这是Before做不到的。

AspectJ注解

接口和实现类

与上面相同

自定义AspceJAdvice

@Aspect
public class AspceJAdvice {

    /**
     * Pointcut
     * 定义Pointcut,Pointcut的名称是aspectjMethod(),此方法没有返回值和参数
     * 该方法就是一个标识 不进行调用
     */
    @Pointcut("execution(* find*(..))")
    private void aspectjMethod() {
    }

    /**
     * Before
     * 在核心业务执行前执行,不能阻止核心业务的调用
     */
    @Before("aspectjMethod()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("-----beforeAdvice().invoke-----");
        System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of beforeAdvice()------");
    }

    /**
     * After
     * 核心业务逻辑退出后(包括正常执行结束和异常退出),执行此Advice
     */
    @After("aspectjMethod()")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("-----afterAdvice().invoke-----");
        System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of afterAdvice()------");
    }

    /**
     * Around
     * 手动控制调用核心业务逻辑,以及调用前和调用后的处理,
     * <p>
     * 注意:当核心业务抛异常后,立即退出,转向AfterAdvice
     * 执行完AfterAdvice,再转到ThrowingAdvice
     */
    @Around(value = "aspectjMethod()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("-----aroundAdvice().invoke-----");
        System.out.println(" 此处可以做类似于Before Advice的事情");
        //调用核心逻辑
        Object retVal = pjp.proceed();
        System.out.println(" 此处可以做类似于After Advice的事情");
        System.out.println("-----End of aroundAdvice()------");
        return retVal;
    }

    /**
     * AfterReturning
     * 核心业务逻辑调用正常退出后,不管是否有返回值,正常退出后,均执行此Advice
     */
    @AfterReturning(value = "aspectjMethod()", returning = "retVal")
    public void afterReturningAdvice(JoinPoint joinPoint, String retVal) {
        System.out.println("-----afterReturningAdvice().invoke-----");
        System.out.println("Return Value: " + retVal);
        System.out.println(" 此处可以对返回值做进一步处理");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of afterReturningAdvice()------");
    }

    /**
     * 核心业务逻辑调用异常退出后,执行此Advice,处理错误信息
     * <p>
     * 注意:执行顺序在Around Advice之后
     */
    @AfterThrowing(value = "aspectjMethod()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        System.out.println("-----afterThrowingAdvice().invoke-----");
        System.out.println(" 错误信息:" + ex.getMessage());
        System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");
        System.out.println(" 可通过joinPoint来获取所需要的内容");
        System.out.println("-----End of afterThrowingAdvice()------");
    }
}

Spring配置文件中配置

<bean id="aspcejHandler" class="aop.AspceJAdvice"/>

<aop:aspectj-autoproxy/>

测试

同上

输出结果

-----aroundAdvice().invoke-----
 此处可以做类似于Before Advice的事情
-----beforeAdvice().invoke-----
 此处意在执行核心业务逻辑前,做一些安全性的判断等等
 可通过joinPoint来获取所需要的内容
-----End of beforeAdvice()------
---------UserManagerImpl.findUserById()--------
 此处可以做类似于After Advice的事情
-----End of aroundAdvice()------
-----afterAdvice().invoke-----
 此处意在执行核心业务逻辑之后,做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of afterAdvice()------
-----afterReturningAdvice().invoke-----
Return Value: 张三
 此处可以对返回值做进一步处理
 可通过joinPoint来获取所需要的内容
-----End of afterReturningAdvice()------
user:张三
=====华丽丽的分割线=======
-----aroundAdvice().invoke-----
 此处可以做类似于Before Advice的事情
-----beforeAdvice().invoke-----
 此处意在执行核心业务逻辑前,做一些安全性的判断等等
 可通过joinPoint来获取所需要的内容
-----End of beforeAdvice()------
---------UserManagerImpl.findUserById()--------
-----afterAdvice().invoke-----
 此处意在执行核心业务逻辑之后,做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of afterAdvice()------
-----afterThrowingAdvice().invoke-----
 错误信息:该用户不存在!
 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等
 可通过joinPoint来获取所需要的内容
-----End of afterThrowingAdvice()------

通过测试的发现AroundAdvice、BeforeAdvice、AfterAdvice、ReturningAdvice的执行顺序是根据注解的顺序而定的

XML配置和注解配置优缺点

XML配置优点

  • xml 作为可扩展标记语言最大的优势在于开发者能够为软件量身定制适用的标记,使代码更加通俗易懂。
  • 利用 xml 配置能使软件更具扩展性。例如 Spring 将 class 间的依赖配置在 xml 中,最大限度地提升应用的可扩展性。
  • 具有成熟的验证机制确保程序正确性。利用 Schema 或 DTD 可以对 xml 的正确性进行验证,避免了非法的配置导致应用程序出错。
    修改配置而无需变动现有程序。

XML配置缺点

  • 需要解析工具或类库的支持。
  • 解析 xml 势必会影响应用程序性能,占用系统资源。
  • 配置文件过多导致管理变得困难。
  • 编译期无法对其配置项的正确性进行验证,或要查错只能在运行期。
  • IDE 无法验证配置项的正确性无能为力。
  • 查错变得困难。往往配置的一个手误导致莫名其妙的错误。
  • 开发人员不得不同时维护代码和配置文件,开发效率变得低下。
  • 配置项与代码间存在潜规则。改变了任何一方都有可能影响另外一方。

注解配置优点

  • 保存在 class 文件中,降低维护成本。
  • 无需工具支持,无需解析。
  • 编译期即可验证正确性,查错变得容易。
  • 提升开发效率。

注解配置缺点

  • 若要对配置项进行修改,不得不修改 Java 文件,重新编译打包应用。
  • 配置项编码在 Java 文件中,可扩展性差。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值