Administrator
Administrator
Published on 2025-01-06 / 0 Visits
0
0

Springboot(2.7.6)单体工程-AOP介绍(D)

以下文章借鉴了:https://blog.csdn.net/qq_43331014/article/details/132867178

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。

利用AOP可对业务逻辑进行增强,在不改变原有逻辑的基础上,在其前后进行处理。降低了耦合性,减少了大量冗余的操作。特别适合用于大量方法都需要进行相同处理的操作,例如日志记录、事务管理、安全性检查、权限检查、读写分离等

AOP相关概念

AOP就是做了个切面,在不破坏原有方法的基础上,将切面切进去,在其前后进行处理,整体逻辑关系图如下:
Description

切面(Aspect):一般是指被@Aspect修饰的类,代表着某一具体功能的AOP逻辑。
切入点(Pointcut):选择对哪些方法进行增强。
连接点(JoinPoint):就是那些被切入点选中的方法。这些方法会被增强处理。
通知(Advice):对目标方法的增强,有一下五种增强的类型。

  • 环绕通知(@Around):内部执行方法,可自定义在方法执行的前后操作。

  • 前置通知(@Before):在方法执行前执行。

  • 后置通知(@After):在方法执行后执行。

  • 返回通知(@AfterReturning):在方法返回后执行。

  • 异常通知(@AfterThrowing):在方法抛出异常后执行。

对于各种通知的方法、注解等没有什么特别的操作,具体使用会在后面举例。而切入点是选择对哪些方法生效的定义,那怎么知道它选择的是哪些方法呢?因为有多种匹配方式。

表达式类型

说明

execution()

匹配方法,最常用的一个

args()

匹配入参类型

@args()

匹配入参类型上的注解

@annotation()

匹配方法上的注解

用的比较多的是execution()和@annotation

案例:对业务层的方法进行性能监控,把每个方法的执行时间 记录在日志中
    package org.lanqiao.taru.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * 监控注解
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Monitor {
    
    
    }



    package org.lanqiao.taru.aspectj;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 切面类
 * 日志记录(记录方法执行的时间差  )
 */
@Slf4j
@Aspect
@Component
public class LogAspectj {
    /**
     * 定义切点(什么方法进行监控)
     * pointCut  相当于切点的ID
     */
    @Pointcut("execution( public *  org.lanqiao.taru.service..*(..))")
    public  void pointCut(){
    }



    /**
     * 注解切点
     */
    @Pointcut("@annotation(org.lanqiao.taru.annotation.Monitor)")
    public  void pointCut2(){
    }




    /**
     * 环绕通知
     * @return
     * @throws Throwable
     */
    @Around("pointCut()")
    public Object loginfo(ProceedingJoinPoint joinPoint) throws Throwable {
            //开始时间
            long  start  = System.currentTimeMillis();
            //方法放行
            Object proceed = joinPoint.proceed();
            //结束时间
            long  end  = System.currentTimeMillis();
            //获取监控的方法名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            log.info("{}方法性能监控时间为:{}",method.getName(),end-start);
            return proceed;

    }

}



开启AOP注解

@EnableAspectJAutoProxy

@MapperScan("org.lanqiao.taru.dao")
@SpringBootApplication
@EnableAspectJAutoProxy
public class LanqiaoDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(LanqiaoDemoApplication.class, args);
    }

}

@RestController
public class UserinfoController {    
    @GetMapping("/aopTest")
    @Monitor 
    public Result aopTest(){
        System.out.println("正在执行接口");
        return Result.OK("执行成功");
    }
}

AOP的原理介绍:

Spring 框架通过运行时动态代理技术实现目标方法增强,不管是动态代理还是静态代理,它们的实现逻辑是一样的,要么基于继承实现,要么基于接口实现

Description

静态代理设计模式

/**
 * 基于接口的代理 
 */
public class Test {
    /**
     * 学生类
     * @param args
     */
    public static void main(String[] args) {
        HouseMaster  master =new  HouseMaster();  //房东
        ISale  proxy =new LianjiaProxy(master);
        proxy.sale();



    }
}

interface   ISale {

    public void sale();

}



/**
 * 房东类
 */
  class  HouseMaster  implements  ISale  {

    @Override
    public void sale(){
        System.out.println("我是房东,我出租房子3000元");
    }

}


/**
 * 编写一个代理类 ( 如何房东没有接口  那就继承房东  充当房东的儿子  如果房东有接口 就实现接口 冒充房东的兄弟  )
 */
class LianjiaProxy   implements     ISale{
    private  ISale  master;

    public   LianjiaProxy( HouseMaster  master){
        this.master=master;

    }


    /**
     * 必须要跟目标方法一致
     */
    @Override
    public void sale() {
        System.out.println("我妈妈出国了   房租现在涨了2000元");
        master.sale();

    }
}





/**
 *基于继承的代理  静态代理
 */
public class Test {
    /**
     * 学生类
     * @param args
     */
    public static void main(String[] args) {
        HouseMaster  master =new  HouseMaster();  //房东
        HouseMaster  proxy =new LianjiaProxy(master);
        proxy.sale();



    }
}

interface   ISale {

    public void sale();

}



/**
 * 房东类
 */
  class  HouseMaster  {


    public void sale(){
        System.out.println("我是房东,我出租房子3000元");
    }

}


/**
 * 编写一个代理类 ( 如何房东没有接口  那就继承房东  充当房东的儿子 )
 */
class LianjiaProxy  extends   HouseMaster   {
    private  HouseMaster  master;

    public   LianjiaProxy( HouseMaster  master){
        this.master=master;

    }


    /**
     * 必须要跟目标方法一致
     */
    @Override
    public void sale() {
        System.out.println("我妈妈出国了   房租现在涨了2000元");
        master.sale();

    }
}



动态代理设计模式
// 自定义 InvocationHandler  (也可以看做是代理)
public class JDKProxy implements InvocationHandler {
    // 要代理的目标实例
    private Object proxyObject;

    public JDKProxy(Object proxyObject) {
        this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // do something
        Object ret = null;  //方法返回值
        System.out.println("JDK Proxy --- 调用前 --- 调用方法是:"+method.getName() + "---参数是:"+ GsonUtil.toJson(args));
        ret = method.invoke(proxyObject, args); //对目标实例调用invoke方法
        System.out.println("JDK Proxy --- 调用后");
        return ret;
    }
}

// 生成代理类的工厂方法
public static Object createJDKProxyInstance(Object proxyObject){
    JDKProxy jdkProxy = new JDKProxy(proxyObject);
    return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), jdkProxy);
}

// 具体使用的代码
DemoManager demoProxy = (DemoManager) ProxyFactory.createJDKProxyInstance(new DemoManagerCustom());


Comment