收藏 分享(赏)

spring入门纲要.doc

上传人:kpmy5893 文档编号:7856915 上传时间:2019-05-27 格式:DOC 页数:33 大小:343.50KB
下载 相关 举报
spring入门纲要.doc_第1页
第1页 / 共33页
spring入门纲要.doc_第2页
第2页 / 共33页
spring入门纲要.doc_第3页
第3页 / 共33页
spring入门纲要.doc_第4页
第4页 / 共33页
spring入门纲要.doc_第5页
第5页 / 共33页
点击查看更多>>
资源描述

1、我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看1一、理论知识1.依赖注入、控制反转依赖注入:在运行期,由外部容器动态地将依赖对象注入到组件中控制反转:应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部窗口负责得。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓的反转。2.spring 的主要特性。(1)降低组件之间的耦合度,实现软件各层之间的解耦。(2)可以使用容器提供的众多服务,如:事务管理服务、消息服务、JMS 服务、持久化服务等等。(3)容器提供单例模式支持,开发人员不再需要自己编写实现

2、代码。(4)容器提供了 AOP 技术,利用它很容易实现如权限拦截,运行期监控等功能。(5)容器提供的众多辅作类,使用这些类能够加快应用的开发,如:JdbcTemplate、HibernateTemplate.(6)对主流的应用框架提供了集成支持。3.常用技术控制反转/依赖注入-面向切入编程-与主流框架的整合、管理-二、基本实例1.准备搭建环境distspring.jarlibjakata-commonscommons-loggin.jar如果使用了切面编程,还需下列 jar 文件:libaspectjaspectjweaver.jar 和 aspectjrt.jarlibcglibcglib-

3、nodep-2.1.3.jar如果使用了 jsr-250 中的注解,还需要下列 jar 文件:libj2eecommon-annotations.jar2.搭建并测试环境建立名为 spring_01_base项目 ,根据需求导入jar包。建立一个Junit测试单元SpringEnvTest,测试代码如下:Testpublic void testEnv() ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml“);beans.xml 配置文件在此省略(见下)。运行此测试如无错,则说明环境搭建成功。说明:bea

4、ns.xml 可以在类路径下进行配置,也可以在具体的目录下配置。可以是一个配置文件,也可以是多个配置文件组成 String 数组传入。3.实例作如下准备工作:(1)建立UseDao接口,代码如下:package com.asm.dao;public interface UserDao void save();(2)建立UserDao接口的实现类,UserDaoImpl我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看2package com.asm.dao.impl;import com.asm.dao.UserDao

5、;public class UserDaoImpl implements UserDaopublic void save() System.out.println(“执行save方法.“);(3)在src目录下配置此beans.xml,它的配置如下:说明:bean 代表一个实质的 java 类,通过它的 id 可以获取一个此类的一个对象。补充:让 xml 配置文件在编译时提示windowspreferencesmyeclipsefiles and editorsxmlxml catalog 点 add,在出现窗口的 location 中选“file system”,然后在 spring 解压目

6、录的 dist/resources目录中选择“spring-beans-2.5.xsd”,并将 key Type 值改为“Schema Location”,key值为:http:/www.springframework.org/schema/beans/spring-beans-2.5.xsd(4)Junit 测试单元 SpringEnvTest 中增加如下代码测试:Testpublic void base() ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml“);UserDao userDao = (

7、UserDao) ctx.getBean(“userDaoImpl“);userDao.save();以上的代码就是通过配置文件 beans.xml 获取所需要的实例对象。4.三种 bean 的生成方式除了上面使用的类直接生成方式,还有 bean 静态工厂及 bean 实例工厂。bean 静态工厂的配置如下:相应的工厂类代码如下:package com.asm.dao.impl;public class UserDaoImplFactory public static UserDaoImpl getUserDaoImpl()return new UserDaoImpl();bean实例工厂的配

8、置如下:我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看3相应的工厂类的代码如下:package com.asm.dao.impl;public class UserDaoImplFactory2 public UserDaoImpl getUserDaoImpl() return new UserDaoImpl();5.bean 的作用域singleton:返回 bean 的同一个实例,也是默认的作用域(无状态 bean 使用此作用域)prototype:每次请求都会创建一个实例(有状态 bean 使用此作用域)r

9、equest、session、global session 这三个作用域主要用在 web 应用中6.bean 的生命周期(1)什么时候初始化 bean 实例当 scopesingleton,即默认情况,会在装载配置文件时实例化。如果希望在调用getBean 时才初始化,可以使用 lazy-init=“true“ 补充:如果希望希望该配置文件中的所有 bean 都延迟初始化,则应在 beans 根结点中使用 lazy-init=“true“。当 scope=prototype 时,在调用 getBean()方法时才会初始化。(2)生命周期:构造器、init 方法、获取 bean 后的操作、des

