Spring学习-05课 SpringAOP介绍与使用

180 阅读4分钟

SpingAOP的介绍与原理

AOP: Aspect Oriented Programming 面向切面编程

OOP: Object Oriented Programming 面向对象编程

面向切面编程:基于OOP基础之上的新的编程思想。OOP面向的主要对象是类,而AOP面向的主要对象是 切面。 在处理日志、安全管理、事务管理有非常重要的作用。AOP是Spring中重要的核心点,虽然IOC容器没有依赖AOP,但是AOP提供了非常强大的功能,用来对IOC做补充。

通俗讲:在不修改原有代码的情况下,将跟主要业务没有关系的公共功能代码增强到写好方法的指定位置。这种编程方式称为AOP。

AOP的底层用的是代理。代理是一种设计模式 分为静态代理和动态代理

静态代理 为每一个被代理的类创建一个“代理类”

动态代理 (AOP底层用的是动态代理) JDK动态代理 和 CGLIB动态代理

JDK动态代理基于反射完成,必须保证被代理的类 实现了 接口

CGLIB动态代理 类 无论是否实现 接口 都能被代理

JDK动态代理

1、基于java.lang.reflect.Proxy调用newProxyInstance。

public  static Object createProxy(Object needProxy){
/*ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h*/
    ClassLoader loader = needProxy.getClass().getClassLoader();
    Class<?>[] interfaces = needProxy.getClass().getInterfaces();
    InvocationHandler h = new MyInvocationHandler(needProxy);


    Object o = Proxy.newProxyInstance(loader, interfaces, h);
    System.out.println(o.getClass());
    return o;
}

2、调用 InvocationHandler 重构方法。

public class MyInvocationHandler implements InvocationHandler {
    Object target;
    public MyInvocationHandler(Object target) {

        this.target = target;

    }

    //所有代理的类方法都经过了 这个方法

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //执行真正的方法
        /**
         * Object obj,
         * Object... args
         *
         *
         * **/
        Object result;
        if(args ==null ||args.length==0){
            System.out.println("没有参数");
            result = method.invoke(target);
        }else{
            System.out.println("日志: 参数"+ Arrays.asList(args));


            result = method.invoke(target, args);
        }

        return result;
    }
}

3、调用代理方法

ICalculator proxy = (ICalculator) MainTest.createProxy(new Calculator());

System.out.println(proxy.mul(11,22));

IGamePlayer proxy1 = (IGamePlayer)MainTest.createProxy(new GamePlayer("张三"));
proxy1.start();

cglib动态代理

1、将依赖加入pom.xml

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>

2、实现 MethodInterceptor 方法。

package com.kdy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibImpl implements MethodInterceptor {
    private Object target;

    public CglibImpl(Object target) {
        this.target = target;
    }


    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("方法执行前增强");

        Object invoke = methodProxy.invoke(target, objects);




        System.out.println("方法执行后增强");
        return invoke;

    }
}

3、调用 CglibImpl代理类

Calculator calculator = new Calculator();
CglibImpl cglib = new CglibImpl(calculator);

Calculator proxy = (Calculator)cglib.getProxy();


System.out.println(proxy.add(1,2));

AOP概念相关

1、AOP 可以写在方法前 方法后 异常 方法结束。灵活 简单
2、AOP 可以动态判断使用(选择)JDK代理或CGLIB代理 并将代理类交给IOC管理

LogUtil跟主要业务没有关系的公共代码增强模块,有 before() after() afterException() afterEnd()

切面(Aspect)

image.png

image.png

AOP包含以下类型的通知:

  • 前置通知(Before advice): 在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它引发异常)。

  • 后置返回通知(After returning advice):在连接点正常完成后执行的通知(例如,当方法没有抛出任何异常并正常返回时)。

  • 后置异常通知(After throwing advice): 在方法抛出异常退出时执行的通知。

  • 后置通知(总会执行)(After (finally) advice): 当连接点退出的时候执行的通知(无论是正常返回还是异常退出)。

  • 环绕通知(Around Advice):环绕连接点的通知,例如方法调用。这是最强大的一种通知类型,。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行连接点或直接返回自定义的返回值又或抛出异常将执行结束。

AOP实现

1、pom.xml添加依赖

<scope>runtime</scope>去掉,否则 影响系统对@Aspect的 识别

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.19</version>
    <scope>runtime</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.24</version>
</dependency>

2、创建一个类 并标注@Aspect和 @Component
声明类为切面 交给Spring去统一管理

@Aspect
@Component
public class LogUtil {

3、添加相应的方法

@Aspect
@Component
public class LogUtil {


   @Before("execution(* com.kdy.service..*.*(..))")
   public static void beforem(){
       System.out.println("前置通知");
   }


   @After("execution(* com.kdy.service..*.*(..))")
   public static void after(){
       System.out.println("后置通知");
   }
   //后置异常通知

   @AfterThrowing("execution(* com.kdy.service..*.*(..))")
   public static void aftetException(){
       System.out.println("后置异常通知");
   }

   @AfterReturning("execution(* com.kdy.service..*.*(..))")
   public static void afterEnd(){
       System.out.println("方法返回");
   }

}

@Before("execution(* com.kdy.service...(..))")

一个.代表 子包

两个..代表 子孙包

. 前面的代表所有的类 后面的代表所有的方法

(..) 括号内的..代表所有参数

4、编写JavaConfig配置文件

<?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:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

   <context:component-scan base-package="com.kdy">
   </context:component-scan>
<!--开启AOP功能-->
   <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


</beans>

5、调用执行

ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring_ioc.xml");
UserService bean = ioc.getBean(UserService.class);
bean.getUser(1);
System.out.println("###########");
bean.getUser(null);

CGlib方式: class com.kdy.service.impl.RoleServiceImplEnhancerBySpringCGLIBEnhancerBySpringCGLIBbe075109

JDK方式 class com.sun.proxy.$Proxy27