Spring系列之AOP达成的两种办法转载

原创
小哥 2年前 (2022-12-26) 阅读数 49 #大杂烩

AOP有两种常用的实现方式, 一种是以声明的方式实现它(基于XML)一种是使用注释来实现(基于AspectJ)

第一次审查AOP一些更重要的概念:

Joinpoint(连接点): 在程序执行的特定时刻,Spring是方法的执行 。
Pointcut(切割点): 用一种更流行的方式来说,spring中AOP切点是需要增强和代理的方法的集合。它通常根据某些约定的规则来表示,例如正则表达式。切点由一类连接点组成。
Advice(通知): 还是用一种更流行的方式来说,就是在指定切点上要干些什么。
Advisor(通知器): 事实上,它是切入点和通知的结合。 。

一、基于XML配置的Spring AOP

以声明方式实现(在XML配置文件中的配置),一般步骤是:配置文件中配置pointcut, 在java用于写入实际aspect 类, 用于相关业务处理的入口点。

业务接口:

package com.spring.service;

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

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

业务实施:

package com.spring.service.impl;

import com.spring.service.IUserManagerService;

public class UserManagerServiceImpl implements IUserManagerService{

private String name;

    public void setName(String name){
        this.name=name;
    }

    public String getName(){
        return this.name;
    }

    public String findUser(){
        System.out.println("============执行业务方法findUser,查找的用户是:"+name+"=============");
        return name;
    }

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

切面类:

package com.spring.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();
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======执行结束环绕通知=========");
        return result;
    }
}

Spring配置:




    
    
        
      

     
    

    
     
        

         
        
         
         
        
      
   

测试类:

package com.spring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.service.IUserManagerService;

public class TestAop {

    public static void main(String[] args) throws Exception{

        ApplicationContext act =  new ClassPathXmlApplicationContext("applicationContext3.xml");
         IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
         userManager.findUser();
         System.out.println("

"); userManager.addUser(); } }

测试结果:

2.使用注释配置AOP

采用注释进行操作aop, 主要将写入spring 配置文件中的连接点将写入注释。

业务接口和业务实现同上,具体节类如下:

package com.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AopAspectJ {

    /**  
     * 必须为final String类型的,注释中使用的变量只能是静态常量类型  
     */  
    public static final String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))";

    /**
     * 切平面的前置法 也就是说,在执行方法之前截获的方法。
      * 目标方法执行前的通知
      * @param jp
     */
    @Before(EDP)
    public void doBefore(JoinPoint jp){

        System.out.println("=========执行预先通知==========");
    }

    /**
     * 在方法正常通过后执行的通知称为返回通知。
      * 可以返回给方法的返回值 在注释后添加returning 
     * @param jp
     * @param result
     */
    @AfterReturning(value=EDP,returning="result")
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========执行后通知============");
    }

    /**
     * 最终通知:在调用目标方法后执行的通知(无论目标方法是否有异常都会执行)
      * @param jp
     */
    @After(value=EDP)
    public void doAfter(JoinPoint jp){
        System.out.println("===========执行最终通知============");
    }

    /**
     * 环绕通知:在目标方法调用之前和之后执行的通知。自定义行为可以在方法调用前后完成。
      * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(EDP)
    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();
        System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
        System.out.println("======执行结束环绕通知=========");
        return result;
    }

    /**
     * 目标方法的异常执行完成, 当抛出异常时,此方法将停止。
      * @param jp
     * @param ex
     */
    @AfterThrowing(value=EDP,throwing="ex")
    public void doAfterThrowing(JoinPoint jp,Exception ex) {
        System.out.println("===========执行异常通知============");
    }
}

spring的配置:




    
        

    
    
        
       

     
    

测试类:

package com.spring.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.service.IUserManagerService;

public class TestAop1 {
    public static void main(String[] args) throws Exception{

        ApplicationContext act =  new ClassPathXmlApplicationContext("applicationContext4.xml");
         IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
         userManager.findUser();
         System.out.println("

"); userManager.addUser(); } }

测试结果与上述相同。

注意:
1.围绕方法通知,围绕方法通知注意,必须在调用后给出返回值,否则代理方法将停止调用并返回。null除非你真的想要。
2.只能使用环绕通知JoinPoint的子类ProceedingJoinPoint,每个连接点类型都可以调用代理的方法并获取和更改返回值。

补充:
1.如果位于元素,命名的切点只能是当前访问中定义的元素,以便整个必须在中指定元素中定义的所有增强访问切点定义如下。
2.如果在直接在元素下定义,必须保证先前定义。您还可以定义,三者在配置有顺序要求:首先必须,然后是,最后是。而在中定义的没有顺序要求,可以在任何地方定义。
.:用于定义可重用的入口点;
.:用于定义只有一个通知和一个入口点的方面;
.用于定义一个切平面,它可以包含多个切入点和通知,标签内的通知和切入点定义是无序的;和advisor不同之处在于,advisor仅包含通知和切入点。
3.在使用spring框架配置AOP是否通过。XML需要定义配置文件和注释方法。pointcut"切入点"
例如,定义切入点表达式 execution( com.sample.service.impl...(..))
execution()是最常用的正切函数,具有以下语法:
整个表达式可以分为五个部分:
(1)、execution(): 表达式的主体。
(2)、第一个
数字:表示返回类型,数字表示所有类型。
(3),package name:表示要拦截的包的名称,后面两个句点表示当前包和当前包的所有子包,com.sample.service.impl包和子包下所有类的方法。
(4)、第二个
数字:表示类名,数字表示所有类别。
(5)、
(..):最后一个星号表示方法名称,*数字表示所有方法,括号表示方法的参数,两个句点表示任何参数。

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门