likes
comments
collection
share

一文速通Spring AOP使用与原理1. 什么是AOP AOP面向切面编程是Spring中的重要特性之一,其本质是一种

作者站长头像
站长
· 阅读数 36

1. 什么是AOP

AOP面向切面编程是Spring中的重要特性之一,其本质是一种方法增强的方式。何为方法增强?简单来说,AOP可以帮助我们在不修改程序源码的前提下增加对方法的前置与后置处理。比方说我们需要对N个方法的入参做统一的校验,或者说我们需要在N个方法中都加上日志打点,这个时候,我们借助AOP,只需要实现一个切面,之后将注释加在所需的方法上就可以了。可以看出,AOP有两个很大的优势:

  1. 无代码侵入,有很好的扩展性。
  2. 使用简单,复用性强。

2. Spring的AOP的使用方式

2.1. xml配置

原始的服务:

public interface OriService {
    public void start();
}

@Component
public interface OriServiceImpl {
    public void start() {
        System.out.println("启动流程");
    }
}

代理类:

@Component
public class AOPService implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        beforeInvoke();
        Object object = methodInvocation.proceed();
        afterInvoke();
        return object;
    }

    public void beforeInvoke(){
        System.out.println("执行方法前");
    }

    public void afterInvoke(){
        System.out.println("执行方法后");
    }
}

xml配置:


    <!-- 切面配置 -->
    <bean id="Aop" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames">
            <list>
                <value>OriService</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>AOPService</value>
            </list>
        </property>
    </bean>

测试结果:

OriService oriService = (OriService)context.getBean("oriService");
oriService.start();

运行结果:

执行方法前
启动流程
执行方法后

2.2. 注解的方式实现

注解类代码:

@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
@Inherited
public @interface AOPAnnotation {
    String field();
}

原始的服务:

public interface OriService {
    public void start();
}

@Component
public interface OriServiceImpl {
    @AOPAnnotation
    public void start() {
        System.out.println("启动流程");
    }
}

切面逻辑的实现:

@Aspect
@Component
public class AOPService {

    public AOPService(){

    }

    @Pointcut("annotation(Spring.AOPAnnotation)")
    public void startPoint()

    @Before("startPoint()")
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        System.out.println("执行方法前");
    }

    @AfterReturning("startPoint()")
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
        System.out.println("执行方法后");
    }
}

3. Spring AOP的概念解释

相信通过上面具体实现的例子,大家都已经对AOP有了基本的认识,其本质就是对方法进行增加,通过分非侵入式的过程增加前置或者后置处理。

在AOP中有很多概念与术语,这些并不需要死记硬背,在使用的时候查询,慢慢理解就好:

  1. join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法或特定异常的抛出。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
  2. point cut(切入点):本质上是一个捕获连接点的结构,是一系列连接点的集合。在AOP中,可以定义一个point cut,来捕获相关方法的调用。例如上面代码的切入点就是方法的注释。
  3. advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。 Spring中advice有以下几种:
    • Before(前置通知):org.apringframework.aop.MethodBeforeAdvice
    • After-returning(返回通知 == final通知):org.springframework.aop.AfterReturningAdvice
    • After-throwing(抛出通知):org.springframework.aop.ThrowsAdvice
    • Introduction(引入通知):org.springframework.aop.IntroductionInterceptor
    • Around(环绕通知):org.aopaliance.intercept.MethodInterceptor
  1. 切面(Aspect) :通知和切入点共同组成了切面:时间、地点和要发生的“故事”
  2. 引入(Introduction) :引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
  3. 目标对象(Target Object):包含连接点的对象。也被称作被通知或被代理对象。
  4. AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  5. 织入(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。

4. Spring实现AOP的原理

4.1. JDK动态代理

JDK动态代理是一种动态代理技术,它通过在运行时创建代理对象来将切面逻辑织入目标对象的方法中。JDK动态代理通过接口来实现代理,它要求目标对象实现一个接口,然后通过代理对象来调用接口中的方法。在Spring AOP中,使用JDK动态代理需要使用代理工厂来创建代理对象,并将切面逻辑织入目标对象的方法中。

以下是一个解释JDK动态代理的代码示例:

// 定义一个接口
public interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

// 实现接口的类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("Add user: " + username);
    }

    @Override
    public void deleteUser(String username) {
        System.out.println("Delete user: " + username);
    }
}

// 创建一个切面类,定义通知
public class MyAspect {
    public void before() {
        System.out.println("Before method execution");
    }

    public void after() {
        System.out.println("After method execution");
    }
}

// 配置Spring AOP
public class Main {
    public static void main(String[] args) {
        // 创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();

        // 设置目标对象和切面
        proxyFactory.setTarget(new UserServiceImpl());
        proxyFactory.addAdvice(new MyAspect());

        // 获取代理对象
        UserService proxy = (UserService) proxyFactory.getProxy();

        // 调用代理对象的方法
        proxy.addUser("Alice");
        proxy.deleteUser("Bob");
    }
}

可以看出,JDK动态代理的本质是在运行时生成一个借口的代理类,在运行被增强的方法时,直接运行代理类而不是原方法,以起到方法增强的效果。

4.2. CGLIB动态代理

CGLIB(Code Generation Library)是一个基于ASM(Java字节码操作框架)的代码生成类库,它通过生成目标类的子类,并重写其中的方法来实现动态代理。CGLIB动态代理的底层原理主要包括以下几个步骤:

  1. 目标类的子类生成:CGLIB通过ASM生成目标类的子类,并重写其中的方法。这个子类将成为动态代理类,它会代理目标类的方法调用。
  2. 方法拦截:CGLIB通过重写目标类的方法,在方法前后插入拦截逻辑,从而实现对目标方法的拦截和增强。
  3. 动态类加载:CGLIB使用目标类的ClassLoader来加载生成的子类,从而实现对目标类的动态代理。

通过上述步骤,CGLIB实现了对目标类的动态代理,并可以在目标方法调用前后插入自定义逻辑,实现对目标方法的增强和拦截。

与JDK动态代理相比,CGLIB动态代理可以代理没有实现接口的类,但生成的动态代理类会比JDK动态代理复杂的多。因此,在Spring中,默认有接口的类在生成AOP代理的时候,是优先采用JDK动态代理的。

转载自:https://juejin.cn/post/7413958997844082738
评论
请登录