10、troy 方法(ctx.close、注意如果 bean 的scope 设为 prototype 时,当 ctx.close 时,destroy 方法不会被调用)7.属性注入 Setter 方式(1)简单属性(如String):(2)对象属性-外部 bean 注入:在上面的中增加如下配置:对象属性-内部bean注入:在上面的中增加如下配置:(3)集合属性注入:List值一List值二List值三我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看4Set值二Set值一Set值三firstsecondthird注意:在相应的

11、字段上一定要有 setter 方法,才能注入。补充:使用继承。在beans.xml中的配置如下:相当于在 XXX bean 实例中也有 username 属性设置。8.属性注入构造器方式List值一List值二List值三UserServiceBean对应的构造方法代码如下:我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看5public UserServiceBean(String username, UserDao userDao, Set set) this.username=username;this.user

12、Dao=userDao;this.set=set;注意:此方法会覆盖掉默认的构造方法,导致要依赖默认构造方法的配置不可用,因此我们还应为此类提供一个默认的构造器。三、使用注解方式注入1.准备注解方式的注入主要针对对象属性的注入。使用注解功能要引用注解包,另beans.xml的配置模板如下:2.Resource 注解实例拷贝上一个项目为 spring_02_annotation 项目 ,修改 UserServiceBean 为如下形式:package com.asm.service;public class UserServiceBean Resource(name = “userDaoImpl

13、“)private UserDao userDao;private UserDao userDao2;Resourcepublic void setUserDao2(UserDao userDao2) this.userDao2 = userDao2;public void test() userDao.save();userDao2.save();然后在 bean.xml 中的配置如下:简要说明:Resouce注解可以在字段上标记,也可以在对应的setter方法上标记。此注解可以不使用name属性,它会自动去查找匹配的类型(先以字段名称为name的值查找,如找不我的 je 博客: http:

14、/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看6到会再根据依赖对象的类型查找) 。但只要使用了name属性,就应确保name的值在xml中有相应的bean Id对应。它是属于java本身的注解,Resource默认按属性名称装配3.Autowired注解实例Autowired(required=false)Qualifier(“userDaoImplXXX“)private UserDao userDao3;说明:Autowired 默认是按照类型来查找对应的 bean 实例注入,如果想注入指定名称的bean 实例,可以使用 Qualifier

15、 注解来指定名字。Required 属性设为 true 时,如果不能成功注入则会报告异常,如果为设为 false 而不能成功注入,则会将 userDao3 设为null。同样地,它也实用于 setter 方法。它属于 spring 特有的注解,Autowired 默认按类型装配。4.自动装配自动装配(了解,不建议使用):除了要设置字段的setter方法外,还应在beans.xml配置文件中设置如下内容:说明:除了byType外,autowire的可选属性如下:byName:根据类中的字段名来查找对应的bean,如不能成功注入,则字段设为null.byType:根据类型装配,如果发现多个类型都能

16、够匹配,则抛出异常。Consturctor:也byType相似,不同之处在于它应用于构造器的参数,如果容器中没有找到与构造器参数类型一致的bean,则抛出异常。Autodetect:通过bean类的自省机制来决定是使用consturctor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。四、自动扫描管理 bean1.准备工作在前面使用注解的时候,除了结点配置增加了名称空间说明,另还增加了配置,它的作用就是注册一个处理器。通常情况下,我们要使用某个bean实例,总会配置相关内容。Spring最新版本可以简化这一操作,即是说我们只要在配置文件作如下设置:便可以自

17、动管理指定包名及子包下标住了service (业务层组件) 、controller(控制层组件) 、repository(数据访问组件)或component(泛指组件)的类,并把它们作一个实例bean,相当于在beans.xml中配置了元素。需要说明的是,使用了此配置同时意味着还注册了注解配置的处理器,所以在这些类中用到了注解配置时并不需要再配置。为什么提出自动扫描管理:在一些比较大的项目中,涉及到的bean实例会有很多,如果依次对每个bean实例进行配置,不但配置内容繁琐,而且配置文件也会显得杂乱。因此spring提出了自动扫描bean,它依据在xml文件中指定的包名和类中标记的compon

