文档章节

Spring AOP 创建代理的源码解析

TSMYK
 TSMYK
发布于 01/01 19:40
字数 2860
阅读 91
收藏 3

相关文章

Spring AOP 注解方式源码解析

Spring AOP 功能使用详解

Spring 的 getBean 方法源码解析

Spring bean 创建过程源码解

Spring 中 bean 注册的源码解析

前言

在上篇文章 Spring AOP 注解方式源码解析 中已经获取到了 bean 的对应增强器,之后,就可以创建对应的代理了,Spring AOP 底层使用的是 JDK 动态代理和 CGLIB 的代理,在什么情况下使用JDK动态代理,什么时候使用 CGLIB 代理呢,下面通过源码来看一下

// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	//......
    // 获取的增强器
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理
		Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}
    //............
	return bean;
}

创建代理 createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    // ....
	ProxyFactory proxyFactory = new ProxyFactory();
    //复制当前类的一些属性
	proxyFactory.copyFrom(this);
    // 如果在配置文件中配置的aop标签的属性proxy-target-class为false,
	if (!proxyFactory.isProxyTargetClass()) {
        // 是否需要代理当前类而不是代理接口,根据preserveTargetClass属性来判断Boolean.TRUE.equals(bd.getAttribute("preserveTargetClass")
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
            // 如果代理的是接口,则添加代理接口
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}
    // 对增强器进行包装,有些增强是通过拦截器等方式来实现的,所以这里统一封装为 Advisor 进行处理
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 加入增强器
	proxyFactory.addAdvisors(advisors);
    // 设置要代理的类
	proxyFactory.setTargetSource(targetSource);
    // 用户自定义代理
	customizeProxyFactory(proxyFactory);
    // 该属性用来控制代理工厂被配置以后,是否还允许修改通知,默认为false
	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}
    // 创建代理
	return proxyFactory.getProxy(getProxyClassLoader());
}

// 添加接口代理
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
	Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
	boolean hasReasonableProxyInterface = false;
	//....
	if (hasReasonableProxyInterface) {
		for (Class<?> ifc : targetInterfaces) {
			proxyFactory.addInterface(ifc);
		}
	}
	else {
		proxyFactory.setProxyTargetClass(true);
	}
}

封装增强,在Spring中,有些增强是通过拦截器来实现的,所以这里统一封装为 Advisor 进行处理,对应方法 buildAdvisors():

protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
	//解析所有的 InterceptorName
	Advisor[] commonInterceptors = resolveInterceptorNames();

	List<Object> allInterceptors = new ArrayList<>();
	if (specificInterceptors != null) {
        // 添加参数传进来的,即我们自定义的增强
		allInterceptors.addAll(Arrays.asList(specificInterceptors));
		if (commonInterceptors.length > 0) {
            // 添加拦截器
			if (this.applyCommonInterceptorsFirst) {
				allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
			}
			else {
				allInterceptors.addAll(Arrays.asList(commonInterceptors));
			}
		}
	}
    //把拦截器包装为Advisor
	Advisor[] advisors = new Advisor[allInterceptors.size()];
	for (int i = 0; i < allInterceptors.size(); i++) {
        // wrap包装
		advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
	}
	return advisors;
}

//wrap包装
// 仅仅对 Advisor 和 Advice进行包装
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    // 如果本来就是 Advisor,则直接返回
	if (adviceObject instanceof Advisor) {
		return (Advisor) adviceObject;
	}
    // 类型不正确,异常
	if (!(adviceObject instanceof Advice)) {
		throw new UnknownAdviceTypeException(adviceObject);
	}
	Advice advice = (Advice) adviceObject;
	if (advice instanceof MethodInterceptor) {
        // MethodInterceptor 类型使用 DefaultPointcutAdvisor 封装
		return new DefaultPointcutAdvisor(advice);
	}
    // 如果存在 Advisor 的适配器,也需要包装
	for (AdvisorAdapter adapter : this.adapters) {
		if (adapter.supportsAdvice(advice)) {
			return new DefaultPointcutAdvisor(advice);
		}
	}
	throw new UnknownAdviceTypeException(advice);
}

创建代理getProxy

return proxyFactory.getProxy(getProxyClassLoader());

public Object getProxy(ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

createAopProxy():

在这里会判断代理创建的方式,是使用 JDK 的动态代理还是 CGLIB 的代理。

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    //这里初步判断代理的创建方式,如果不满足则直接使用 JDK 动态代理,如果满足条件,则进一步在判断是否使用 JKD 动态代理还是 CGLIB 代理
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("......");
		}
        // 如果代理的是接口或者设置代理的类就是当前类,则使用 JDK 动态代理
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
        // 否则使用 CGLIB 代理
		return new ObjenesisCglibAopProxy(config);
	}
    // 条件不满足CGBLIB的方式直接使用JDK动态代理
	else {
		return new JdkDynamicAopProxy(config);
	}
}

