做报名统计的网站微门户网站建设
news/
2025/9/27 18:35:18/
文章来源:
做报名统计的网站,微门户网站建设,学视频剪辑大概需要多少钱,四川省建筑人才网个人中心前面两篇文章记录了 Spring IOC 的相关知识#xff0c;本文记录 Spring 中的另一特性 AOP 相关知识。 部分参考资料#xff1a; 《Spring实战#xff08;第4版#xff09;》 《轻量级 JavaEE 企业应用实战#xff08;第四版#xff09;》 Spring 官方文档 W3CSchool Spri…前面两篇文章记录了 Spring IOC 的相关知识本文记录 Spring 中的另一特性 AOP 相关知识。 部分参考资料 《Spring实战第4版》 《轻量级 JavaEE 企业应用实战第四版》 Spring 官方文档 W3CSchool Spring教程 易百教程 Spring教程 一、AOP——另一种编程思想
1.1 什么是 AOP
AOP Aspect Orient Programming,直译过来就是 面向切面编程。AOP 是一种编程思想是面向对象编程OOP的一种补充。面向对象编程将程序抽象成各个层次的对象而面向切面编程是将程序抽象成各个切面。 从《Spring实战第4版》图书中扒了一张图
从该图可以很形象地看出所谓切面相当于应用对象间的横切点我们可以将其单独抽象为单独的模块。
1.2 为什么需要 AOP
想象下面的场景开发中在多个模块间有某段重复的代码我们通常是怎么处理的显然没有人会靠“复制粘贴”吧。在传统的面向过程编程中我们也会将这段代码抽象成一个方法然后在需要的地方分别调用这个方法这样当这段代码需要修改时我们只需要改变这个方法就可以了。然而需求总是变化的有一天新增了一个需求需要再多出做修改我们需要再抽象出一个方法然后再在需要的地方分别调用这个方法又或者我们不需要这个方法了我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
1.3 AOP 实现分类
AOP 要达到的效果是保证开发者不修改源代码的前提下去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码看到这其实应该明白了AOP 其实就是前面一篇文章讲的代理模式的典型应用。 按照 AOP 框架修改源代码的时机可以将其分为两类
静态 AOP 实现 AOP 框架在编译阶段对程序源代码进行修改生成了静态的 AOP 代理类生成的 *.class 文件已经被改掉了需要使用特定的编译器比如 AspectJ。动态 AOP 实现 AOP 框架在运行阶段对动态生成代理对象在内存中以 JDK 动态代理或 CGlib 动态地生成 AOP 代理类如 SpringAOP。
下面给出常用 AOP 实现比较
如不清楚动态代理的可参考我前面的一篇文章有讲解静态代理、JDK动态代理和 CGlib 动态代理。 静态代理和动态代理 https://www.cnblogs.com/joy99/p/10865391.html
二、AOP 术语
AOP 领域中的特性术语
通知Advice: AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。连接点join point: 连接点表示应用执行过程中能够插入切面的一个点这个点可以是方法的调用、异常的抛出。在 Spring AOP 中连接点总是方法的调用。切点PointCut: 可以插入增强处理的连接点。切面Aspect: 切面是通知和切点的结合。引入Introduction引入允许我们向现有的类添加新的方法或者属性。织入Weaving: 将增强处理添加到目标对象中并创建一个被增强的对象这个过程就是织入。
概念看起来总是有点懵并且上述术语不同的参考书籍上翻译还不一样所以需要慢慢在应用中理解。
三、初步认识 Spring AOP
3.1 Spring AOP 的特点
AOP 框架有很多种1.3节中介绍了 AOP 框架的实现方式有可能不同 Spring 中的 AOP 是通过动态代理实现的。不同的 AOP 框架支持的连接点也有所区别例如AspectJ 和 JBoss,除了支持方法切点它们还支持字段和构造器的连接点。而 Spring AOP 不能拦截对对象字段的修改也不支持构造器连接点,我们无法在 Bean 创建时应用通知。
3.2 Spring AOP 的简单例子
下面先上代码对着代码说比较好说看下面这个例子 这个例子是基于gradle创建的首先 build.gradle 文件添加依赖Maven也可以导入依赖
dependencies {compile org.springframework:spring-context:5.0.6.RELEASE
}首先创建一个接口 IBuy.java
package com.sharpcj.aopdemo.test1;public interface IBuy {String buy();
}Boy 和 Gril 两个类分别实现了这个接口 Boy.java
package com.sharpcj.aopdemo.test1;import org.springframework.stereotype.Component;Component
public class Boy implements IBuy {Overridepublic String buy() {System.out.println(男孩买了一个游戏机);return 游戏机;}
}Girl.java
package com.sharpcj.aopdemo.test1;import org.springframework.stereotype.Component;Component
public class Girl implements IBuy {Overridepublic String buy() {System.out.println(女孩买了一件漂亮的衣服);return 衣服;}
}配置文件, AppConfig.java
package com.sharpcj.aopdemo;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;Configuration
ComponentScan(basePackageClasses {com.sharpcj.aopdemo.test1.IBuy.class})
public class AppConfig {
}测试类 AppTest.java
package com.sharpcj.aopdemo;import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AppTest {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);Boy boy context.getBean(boy,Boy.class);Girl girl (Girl) context.getBean(girl);boy.buy();girl.buy();}
}运行结果
这里运用SpringIOC里的自动部署。现在需求改变了我们需要在男孩和女孩的 buy 方法之前需要打印出“男孩女孩都买了自己喜欢的东西”。用 Spring AOP 来实现这个需求只需下面几个步骤 1、 既然用到 Spring AOP, 首先在 build.gralde 文件中引入相关依赖
dependencies {compile org.springframework:spring-context:5.0.6.RELEASEcompile org.springframework:spring-aspects:5.0.6.RELEASE
}2、 定义一个切面类BuyAspectJ.java
package com.sharpcj.aopdemo.test1;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;Aspect
Component
public class BuyAspectJ {Before(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void haha(){System.out.println(男孩女孩都买自己喜欢的东西);}
}这个类我们使用了注解 Component 表明它将作为一个Spring Bean 被装配使用注解 Aspect 表示它是一个切面。 类中只有一个方法 haha 我们使用 Before 这个注解表示他将在方法执行之前执行。关于这个注解后文再作解释。 参数(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..))) 声明了切点表明在该切面的切点是com.sharpcj.aopdemo.test1.Ibuy这个接口中的buy方法。至于为什么这么写下文再解释。 3、 在配置文件中启用AOP切面功能
package com.sharpcj.aopdemo;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;Configuration
ComponentScan(basePackageClasses {com.sharpcj.aopdemo.test1.IBuy.class})
EnableAspectJAutoProxy(proxyTargetClass true)
public class AppConfig {
}
我们在配置文件类增加了EnableAspectJAutoProxy注解启用了 AOP 功能参数proxyTargetClass的值设为了 true 。默认值是 false两者的区别下文再解释。 OK下面只需测试代码,运行结果如下
我们看到结果与我们需求一致我们并没有修改 Boy 和 Girl 类的 Buy 方法也没有修改测试类的代码几乎是完全无侵入式地实现了需求。这就是 AOP 的“神奇”之处。
四、通过注解配置 Spring AOP
4.1 通过注解声明切点指示器
Spring AOP 所支持的 AspectJ 切点指示器 在spring中尝试使用AspectJ其他指示器时将会抛出IllegalArgumentException异常。
当我们查看上面展示的这些spring支持的指示器时注意只有execution指示器是唯一的执行匹配而其他的指示器都是用于限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器在此基础上我们使用其他指示器来限制所匹配的切点。
下图的切点表达式表示当Instrument的play方法执行时会触发通知。 我们使用execution指示器选择Instrument的play方法方法表达式以 * 号开始标识我们不关心方法的返回值类型。然后我们指定了全限定类名和方法名。对于方法参数列表我们使用 .. 标识切点选择任意的play方法无论该方法的入参是什么。 多个匹配之间我们可以使用链接符 、||、来表示 “且”、“或”、“非”的关系。但是在使用 XML 文件配置时这些符号有特殊的含义所以我们使用 “and”、“or”、“not”来表示。
举例 限定该切点仅匹配的包是 com.sharpcj.aopdemo.test1,可以使用 execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) within(com.sharpcj.aopdemo.test1.*) 在切点中选择 bean,可以使用 execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) bean(girl) 修改 BuyAspectJ.java
package com.sharpcj.aopdemo.test1;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;Aspect
Component
public class BuyAspectJ {Before(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)) within(com.sharpcj.aopdemo.test1.*) bean(girl))public void hehe(){System.out.println(男孩女孩都买自己喜欢的东西);}
}
此时切面只会对 Girl.java 这个类生效执行结果
细心的你可能发现了切面中的方法名已经被我悄悄地从haha改成了hehe丝毫没有影响结果说明方法名没有影响。和 Spring IOC 中用 java 配置文件装配 Bean 时用Bean 注解修饰的方法名一样没有影响。
4.2 通过注解声明 5 种通知类型
Spring AOP 中有 5 中通知类型分别如下
下面修改切面类
package com.sharpcj.aopdemo.test1;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;Aspect
Component
public class BuyAspectJ {Before(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void hehe() {System.out.println(before ...);}After(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void haha() {System.out.println(After ...);}AfterReturning(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void xixi() {System.out.println(AfterReturning ...);}Around(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void xxx(ProceedingJoinPoint pj) {try {System.out.println(Around aaa ...);pj.proceed();System.out.println(Around bbb ...);} catch (Throwable throwable) {throwable.printStackTrace();}}}
为了方便看效果,我们测试类中只要 Boy 类:
package com.sharpcj.aopdemo;import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AppTest {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);Boy boy context.getBean(boy,Boy.class);Girl girl (Girl) context.getBean(girl);boy.buy();// girl.buy();}
}
执行结果如下
结果显而易见。指的注意的是 Around 修饰的环绕通知类型是将整个目标方法封装起来了在使用时我们传入了 ProceedingJoinPoint 类型的参数这个对象是必须要有的并且需要调用 ProceedingJoinPoint 的 proceed() 方法。 如果没有调用 该方法执行结果为
Around aaa ...
Around bbb ...
After ...
AfterReturning ...可见如果不调用该对象的 proceed() 方法表示原目标方法被阻塞调用当然也有可能你的实际需求就是这样。
4.3 通过注解声明切点表达式
如你看到的上面我们写的多个通知使用了相同的切点表达式对于像这样频繁出现的相同的表达式我们可以使用 Pointcut注解声明切点表达式然后使用表达式修改代码如下 BuyAspectJ.java
package com.sharpcj.aopdemo.test1;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;Aspect
Component
public class BuyAspectJ {Pointcut(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void point(){}Before(point())public void hehe() {System.out.println(before ...);}After(point())public void haha() {System.out.println(After ...);}AfterReturning(point())public void xixi() {System.out.println(AfterReturning ...);}Around(point())public void xxx(ProceedingJoinPoint pj) {try {System.out.println(Around aaa ...);pj.proceed();System.out.println(Around bbb ...);} catch (Throwable throwable) {throwable.printStackTrace();}}
}
程序运行结果没有变化。 这里我们使用
Pointcut(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))
public void point(){}
声明了一个切点表达式该方法 point 的内容并不重要方法名也不重要实际上它只是作为一个标识供通知使用。
4.4 通过注解处理通知中的参数
上面的例子我们要进行增强处理的目标方法没有参数下面我们来说说有参数的情况并且在增强处理中使用该参数。 下面我们给接口增加一个参数表示购买所花的金钱。通过AOP 增强处理如果女孩买衣服超过了 68 元就可以赠送一双袜子。 更改代码如下 IBuy.java
package com.sharpcj.aopdemo.test1;public interface IBuy {String buy(double price);
}
Girl.java
package com.sharpcj.aopdemo.test1;import org.springframework.stereotype.Component;Component
public class Girl implements IBuy {Overridepublic String buy(double price) {System.out.println(String.format(女孩花了%s元买了一件漂亮的衣服, price));return 衣服;}
}
Boy.java
package com.sharpcj.aopdemo.test1;import org.springframework.stereotype.Component;Component
public class Boy implements IBuy {Overridepublic String buy(double price) {System.out.println(String.format(男孩花了%s元买了一个游戏机, price));return 游戏机;}
}
再看 BuyAspectJ 类我们将之前的通知都注释掉。用一个环绕通知来实现这个功能
package com.sharpcj.aopdemo.test1;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;Aspect
Component
public class BuyAspectJ {/*Pointcut(execution(* com.sharpcj.aopdemo.test1.IBuy.buy(..)))public void point(){}Before(point())public void hehe() {System.out.println(before ...);}After(point())public void haha() {System.out.println(After ...);}AfterReturning(point())public void xixi() {System.out.println(AfterReturning ...);}Around(point())public void xxx(ProceedingJoinPoint pj) {try {System.out.println(Around aaa ...);pj.proceed();System.out.println(Around bbb ...);} catch (Throwable throwable) {throwable.printStackTrace();}}*/Pointcut(execution(String com.sharpcj.aopdemo.test1.IBuy.buy(double)) args(price) bean(girl))public void gif(double price) {}Around(gif(price))public String hehe(ProceedingJoinPoint pj, double price){try {pj.proceed();if (price 68) {System.out.println(女孩买衣服超过了68元赠送一双袜子);return 衣服和袜子;}} catch (Throwable throwable) {throwable.printStackTrace();}return 衣服;}
}
前文提到当不关心方法返回值的时候我们在编写切点指示器的时候使用了 * 当不关心方法参数的时候我们使用了 ..。现在如果我们需要传入参数并且有返回值的时候则需要使用对应的类型。在编写通知的时候我们也需要声明对应的返回值类型和参数类型。
测试类AppTest.java
package com.sharpcj.aopdemo;import com.sharpcj.aopdemo.test1.Boy;
import com.sharpcj.aopdemo.test1.Girl;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AppTest {public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);Boy boy context.getBean(boy,Boy.class);Girl girl (Girl) context.getBean(girl);String boyBought boy.buy(35);String girlBought girl.buy(99.8);System.out.println(男孩买到了 boyBought);System.out.println(女孩买到了 girlBought);}
}
测试结果 可以看到我们成功通过 AOP 实现了需求并将结果打印了出来。
4.5 通过注解配置织入的方式
前面还有一个遗留问题在配置文件中我们用注解 EnableAspectJAutoProxy() 启用Spring AOP 的时候我们给参数 proxyTargetClass 赋值为 true,如果我们不写参数默认为 false。这个时候运行程序程序抛出异常
这是一个强制类型转换异常。为什么会抛出这个异常呢或许已经能够想到这跟Spring AOP 动态代理的机制有关这个 proxyTargetClass 参数决定了代理的机制。当这个参数为 false 时 通过jdk的基于接口的方式进行织入这时候代理生成的是一个接口对象将这个接口对象强制转换为实现该接口的一个类自然就抛出了上述类型转换异常。 反之proxyTargetClass 为 true则会使用 cglib 的动态代理方式。这种方式的缺点是拓展类的方法被final修饰时无法进行织入。 测试一下我们将 proxyTargetClass 参数设为 true同时将 Girl.java 的 Buy 方法用 final 修饰 AppConfig.java
package com.sharpcj.aopdemo;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;Configuration
ComponentScan(basePackageClasses {com.sharpcj.aopdemo.test1.IBuy.class})
EnableAspectJAutoProxy(proxyTargetClass true)
public class AppConfig {
}
Girl.java
package com.sharpcj.aopdemo.test1;import org.springframework.stereotype.Component;Component
public class Girl implements IBuy {Overridepublic final String buy(double price) {System.out.println(String.format(女孩花了%s元买了一件漂亮的衣服, price));return 衣服;}
}
此时运行结果
可以看到我们的切面并没有织入生效。
五、通过 XML 配置文件声明切面
前面的示例中我们已经展示了如何通过注解配置去声明切面下面我们看看如何在 XML 文件中声明切面。下面先列出 XML 中声明 AOP 的常用元素
我们依然可以使用 元素他能够自动代理AspectJ注解的通知类。
5.1 XML 配置文件中切点指示器
在XML配置文件中切点指示器表达式与通过注解配置的写法基本一致区别前面有提到即XML文件中需要使用 “and”、“or”、“not”来表示 “且”、“或”、“非”的关系。
5.2 XML 文件配置 AOP 实例
下面我们不使用任何注解改造上面的例子 BuyAspectJ.java
package com.sharpcj.aopdemo.test2;import org.aspectj.lang.ProceedingJoinPoint;public class BuyAspectJ {public void hehe() {System.out.println(before ...);}public void haha() {System.out.println(After ...);}public void xixi() {System.out.println(AfterReturning ...);}public void xxx(ProceedingJoinPoint pj) {try {System.out.println(Around aaa ...);pj.proceed();System.out.println(Around bbb ...);} catch (Throwable throwable) {throwable.printStackTrace();}}
}
在 Resource 目录下新建一个配置文件 aopdemo.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdbean idboy classcom.sharpcj.aopdemo.test2.Boy/beanbean idgirl classcom.sharpcj.aopdemo.test2.Girl/beanbean idbuyAspectJ classcom.sharpcj.aopdemo.test2.BuyAspectJ/beanaop:config proxy-target-classtrueaop:aspect idqiemian refbuyAspectJaop:before pointcutexecution(* com.sharpcj.aopdemo.test2.IBuy.buy(..)) methodhehe/aop:after pointcutexecution(* com.sharpcj.aopdemo.test2.IBuy.buy(..)) methodhaha/aop:after-returning pointcutexecution(* com.sharpcj.aopdemo.test2.IBuy.buy(..)) methodxixi/aop:around pointcutexecution(* com.sharpcj.aopdemo.test2.IBuy.buy(..)) methodxxx//aop:aspect/aop:config
/beans
这里分别定义了一个切面里面包含四种类型的通知。 测试文件中使用
ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(aopdemo.xml);
来获取 ApplicationContext其它代码不变。
5.3 XML 文件配置声明切点
对于频繁重复使用的切点表达式我们也可以声明成切点。 配置文件如下aopdemo.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdbean idboy classcom.sharpcj.aopdemo.test2.Boy/beanbean idgirl classcom.sharpcj.aopdemo.test2.Girl/beanbean idbuyAspectJ classcom.sharpcj.aopdemo.test2.BuyAspectJ/beanaop:config proxy-target-classtrueaop:pointcut idapoint expressionexecution(* com.sharpcj.aopdemo.test2.IBuy.buy(..))/aop:aspect idqiemian refbuyAspectJaop:before pointcut-refapoint methodhehe/aop:after pointcut-refapoint methodhaha/aop:after-returning pointcut-refapoint methodxixi/aop:around pointcut-refapoint methodxxx//aop:aspect/aop:config
/beans
5.4 XML文件配置为通知传递参数
BuyAspectJ.java
package com.sharpcj.aopdemo.test2;import org.aspectj.lang.ProceedingJoinPoint;public class BuyAspectJ {
public String hehe(ProceedingJoinPoint pj, double price){try {pj.proceed();if (price 68) {System.out.println(女孩买衣服超过了68元赠送一双袜子);return 衣服和袜子;}} catch (Throwable throwable) {throwable.printStackTrace();}return 衣服;}
}
aopdemo.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdbean idboy classcom.sharpcj.aopdemo.test2.Boy/beanbean idgirl classcom.sharpcj.aopdemo.test2.Girl/beanbean idbuyAspectJ classcom.sharpcj.aopdemo.test2.BuyAspectJ/beanaop:config proxy-target-classtrueaop:pointcut idapoint expressionexecution(String com.sharpcj.aopdemo.test2.IBuy.buy(double)) and args(price) and bean(girl)/aop:aspect idqiemian refbuyAspectJaop:around pointcut-refapoint methodhehe//aop:aspect/aop:config
/beans5.5 Xml 文件配置织入的方式
同注解配置类似, CGlib 代理方式
aop:config proxy-target-classtrue /aop:configJDK 代理方式
aop:config proxy-target-classfalse /aop:config六、总结
本文简单记录了 AOP 的编程思想然后介绍了 Spring 中 AOP 的相关概念以及通过注解方式和XML配置文件两种方式使用 Spring AOP进行编程。 相比于 AspectJ 的面向切面编程Spring AOP 也有一些局限性但是已经可以解决开发中的绝大多数问题了如果确实遇到了 Spring AOP 解决不了的场景我们依然可以在 Spring 中使用 AspectJ 来解决。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/919798.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!