18、ent系列注解。2.实例建立 spring_03_autoscan项目 ,内容基本和前面两个项目一样,只是在要纳入spring管理的类前增加了component这样的注解。Beans.xml的配置如下:说明:以上的配置会自动管理 service 包和 impl 包及它们子包下的带有 component 标记的类,上面两行配置代码等价于下面以 UserServiceBean 为例进行说明,代码如下:package com.asm.service;Service(“usb“)Scope(“singleton“)public class UserServiceBean Resource(name =

19、 “userDaoImpl“)private UserDao userDao;private UserDao userDao2;Autowired(required = true)Qualifier(“userDaoImpl“)private UserDao userDao3;Resourcepublic void setUserDao2(UserDao userDao2) this.userDao2 = userDao2;public UserServiceBean() PostConstructpublic void init() System.out.println(“init meth

20、od is called“);public void test() System.out.println(“*“);userDao.save();userDao2.save();System.out.println(userDao3);/ userDao3.save();System.out.println(“*“);说明:如果使用Service这些注解时不指定名称,这些实例 bean的名称就是类名(但首字母小写) ,也可以指定实例bean的名字,比如这里指定其名字为“usb”,scope注解配置了bean的作用范围,PostConstruct注解指定了bean的init方法。关于其它的一些注

21、解配置我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看8参文档(3.11-3.12) 。它的junit测试代码如下:public class AutoScanTest Testpublic void base() ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml“);UserServiceBean udb = (UserServiceBean) ctx.getBean(“usb“);udb.test();小结使用自动扫描管理的

22、核心:配置扫描的包、类前的component标记、了解常用注解。五、AOP 技术1.引出问题建立 spring_04_aop项目 ,在该项目下有一个UserDao接口,代码如下:package com.asm.dao;public interface UserDao void save();void update();该接口的实现类 UseDaoImp,代码如下:package com.asm.dao.impl;import com.asm.dao.UserDao;public class UserDaoImp implements UserDao private String usernam

23、e;public UserDaoImp() public UserDaoImp(String username) this.username = username;public String getUsername() return username;Overridepublic void save() System.out.println(“save method is called:“ + username);Overridepublic void update() System.out.println(“update method is called“ + username);需求如下:

24、如果实现类的 username!=null,才可以调用 save 与 update 方法,为 null 则不我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看9能调用。当然要解决此问题,可以在 save 与 update 方法内部进行判断,但是如果在方法内部进行判断,代码则失去了灵活性,如果以后的需求改变,比如变成 username.equals时,则又要在 update/save 方法中重新进行一次判断。如果 save/update 这样的方法很多,这样就会很麻烦。其实要解决此问题,可以通过动态代理技术实现。这里的需

25、求其实就是根据要求来拦截一些业务方法,这种编程问题称之为横切性关注点。代理的目标对象必须实现了一个接口-横切关注点2.动态代理实现JDK建立代理工厂ProxyFactory,代码如下:package com.asm.dao.impl.factory;public class ProxyFactory implements InvocationHandler private Object target;public Object createUserDaoImp(Object target) this.target = target;return Proxy.newProxyInstance(t

26、his.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);Overridepublic Object invoke(Object proxy, Method method, Object args) throws Throwable UserDaoImp udi = (UserDaoImp) target;Object result = null;if (udi.getUsername() != null) result = method.invoke(target, args);

27、return result;简析动态代理:此类根据传递的 target 对象来生成此目标对象的代理对象,需要强调的是动态代理技术所要代理的对象必须实现一个接口。newProxyInstance 参数说明:第一个参数是目标对象的类装载器,第二个参数是目标对象的接口,第三个参数是回调对象,生成的代理对象要执行方法时就是依靠这个回调对象的 invoke 方法来进行目标对象的方法回调。关于动态代理的其它细节不在此讨论。建立 junit 测试代码,内容如下:public class AopProxyTest Test /用户名为空,不执行方法public void testProxy()ProxyFac