这里的 if 条件有三个:

1. config.isOptimize() : 用来控制通过 CGLIB 创建的代理是否使用激进的优化策略,目前仅用于 CGLIB 代理

2. config.isProxyTargetClass() :  在 Spring AOP 功能使用详解 中了解到,我们可以强制 Spring 完全使用 CGLIB 进行代理,只要在配置文件配置 proxy-target-class 属性为true即可,如:<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>,如果配置个该属性,则会使用 CGLIB 来创建代理

3. hasNoUserSuppliedProxyInterfaces(config) : 是否存在代理接口,如果不存在代理接口,则使用 CGLIB 进行代理

如果这三个条件有一个满足,则会再进一次判断,需要代理的类是否是接口或者是否设置的就是代理当前类,如果是,则还是会使用 JDK 动态代理,否则的话才会使用 CGLIB 代理。

如果代理类实现了接口,则Spring默认使用 JDK 动态代理,但可以设置强制使用 CGLIB 代理

JDK 动态代理只能代理接口而不能代理类

CGLIB 代理类,通过继承的方式,为目标类生成子类,并重写方法来实现代理,它不能代理final的类或方法

关于 JDK 动态代理和 CGLIB 代理的使用方式可以参考 Spring AOP 功能使用详解

接下来看下 JDK 动态代理和 CGLIB 代理的创建过程:

JDK 动态代理

return new JdkDynamicAopProxy(config);

// JdkDynamicAopProxy.java
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
	this.advised = config;
}

通过 JDK 动态代理来获取代理的方法 getProxy():

public Object getProxy(@Nullable ClassLoader classLoader) {
    // 获取代理类的接口
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // 处理 equals , hashcode 方法
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 创建代理
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

可以看到,Spring 使用 JDK 创建代理和我们使用的 JDK 来创建代理是没有区别的,都是使用 Proxy.newProxyInstance 的方式来创建;我们知道 JDK 动态代理有个 invoke 方法,用来执行目标方法,而 JdkDynamicAopProxy 实现了 InvocationHandler 接口,所有它也会重写该方法,在该方法中植入增强:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	MethodInvocation invocation;
	Object oldProxy = null;
	boolean setProxyContext = false;
    // 目标类
	TargetSource targetSource = this.advised.targetSource;
	Object target = null;

	try {
        // 如果接口没有定义 equals 方法且当前方法是 equals 方法,则不会增强,直接返回
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			return equals(args[0]);
		}
		else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 如果接口没有定义 hashCode方法且当前方法是 hashCode方法,则不会增强,直接返回
			return hashCode();
		}
		else if (method.getDeclaringClass() == DecoratingProxy.class) {
			return AopProxyUtils.ultimateTargetClass(this.advised);
		}
		else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
				method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 如果方法所在的类和Advised是同一个类或者是父类子类关系,则直接执行
			return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}
        // 返回值
		Object retVal;
        
        // 这里对应的是expose-proxy属性的应用,把代理暴露处理
        // 目标方法内部的自我调用将无法实施切面中的增强,所以在这里需要把代理暴露出去
		if (this.advised.exposeProxy) {
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}

		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);

		// 获取该方法的拦截器
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		//如果方法的拦截器为空,则直接执行目标方法,避免创建 MethodInvocation 对象
		if (chain.isEmpty()) {
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // 执行目标方法:method.invoke(target, args)
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
            // 把所有的拦截器封装在ReflectiveMethodInvocation中,以便于链式调用 
			invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 执行拦截器链
			retVal = invocation.proceed();
		}
        // ..........
		return retVal;
	}
	finally {
      // .            
	}
}

在执行拦截器方法 proceed 中执行增强方法,比如前置增强在方法之前执行,后置增强在方法之后执行,proceed 方法如下:

public Object proceed() throws Throwable {
	//当执行完所有增强方法后执行目标方法
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // method.invoke(target, args)
		return invokeJoinpoint();
	}
     // 获取下一个要执行的拦截器
	Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 动态匹配
		InterceptorAndDynamicMethodMatcher dm = interceptorOrInterceptionAdvice;
		Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 如果能够匹配,则执行拦截器的方法,
		if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 比如 @After @Before 对应的增强器(拦截器)的方法
            // 比如 @After 对应的增强器 AspectJAfterAdvice 的invoke方法为:MethodInvocation.proceed();
			return dm.interceptor.invoke(this);
		}
		else {
			// 如果动态匹配失败,则跳过该拦截器,执行下一个拦截器
			return proceed();
		}
	}
	else {
		// 普通拦截器,直接调用
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

在该方法中完成了增强的植入,主要逻辑就是,每个方法都会有一个拦截器链,在 AOP 中我们称之为增强,然后循环执行每个拦截器链,当执行完所有的拦截器后,才会执行目标方法。比如 @After 对应的增强器AspectJAfterAdvice, @Around 对应的增强器AspectJAroundAdvice等。

以上就是 Spring 通过 JDK 动态代理来实现 AOP 的一个过程。

CGLIB 代理

ObjenesisCglibAopProxy 继承于 CglibAopProxy

return new ObjenesisCglibAopProxy(config)

public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
	this.advised = config;
	this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}

