# 22. AOP代理

## AOP代理

通常来说Spring AOP有两种代理方式，一种默认的JDK代理，只能代理接口，一种是CGLIB代理，可以代理具体的类对象。

SpringAOP默认为对AOP代理使用标准的JDK动态代理。如果业务对象不实现接口，则使用CGLIB。

如果使用CGLIB，要注意对于CGLIB，不能advice final方法，因为它们不能在运行时生成的子类中被重写。

由于Spring的AOP框架基于代理的特性，根据定义，**目标对象内的方法调用不会被拦截**。对于JDK代理，只能截获对代理的公共接口方法调用。使用cglib，可以截获代理上的公共和受保护的方法调用（如果需要，甚至可以截获包可见的方法）。

> 如果需要拦截在目标类内的方法调用甚至构造函数，那么考虑使用Spring驱动的native AspectJ weaving，而不是Spring的基于代理的AOP框架。

要强制使用CGLIB代理，请将<aop:config>元素的proxy target class属性的值设置为true，如下所示：

```xml
<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>
```

要在使用@Aspectj auto proxy支持时强制cglib代理，请将<aop:aspectj-autoproxy>元素的proxy-target-class属性设置为true，如下所示：

```xml
<aop:aspectj-autoproxy proxy-target-class="true"/>
```

## AOP Proxies原理

SpringAOP是基于代理的，那什么是代理呢？

首先我们考虑一个最简单的POJO对象：

```java
public class SimplePojo implements Pojo {

    public void foo() {
        // this next method invocation is a direct call on the 'this' reference
        this.bar();
    }

    public void bar() {
        // some logic...
    }
}
```

如果直接调用该对象的方法，则运行原理如下所示：

![](https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/images/aop-proxy-plain-pojo-call.png)

调用方法如下：

```java
public class Main {

    public static void main(String[] args) {
        Pojo pojo = new SimplePojo();
        // this is a direct method call on the 'pojo' reference
        pojo.foo();
    }
}
```

如果是调用代理，则运行原理如下：

![](https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/images/aop-proxy-call.png)

调用方法如下：

```java
public class Main {

    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());

        Pojo pojo = (Pojo) factory.getProxy();
        // this is a method call on the proxy!
        pojo.foo();
    }
}
```

本文的例子请参考[aop-proxy](https://github.com/ddean2009/spring5-core-workshop)

更多教程请参考 [flydean的博客](http://www.flydean.com/spring5-aop-proxy/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/www.flydean.com/docs/spring/01-springbase/00022-spring5-aop-proxy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
