SpringBoot系列之AOP实现的两种方式

AOP经常使用的实现方式有两种,一种是采用声明的方式来实现(基于XML),一种是采用注解的方式来实现(基于AspectJ)java

首先复习下AOP中一些比较重要的概念:正则表达式

Joinpoint(链接点):程序执行时的某个特定的点,在Spring中就是某一个方法的执行 。
Pointcut(切点):说的通俗点,spring中AOP的切点就是指一些方法的集合,而这些方法是须要被加强、被代理的。通常都是按照必定的约定规则来表示的,如正则表达式等。切点是由一类链接点组成。 
Advice(通知):仍是说的通俗点,就是在指定切点上要干些什么。 
Advisor(通知器):其实就是切点和通知的结合 。spring

1、基于XML配置的Spring AOPexpress

采用声明的方式实现(在XML文件中配置),大体步骤为:配置文件中配置pointcut, 在java中用编写实际的aspect 类, 针对对切入点进行相关的业务处理。springboot

业务接口:ide

package com.springboottime.time.service;

public interface AdviceService {
    /*查找用户*/
    public String findUser();

    /*添加用户*/
    public void addUser();
}

业务实现:测试

package com.springboottime.time.service.serviceImpl;

import com.springboottime.time.service.AdviceService;
import lombok.Data;

@Data
public class AdviceServiceImpl implements AdviceService {

    private String name;

    @Override
    public String findUser() {
        System.out.println("***************执行业务方法findUser,查找的用户名字为:"+name+"****************");
        return name;
    }

    @Override
    public void addUser() {
        System.out.println("***************执行业务方法addUser****************");
        throw new RuntimeException();
    }
}

切面类:spa

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AopAspect {

    /**
     * 前置通知:目标方法调用以前执行的代码
     * @param jp
     */
    public void doBefore(JoinPoint jp){
        System.out.println("===========执行前置通知============");
    }

    /**
     * 后置返回通知:目标方法正常结束后执行的代码
     * 返回通知是能够访问到目标方法的返回值的
     * @param jp
     * @param result
     */
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========执行后置通知============");
        System.out.println("返回值result==================="+result);
    }

    /**
     * 最终通知:目标方法调用以后执行的代码(不管目标方法是否出现异常均执行)
     * 由于方法可能会出现异常,因此不能返回方法的返回值
     * @param jp
     */
    public void doAfter(JoinPoint jp){
        System.out.println("===========执行最终通知============");
    }

    /**
     *
     * 异常通知:目标方法抛出异常时执行的代码
     * 在目标方法执行的时候,若是抛出异常,当即进入此方法
     * 能够访问到异常对象
     * @param jp
     * @param ex
     */
    public void doAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println("===========执行异常通知============");
    }

    /**
     * 环绕通知:目标方法调用先后执行的代码,能够在方法调用先后完成自定义的行为。
     * 包围一个链接点(join point)的通知。它会在切入点方法执行前执行同时方法结束也会执行对应的部分。
     * 主要是调用proceed()方法来执行切入点方法,来做为环绕通知先后方法的分水岭。
     *
     * 环绕通知相似于动态代理的全过程:ProceedingJoinPoint类型的参数能够决定是否执行目标方法。
     * 并且环绕通知必须有返回值,返回值即为目标方法的返回值
     * @param pjp
     * @return
     * @throws Throwable
     */
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("======执行环绕通知开始=========");
        // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值
        // 调用proceed()方法,就会触发切入点方法执行
        Object result=pjp.proceed();
        //若是调用pjp.proceed()执行业务方法的时候抛出异常,那么下面的代码将不会执行
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======执行环绕通知结束=========");
        return result;
    }
}

Spring的AOP配置:spring-aop.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: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/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- 声明一个业务类 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 声明通知类 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspect" />

    <aop:config>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="pointcut" expression="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))"/>

            <aop:before method="doBefore" pointcut-ref="pointcut"/>
            <aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/>
            <aop:after method="doAfter" pointcut-ref="pointcut" />
            <aop:around method="doAround" pointcut-ref="pointcut"/>
            <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/>
        </aop:aspect>
    </aop:config>
</beans>