CglibAopProxy 的 getProxy 方法如下:

public Object getProxy(@Nullable ClassLoader classLoader) {
    // 代理的目标类
	Class<?> rootClass = this.advised.getTargetClass();
	Class<?> proxySuperClass = rootClass;
	if (ClassUtils.isCglibProxyClass(rootClass)) {
		proxySuperClass = rootClass.getSuperclass();
		Class<?>[] additionalInterfaces = rootClass.getInterfaces();
		for (Class<?> additionalInterface : additionalInterfaces) {
			this.advised.addInterface(additionalInterface);
		}
	}
	// 创建并配置 CGLIB Enhancer
	Enhancer enhancer = createEnhancer();
	if (classLoader != null) {
		enhancer.setClassLoader(classLoader);
		if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
			enhancer.setUseCache(false);
		}
	}
	enhancer.setSuperclass(proxySuperClass);
	enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

    // 设置拦截器
	Callback[] callbacks = getCallbacks(rootClass);
	Class<?>[] types = new Class<?>[callbacks.length];
	for (int x = 0; x < types.length; x++) {
		types[x] = callbacks[x].getClass();
	}
	enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
	enhancer.setCallbackTypes(types);

	//生成代理类和创建代理
	return createProxyClassAndInstance(enhancer, callbacks);
}

// 生成代理类和创建代理
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
	enhancer.setInterceptDuringConstruction(false);
	enhancer.setCallbacks(callbacks);
	return (this.constructorArgs != null && this.constructorArgTypes != null ?
			enhancer.create(this.constructorArgTypes, this.constructorArgs) :
			enhancer.create());
}

从上述的方法可知,Sping 使用 CGLIB 来创建代理类和代理对象和我们使用的一样,都是使用 Enhancer.create() 来创建,这里主要的是设置拦截器,通过 getCallbacks () 方法来实现的,如下:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
	//expose-proxy 属性
	boolean exposeProxy = this.advised.isExposeProxy();
	boolean isFrozen = this.advised.isFrozen();
	boolean isStatic = this.advised.getTargetSource().isStatic();

	// 将拦截器封装在 DynamicAdvisedInterceptor 中
	Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

	//暴露代理
	Callback targetInterceptor;
	if (exposeProxy) {
		targetInterceptor = (isStatic ?
				new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
				new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
	}
	else {
		targetInterceptor = (isStatic ?
				new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
				new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
	}
    // 将拦截器 aopInterceptor 进入到 Callback 中 
	Callback[] mainCallbacks = new Callback[] {
			aopInterceptor,  // for normal advice
			targetInterceptor,  // invoke target without considering advice, if optimized
			new SerializableNoOp(),  // no override for methods mapped to this
			targetDispatcher, this.advisedDispatcher,
			new EqualsInterceptor(this.advised),
			new HashCodeInterceptor(this.advised)
	};
    // ..............
	return callbacks;
}

我们知道使用 CGLIB 来实现代理功能的时候,当代理执行的时候,会调用 intercept 方法,和 JKD 动态代理的 invoke 方法类似;Spring 中 CGLIB 的 intercept 方法如下,该方法在 DynamicAdvisedInterceptor 中,从上面的代理知道,使用它来封装拦截器,它是 CglibAopProxy 的一个子类:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy){
	Object oldProxy = null;
	boolean setProxyContext = false;
	Object target = null;
    // 目标类
	TargetSource targetSource = this.advised.getTargetSource();
    // 处理 expose-proxy 属性,暴露代理
	if (this.advised.exposeProxy) {
		oldProxy = AopContext.setCurrentProxy(proxy);
		setProxyContext = true;
	}
	target = targetSource.getTarget();
	Class<?> targetClass = (target != null ? target.getClass() : null);
   
    // 获取拦截器链
	List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    // 返回值
	Object retVal;

	if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
		Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        // 如果拦截器为空则直接执行目标方法
		retVal = methodProxy.invoke(target, argsToUse);
	}
	else {
		//封装拦截器链并执行
		retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
	}
    // 处理返回值类型
	retVal = processReturnType(proxy, target, method, retVal);
	return retVal;
    // .....................
}