28、tory pf=new ProxyFactory();UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp();ud.save();Test /用户名为不为空,才执行save方法public void testProxy2()我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看10ProxyFactory pf=new ProxyFactory();UserDao ud=(UserDao) pf.createUserDaoImp(new UserDaoImp(“张

29、某“);ud.save();3.cglib 实现代理JDK的Proxy实现代理要求被代理的目标对象必须实现一个接口,而如果目标对象没有实现接口则不能使用Proxy来代理。其实也可以借助cglib来实现代理。操作步骤如下步骤一、建立UserDaoImp2类,它与UserDaoImp的唯一区别就是没有实现任何接口。步骤二、导入cglib.jar包,创建cglib代理工厂,代码如下:package com.asm.dao.impl.factory;public class CglibFactory implements MethodInterceptor private Object target;

30、public Object createUserDaoImp2(Object target) this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass(); / cglib创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码。enhancer.setCallback(this); /注册回调器return enhancer.create();Overridepublic Object

31、intercept(Object proxy, Method method, Object args, MethodProxy methodProxy)throws Throwable UserDaoImp2 udi = (UserDaoImp2) target;Object result = null;if (udi.getUsername() != null) / 前置通知try result = methodProxy.invoke(target, args);/ 后置通知 catch (Exception e) e.printStackTrace();/ 例外通知 finally /

32、最终通知return result;说明:注意注释的通知,通知就是拦截到方法后执行的一些代码,比如前置通知,就是说我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看11在回调目标方法时执行的一些操作。步骤三、建立测试代码(省略,和五.2 的测试代码相似)4.aop 理论知识横切性关注点:对哪些方法拦截,拦截后怎么处理,这些关注就称之为横切性关注点切面(aspect):类是对物体特征的抽象,而切面是指对横切性关注点的抽象。连接点(joinpoint):被拦截到的点,因为 spring 只支持方法类型的连接点,所以在spr

33、ing 中连接点指的就是被拦截到的方法。实际上连接点还可以是字段或构造器。切入点(pointcut):对连接点进行拦截的定义。通知(advice):所谓通知就是指拦截到连接点之后的要执行的代码。通知分为前置、后置、异常、最终。环绕通知五类。目标对象:代理的目标对象。织入(weave):将切面应用到目标对象并导致代理对象创建的过程引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。5.基于 spring 的 AOP 实现步骤一、导入 spring 开发的基本包(包括切面及注解包)步骤二、编写切面类 TheInterceptor,代码如下:pack

34、age com.asm.dao.impl.factory;Aspectpublic class TheInterceptor Pointcut(“execution (* com.asm.dao.impl.UserDaoImp.*()“)/ 声明一个切入点(第一个 *后要留一个空格)private void anyMethod() Before(“anyMethod()“)/ 前置通知public void before() System.out.println(“前置通知“ );AfterReturning(“anyMethod()“)/后置通知public void afterReturn

35、ing()System.out.println(“后置通知“ );简析:Aspect 注解声明此类为一个切面类,Pointcut 注解用来声明一个切入点,括号中的参数是切入点的表达式,这里的表达式的意思是对 UserDaoImp 类的所有方法进行拦截。关于切入点表达式后面会有详细的说明。anyMethod 是为切入点起一个名字,后面的“通知”都要依赖这个名字。步骤三、beans.xml 配置文件的内容如下:说明:要想切面类起作用,首先要把切面类纳入spring容器管理。步骤四、编写junit测试单元Testpublic void base() ApplicationContext ctx =

36、new ClassPathXmlApplicationContext(“beans.xml“);/ UserDaoImp udii=(UserDaoImp) ctx.getBean(“userDaoImp“);UserDao ud = (UserDao) ctx.getBean(“userDaoImp“);System.out.println(ud.getClass().getName();ud.save();说明:由于开启了切面编程功能,所以当我们获取一个被切面类监控管理的bean对象UserDaoImp时,它实际上获取的是此对象的一个代理对象,而在spring 中对代理对象的处理有如下原则

37、:(1)如果要代理的对象实现了接口,则会按照Proxy的方式来产生代理对象,这即是说产生的代理对象只能是接口类型,比如起用上面注掉的代码就会报错,而且通过下面的打印语句我们也可以看出产生的是一个代理对象。(2)要代理的对象未实现接口,则按cglib方式来产生代理对象。 另还要注意:要想spring 的切面技术起作用,被管理的bean对象只能是通过spring容器获取的对象。比如这里如果直接 new出UseDaoImp对象,则new出的对象是不能被spring的切面类监控管理。补充:测试被代理对象未实现接口时,spring切面技术的应用。步骤一、修改切面类TheInterceptor切入点为如下

38、内容:Pointcut(“execution (* com.asm.dao.impl.*.*()“)说明:拦截impl包下的所有类所有方法步骤二、在beans.xml中增加如下内容:步骤三、junit测试代码如下:public void base2() ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml“);UserDaoImp2 udi2 = (UserDaoImp2) ctx.getBean(“userDaoImp2“);System.out.println(udi2.getClass().getNa

39、me();System.out.println(udi2.getClass().getSuperclass().getName();udi2.save();我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看13说明:UseDaoImp2 未实现任何接口,因此在 spring 中利用切面技术来管理此类使用的动态代理技术实质是 cglib 的动态代理方式,所以产生的代理对象实质是被代理对象的一个子类,通过上面的控制台打印语句可以看出。小结:(1)声明 aspect 的切面类要纳入 spring 容器管理才能起作用。(2)被

40、管理的bean 实例要通过容器的 getBeans 方法获取。 (3)依据被管理的 bean 是否实现接口,spring 采取两种方式来产生代理对象。(4)在 xml 文件中启用6.通知应用实例(基于注解)在前一节,我们应用了前置通知和后置通知,除了这两个通知外,下面接着演示其它通知的应用。(1)最终通知在切面类TheInterceptor中增加如下代码即可:略去测试。After(“anyMethod()“)/ 最终通知public void after() System.out.println(“最终通知“ );(2)异常通知为了演示此实例,我们在 UseDaoImp 中增加如下代码以抛出异

41、常:int i=1/0;在然后在切面类TheInterceptor 中增加如下代码:AfterThrowing(“anyMethod()“) / 例外通知public void AfterThrowing() System.out.println(“例外通知“ );当获取代理对象并调用 save 方法时会抛出异常,例外通知便会得以执行。(3)环绕通知Around(“anyMethod()“) /环绕通知public Object around(ProceedingJoinPoint pjp) throws Throwable System.out.println(“进入环绕“ );/if()

42、/ 进行一些判断,再执行环绕Object result = pjp.proceed();/System.out.println(“退出环绕“ );return result;注意的是方法的参数及抛出异常类型的固定写法(方法名可以是任意得),另在该方法中必须执行 pjp.proceed()才能让环绕通知中的两处打印代码得以执行。即是说要想环绕通知的拦截处理代码起作用必须调用 pjp.proceed 方法。 补充:环绕通知通常可以用来测试方法的执行时间,在 pjp.proceed 前获取一个时间,在 pjp.proceed 方法后再获取一个时间。最后两个时间相减即可得方法执行时间。(4)传递参数给

43、通知首先在UseDao接口中增加如下代码:String add(String name);然后再在UserDaoImp中实现此方法,代码如下:public String add(String name) 我的 je 博客: http:/ 文章错误之处肯请大家留言指正,再此谢过。文章编排目录结构,大家可以在文档结构图下很方便地查看14System.out.println(“add method is called “ + name+“ “);return “添加成功“;需求:获取调用add方法传递的参数。操作步骤如下:在切面类增加如下代码:Before(“anyMethod() 说明:在前置通知

44、的方法中有一个参数,然后再把此参数作为拦截条件(即是说拦截带有一个String类型参数的方法)。args的名字和beforeAdd方法参数名字相同。测试代码:public void advieeTest() ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml“);UserDao ud=(UserDao) ctx.getBean(“userDaoImp“);ud.add(“xxx“);(5)获取方法的返回值我们知道add方法有一个返回值,我们对此方法进行拦截并获取返回值,在切面类中增加如下代码:AfterR

45、eturning(pointcut = “anyMethod()“, returning = “result“)/ 后置通知,监听返回结果,针对 UserDaoImp的getUsername()方法public void afterReturningRes(String result) System.out.println(“后置通知, 返回结果:“ + result);说明:afterReturningRes 方法的参数就是要返回的参数类型,returning 标记的就是的结果,它的取值与该方法参数名相同。 测试代码同(4)。(6)获取抛出的异常切面类的增加如下代码:AfterThrowin

46、g(pointcut=“anyMethod“,throwing=“e“)public void catchException(Exception e)System.out.println(“获取抛出的异常:“ +e); throwing 的取值和方法的参数名相同,测试代码省略。7. 通知应用实例(基于 XML)步骤一、复制 TheInterceptorX类为 TheInterceptorXML,并去掉所有注解。步骤二、建立beansXML.xml配置文件,内容如下:-声明一个切面类测试代码如下:public void advieeTest() ApplicationContext ctx =

47、new ClassPathXmlApplicationContext(“beansXML.xml“);UserDao ud=(UserDao) ctx.getBean(“userDaoImp“);ud.add(“xxx“);未解决问题:不能成功传参给前置通知。8.解析切入点表达式1.格式:execution(返回值 空格 方法选择)。两部分组成,中间一定要有空格。返回值:可以是*,说明拦截任何方法。Java.lang.String(全名),拦截返回值为 String类型的方法。 常用的实例如下:方法选择:包名类名.*()。设定要拦截的方法签名。表达式(省略 execution) 说明(java.lang.String 方法选择略) 拦截返回值为 String 类型的方法(!void 方法选择略) 拦截返回值非空的方法(* com.asm*.*() 拦截 com.as

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报