springboot启动类设置:注意要引入xml文件code

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource("classpath:spring-aop.xml")
//@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

测试类:

package com.springboottime.time;

import com.springboottime.time.service.AdviceService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TimeApplicationTests {

    @Autowired
    private AdviceService adviceService;

    @Test
    public void contextLoads() {
    }

    @Test
    public void test(){
        String user = adviceService.findUser();
        System.out.println("<><><><><><><><><><><><><>");
        adviceService.addUser();
    }
}

测试结果:

===========执行前置通知============
======执行环绕通知开始=========
***************执行业务方法findUser,查找的用户名字为:lixiaoxi****************
输出,方法名:findUser;目标对象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======执行环绕通知结束=========
===========执行最终通知============
===========执行后置通知============
返回值result===================lixiaoxi
<><><><><><><><><><><><><>
===========执行前置通知============
======执行环绕通知开始=========
***************执行业务方法addUser****************
===========执行异常通知============
===========执行最终通知============

java.lang.RuntimeException
	at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)

2、使用注解配置AOP

采用注解来作aop, 主要是将写在spring 配置文件中的链接点写到注解里面。

业务接口和业务实现与上边同样,具体切面类以下:

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AopAspectJ {

    /**
     * 必须为final String类型的,注解里要使用的变量只能是静态常量类型的
     */
    public static final String EDP="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))";

    /**
     * 切面的前置方法 即方法执行前拦截到的方法
     * 在目标方法执行以前的通知
     * @param jp
     */
    @Before(EDP)
    public void doBefore(JoinPoint jp){

        System.out.println("=========AopAspectJ执行前置通知==========");
    }


    /**
     * 在方法正常执行经过以后执行的通知叫作返回通知
     * 能够返回到方法的返回值 在注解后加入returning
     * @param jp
     * @param result
     */
    @AfterReturning(value=EDP,returning="result")
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========AopAspectJ执行后置通知============");
    }

    /**
     * 最终通知:目标方法调用以后执行的通知(不管目标方法是否出现异常均执行)
     * @param jp
     */
    @After(value=EDP)
    public void doAfter(JoinPoint jp){
        System.out.println("===========AopAspectJ执行最终通知============");
    }

    /**
     * 在目标方法非正常执行完成, 抛出异常的时候会走此方法
     * @param jp
     * @param ex
     */
    @AfterThrowing(value=EDP,throwing="ex")
    public void doAfterThrowing(JoinPoint jp,Exception ex) {
        System.out.println("===========AopAspectJ执行异常通知============");
    }

    /**
     * 环绕通知:目标方法调用先后执行的通知,能够在方法调用先后完成自定义的行为。
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(EDP)
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{

        System.out.println("======AopAspectJ执行环绕通知开始=========");
        // 调用方法的参数
        Object[] args = pjp.getArgs();
        // 调用的方法名
        String method = pjp.getSignature().getName();
        // 获取目标对象
        Object target = pjp.getTarget();
        // 执行完方法的返回值
        // 调用proceed()方法,就会触发切入点方法执行
        Object result=pjp.proceed();
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======AopAspectJ执行环绕通知结束=========");
        return result;
    }

}

spring的配置:spring-aop-aspectJ.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: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/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- 声明spring对@AspectJ的支持 -->
    <aop:aspectj-autoproxy/>

    <!-- 声明一个业务类 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 声明通知类 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspectJ" />
</beans>

springboot启动类配置:

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

//@ImportResource("classpath:spring-aop.xml")
@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

测试结果:

======AopAspectJ执行环绕通知开始=========
=========AopAspectJ执行前置通知==========
***************执行业务方法findUser,查找的用户名字为:lixiaoxi****************
输出,方法名:findUser;目标对象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======AopAspectJ执行环绕通知结束=========
===========AopAspectJ执行最终通知============
===========AopAspectJ执行后置通知============
<><><><><><><><><><><><><>
======AopAspectJ执行环绕通知开始=========
=========AopAspectJ执行前置通知==========
***************执行业务方法addUser****************
===========AopAspectJ执行最终通知============
===========AopAspectJ执行异常通知============

java.lang.RuntimeException
	at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)
相关文章
相关标签/搜索