CGLIB 使用 CglibMethodInvocation 来封装拦截器链,它是 CglibAopProxy 的一个内部类:

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

	@Nullable
	private final MethodProxy methodProxy;

	public CglibMethodInvocation(Object proxy, Object target, Method method,Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

		super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);

		this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
				method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
				!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
				methodProxy : null);
	}

	// proceed 方法会调用该方法来执行
	@Override
	protected Object invokeJoinpoint() throws Throwable {
		if (this.methodProxy != null) {
			return this.methodProxy.invoke(this.target, this.arguments);
		}
		else {
			return super.invokeJoinpoint();
		}
	}
}

当调用 proceed 方法时,和 JDK 的处理是一样的,只不过当执行完所有的拦截器后,执行目标方法调用的是 CglibMethodInvocation  的 invokeJoinpoint 来执行而已;因为 CglibMethodInvocation 继承于 ReflectiveMethodInvocation ,而 JDK 使用的就是 ReflectiveMethodInvocation 来执行的,ReflectiveMethodInvocation 的 invokeJoinpoint 方法为 : method.invoke(target, args)

以上就是 Spring  使用 JDK 动态代理和 CGLIB 代理来实现 AOP 的原理。

 

 

© 著作权归作者所有

TSMYK
粉丝 96
博文 81
码字总数 197813
作品 0
成都
程序员
私信 提问
深入聊一聊 Spring AOP 实现机制!

作者 | 张书康 责编 | 郭 芮 AOP(Aspect-Oriented Programming,即面向切面编程。Spring Aop 在 Spring框架中的地位举足轻重,主要用于实现事务、缓存、安全等功能。本篇主要是对源码进行深...

CSDN资讯
01/26
0
0
spring-AOP原理与应用

什么是AOP Spring是解决实际开发中的一些问题: * AOP解决OOP中遇到的一些问题.是OOP的延续和扩展. AOP作用 对程序进行增强:不修改源码的情况下. * AOP可以进行权限校验,日志记录,性能监控,事...

叫我北北
2018/06/29
0
0
Spring AOP 功能使用详解

相关文章 Spring 中 bean 注册的源码解析 Spring bean 创建过程源码解析 Spring 的 getBean 方法源码解析 前言 AOP 既熟悉又陌生,了解过 Spring 人的都知道 AOP 的概念,即面向切面编程,可...

TSMYK
2018/12/25
0
0
dubbo源码理解(1)启动初始化与bean加载

今天看了一些博文,都是关于dubbo源码解析方面的。觉得有必要记一下。 问题1:spring 如何注入dubbo 的?或者说怎么集成dubbo 的,或者说 dubbo启动时怎么启动spring的? 1、首先想要实现 在...

明瞐
2018/10/13
0
0
Spring AOP 源码分析 - 筛选合适的通知器

1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析。本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出合适的通知器(Advisor...

java高级架构牛人
2018/06/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

火焰图(flame graph)是性能分析利器

Perf命令 Perf 命令(performance的简写)是 Linux 系统原生提供的性能分析工具,返回 CPU 正在执行的函数名以及调用栈(stack)。 通常,它的执行频率是 99Hz(每秒99次),如果99次都返回同一个...

呼呼南风
6分钟前
0
0
 好程序员大数据知识点精讲 大数据之Linux

好程序员大数据知识点精讲 大数据之Linux -Linux是什么? Linux是一套作业系统,不是应用程序Linux的基本思想有两点:第一,一切都是文件;第二,每个软件都有确定的用途。 Shell——命令行解...

好程序员IT
10分钟前
0
0
mysql 多行结合

select a1.email as email ,a1.bg ,IFNULL(a1.bg, a2.bg) from ( select * from test01 where sdate = '2019-09-11' ) a1 LEFT join (select * from test01 where sdate = '2019-09-10') a2 ......

昏鸦
12分钟前
0
0
Netflix Eureka 续约 & 更新注册表信息

Eureka Client 要定期的向 Eureka Server 发送心跳请求以保持续约的状态。 也需要定期的从 Eureka Server 获取服务注册表数据,并将服务注册表数据缓存在客户端实例内。 Eureka Client 续约 ...

BryceLoski
15分钟前
11
0
IT兄弟连 Java语法教程 Java开发环境 JVM、JRE、JDK

要想开发Java程序,就需要知道什么是JVM、JRE以及JDK。JVM是运行Java程序的核心,JRE是支持Java程序运行的环境,而JDK是Java开发的核心,下面我们分别具体介绍它们以及它们之间的关系。 1.J...

老码农的一亩三分地
24分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部