Administrator
发布于 2022-09-29 / 50 阅读
0
0

SpringAop

Aop概述

  AOP:(Aspect Oriented Programming)面向切面编程
  OOP:(Object Oriented Programming )面向对象编程

  面向切面编程:基于OOP基础之上新的编程思想;指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式,面向切面编程;

exp:计算器运行计算方法的时候进行日志记录;
加日志记录:
1)、直接编写在方法内部;不推荐,修改维护麻烦;
          日志记录:系统的辅助功能;
          业务逻辑:(核心功能)
          耦合;
          
2)、我们希望的是;
          业务逻辑:(核心功能);日志模块;在核心功能运行期间,自己动态的加上;
          运行的时候,日志功能可以加上;

方法一:可以使用动态代理来将日志代码动态的在目标方法执行前后先进行执行;

/**
 * 帮Calculator.java生成代理对象的类
 * Object newProxyInstance
 * (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
 *
 * @author lfy
 *
 */
public class CalculatorProxy {

    /**
     * 为传入的参数对象创建一个动态代理对象
     * @param calculator
     * @return
     *
     * Calculator calculator:被代理对象;(宝宝)
     * 返回的:宋喆
     */
    public static Calculator getProxy(final Calculator calculator) {
        // TODO Auto-generated method stub

        //方法执行器。帮我们目标对象执行目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             * Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
             * Method method:当前将要执行的目标对象的方法
             * Object[] args:这个方法调用时外界传入的参数值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                
                //System.out.println("这是动态代理将要帮你执行方法...");
                Object result = null;
                try {
                    LogUtils.logStart(method, args);

                    // 利用反射执行目标方法
                    //目标方法执行后的返回值
                    result = method.invoke(calculator, args);
                    LogUtils.logReturn(method, result);
                } catch (Exception e) {
                    LogUtils.logException(method,e);
                }finally{
                    LogUtils.logEnd(method);

                }

                //返回值必须返回出去外界才能拿到真正执行后的返回值
                return result;
            }
        };
        //Class<?>[] interfaces:被代理对象实现的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //ClassLoader loader:被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();

        //Proxy为目标对象创建代理对象;
        Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) proxy;
    }

}

动态代理存在的问题:
1)、写起来难;
2)、jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的;

因为动态代理难;Spring实现了AOP功能;底层就是动态代理;
1)、可以利用Spring一句代码都不写的去创建动态代理;
实现简单,而且没有强制要求目标对象必须实现接口;

将某段代码(日志)动态的切入(不把日志代码写死在业务逻辑方法中)到指定方法(加减乘除)的指定位置(方法的开始、结束、异常。。。)进行运行的这种编程方式(Spring简化了面向切面编程)

AOP的专业术语

Image

ps:
横切关注点:方法从横向看,每一个方法的开始或者结束都可以加日志,叫横切关注点
通知方法:就是要在切入的地方执行的方法
切面类:就是写具体通知方法的那个类

切入点;连接点;切入点表达式的区分
  可以将连接点理解为数据库,切入点表达式就是sql语句;而切入点就是根据sql语句查询出来的结果

AOP的实现

aop所需的依赖
    commons-logging-1.1.3.jar
    spring-aop-4.0.0.RELEASE.jar
    spring-beans-4.0.0.RELEASE.jar
    spring-context-4.0.0.RELEASE.jar
    spring-core-4.0.0.RELEASE.jar
    spring-expression-4.0.0.RELEASE.jar

    Spring支持面向切面编程的包是:
    spring-aspects-4.0.0.RELEASE.jar:基础版
    加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)
    com.springsource.net.sf.cglib-2.2.0.jar
    com.springsource.org.aopalliance-1.0.0.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
在idea中,导入这个依赖就可以
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
            <scope>compile</scope>
        </dependency>
        或者导这个依赖
                <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.23</version>
        </dependency>
        至于我在测试中,课程里说到的加强版的依赖不加也没事,或许有别的用途我没发现
            com.springsource.net.sf.cglib-2.2.0.jar
    	    com.springsource.org.aopalliance-1.0.0.jar

image-1664440416299
实现步骤

 1)、将目标类和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中
 2)、还应该告诉Spring到底哪个是切面类,标注@Aspect注解
 3)、告诉Spring,切面类里面的每一个方法,都是何时何地运行;
 4)、开启基于注解的AOP模式
	<!--包扫描-->
       <context:component-scan base-package="com.lly"/>
      <!--开启基于注解的AOP功能-->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
execution(访问权限符 返回值类型 方法签名)

@Before("execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))")
    public static void logStart(){
        System.out.println("【xxx】方法开始执行,用的参数列表【xxx】");
    }

    //想在目标方法正常执行完成之后执行
    @AfterReturning("execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))")
    public static void logReturn(){
        System.out.println("【xxxx】方法正常执行完成,计算结果是:");
    }

    //想在目标方法出现异常的时候执行
    @AfterThrowing("execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))")
    public static void logException() {
        System.out.println("【xxxx】方法执行出现异常了,异常信息是:;这个异常已经通知测试小组进行排查");
    }

    //想在目标方法结束的时候执行
    @After("execution(public int com.atguigu.impl.MyMathCalculator.*(int, int))")
    public static void logEnd() {
        System.out.println("【xxx】方法最终结束了");
    }

五个通知注解

@Before:在目标方法运行之前
前置通知
@after:在目标方法运行结束之后
后置通知
@AfterReturning:在目标方法正常放回之后
返回通知
@AfterThrowing:在目标方法抛异常之后
异常通知
@Around:环绕
环绕通知


try{
    @Before
    method.invoke(obj,args);
    @AfterReturning
}catch(e){
    @AfterThrowing:在目标方法抛异常之后
}finally{
    @after:在目标方法运行结束之后
}



评论