简述 AOP 动态代理

一、AopAutoConfiguration 源码:

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {

		ClassProxyingConfiguration(BeanFactory beanFactory) {
			if (beanFactory instanceof BeanDefinitionRegistry) {
				BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
				AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
		}

	}

}
  1. CglibAutoProxyConfiguration 类 matchIfMissing = true,所以默认使用 cglib 动态代理
  2. application.properties 里配置如下:spring.aop.auto=false,整个 AOP 都不会生效了
  3. application.properties 里配置如下:spring.aop.proxy-target-class=false,使用 jdk 动态代理

JDK 动态代理的限制在于,它只能代理实现了接口的类,如果一个类没有实现任何接口,JDK 动态代理就无法代理它,这是因为 JDK 动态代理是基于接口的代理,它生成的代理对象会实现指定接口,然后通过该接口来调用被代理类的方法。

需要注意的是,如果接口有多个实现类,并且你使用 @Autowired 注解注入时, Spring 会抛出异常,因为它无法确定应该注入哪个实现类的代理对象,在这种情况下,你需要明确指定要注入的实现类,可以使用 @Qualifier 注解或者在实现类上使用 @Primary 注解来解决这个问题

二、不使用 springboot,手动写 JDK 动态代理案例

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public interface MyInterface {
    void myMethod();
}

public class MyInterfaceImpl implements MyInterface {
    public void myMethod() {
        System.out.println("Real object's method is called.");
    }
}

public class MyInvocationHandler implements InvocationHandler {
    private MyInterface target;

    public MyInvocationHandler(MyInterface target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy object's method is called before real object's method.");
        Object result = method.invoke(target, args);
        System.out.println("Proxy object's method is called after real object's method.");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        MyInterface realObject = new MyInterfaceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(realObject);

        MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[]{MyInterface.class},
                handler
        );

        proxyObject.myMethod();
    }
}

在JDK动态代理中,代理对象实现了指定接口,并且在运行时动态生成代理实例。被代理的类必须实现至少一个接口,而代理对象会实现这个接口,并且在方法调用时会委托给InvocationHandler中的逻辑

在这个例子中,proxyObject是MyInterface接口的代理对象。代理对象实现了MyInterface接口,并且在invoke方法中执行了额外的逻辑。当proxyObject.myMethod()被调用时,代理对象会先执行invoke方法中的逻辑,然后再调用MyInterfaceImpl实现类的myMethod方法

三、不使用 springboot,手动写 CGLIB 动态代理案例

CGLIB(Code Generation Library)是一个功能强大的字节码生成库,它可以在运行时动态生成类的子类,常用于代理那些没有实现接口的类。以下是一个简单的CGLIB动态代理的使用案例:

  1. pom 文件
java 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  1. 然后,考虑以下的被代理类 UserService:
java 复制代码
public class UserService {
    public void saveUser() {
        System.out.println("Saving user...");
    }
}
  1. 现在,我们会使用CGLIB为它生成一个代理对象,并在方法调用前后添加额外的逻辑:
java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class UserServiceProxy implements MethodInterceptor {

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

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before method execution");
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println("After method execution");
        return result;
    }

    public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        UserService userServiceProxyInstance = (UserService) userServiceProxy.createProxy(new UserService());
        userServiceProxyInstance.saveUser();
    }
}
  1. 在上述代码中,MethodInterceptor 接口用于定义拦截器的逻辑。在 intercept

    方法中,我们在方法调用前后添加了额外的逻辑。Enhancer 类用于生成代理类,它设置了被代理类的父类和拦截器。createProxy

    方法接受一个目标对象,返回一个代理对象。

    运行上述代码会输出以下结果:

java 复制代码
Before method execution
Saving user...
After method execution
相关推荐
云深麋鹿5 分钟前
一.算法复杂度
c语言·开发语言·算法
少控科技7 分钟前
QT进阶日记009
开发语言·qt
CodeCraft Studio10 分钟前
从框架到体验:Qt + Qtitan 构建制造业嵌入式UI整体解决方案
开发语言·qt·ui·gui·嵌入式开发·hmi·制造业嵌入式ui
AIFQuant10 分钟前
如何快速接入贵金属期货实时行情 API:python 实战分享
开发语言·python·金融·数据分析·restful
Remember_99311 分钟前
【数据结构】Java对象比较全解析:从equals到Comparable与Comparator,再到PriorityQueue应用
java·开发语言·数据结构·算法·leetcode·哈希算法
郝学胜-神的一滴13 分钟前
深入浅出网络协议:从OSI七层到TCP/IP五层模型全解析
开发语言·网络·c++·网络协议·tcp/ip·程序人生
qq_4061761417 分钟前
吃透JS异步编程:从回调地狱到Promise/Async-Await全解析
服务器·开发语言·前端·javascript·php
@大迁世界21 分钟前
停止使用 innerHTML:3 种安全渲染 HTML 的替代方案
开发语言·前端·javascript·安全·html
jun_bai26 分钟前
conda环境配置nnU-Net生物医学图像分割肺动脉静脉血管
开发语言·python
程序员zgh32 分钟前
C语言 弱定义机制 解读
c语言·开发语言·c++