ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringMVC的类型转换器和数据格式化

2020-12-11 16:29:08  阅读:155  来源: 互联网

标签:格式化 String java SpringMVC return user 转换器 public


一. 类型转换器

前端传入的值,从表单中传入的值,都是字符串或者是字符串数组的形式传入的,在后端需要进行手动的转换类型,然后才能正确的使用。 框架一般对常见的数据类型的转换进行了封装提供,如字符串转换成数字等。

SpringMVC 也提供了一些内置的转换器。

有标量的转换器,即字符串到数字,字符串到Boolean等。
(图片引用于: http://c.biancheng.net/view/4415.html)
有图片。

也有集合类型的转换器, 如数组到集合, 拼接型字符器(如,) 到集合等。
(图片引用于: http://c.biancheng.net/view/4415.html)
有图片。

发现,没有常用的字符串到日期 Date 的转换器。 (如,生日属性,入职日期属性等).

SpringMVC 中,如年龄 age, 转到后端时可以自动转换成 数字类型,这就是内置转换器 StringToNumberConverterFactory 的作用。
内置转换器虽然强大,基本的开发使用是够了,但无法对自定义的类型进行转换。 所以,我们可以自定义类型转换器。

类型转换器,可以用 PropertyEditor 接口, (java 提供的) 来进行相应的转换。 但这种转换有缺点: 只能 是字符串转换成其他的java 对象,不能够java 对象类型到java其他对象类型之间 进行转换。

也可以利用 SpringMVC 的 Convert 接口进行类型转换。 推荐使用第二种。

程序中所使用到的 对象类

User.java

package com.yjl.pojo;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 所用的员工类
 * @author 12905
 *
 */
public class User{
	private Integer id;
    private String name;
    private Integer age;
    private String sex;
    private String description;
	//定义一个幻想的工资属性
	 private double salary;
    //定义一个点类
    private Point point;
    
    //定义一个日期类
    
    private Date birthday;
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
	public Point getPoint() {
		return point;
	}

	public void setPoint(Point point) {
		this.point = point;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

    
}

Point.java

package com.yjl.pojo;
//自定义类型转换的 坐标 Point 类
public class Point {
	private double x;
	private double y;
	public double getX() {
		return x;
	}
	public void setX(double x) {
		this.x = x;
	}
	public double getY() {
		return y;
	}
	public void setY(double y) {
		this.y = y;
	}
	@Override
	public String toString() {
		return "Point [x=" + x + ", y=" + y + "]";
	}
}

二. PropertyEditor 类型转换器的使用。

二.一 编写 坐标类 类型转换器

先看一下 PropertyEditor 接口。

在这里插入图片描述

抽象方法太多,一般不直接实现这个接口,而去 继承它的实现类 PropertyEditorSupport 类。

有图片。

PointConvert.java 转换类

package com.yjl.convert;

import java.beans.PropertyEditorSupport;

import com.yjl.pojo.Point;

public class PointConvert extends PropertyEditorSupport{

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		//不进行异常的处理,只执行正确的程序。
		String arr[]=text.split(",");
		//分解数据
		double x=Double.parseDouble(arr[0]);
		double y=Double.parseDouble(arr[1]);
		//设置对象
		Point point=new Point();
		point.setX(x);
		point.setY(y);
		setValue(point);
	}
}

二.二 前端页面编写 login.jsp

<body>
	<h2>两个蝴蝶飞,类型转换器使用</h2>
	<form:form commandName="user" action="login.action" method="post">
		<form:label path="name">姓名:</form:label>
		<form:input path="name"/><br/>
		
		<form:label path="point">坐标:</form:label>
		<form:input path="point"/><br/>
		
		<form:button>提交</form:button>
	</form:form>
</body>

一个普通名称 name, 一个需要转换的属性 point

二.三 后端处理

@Controller
@RequestMapping(value="/user")
public class UserAction {
	
	//类型转换器注册 初始化绑定
	@InitBinder  
    public void initBinder(WebDataBinder binder) {  
		//Point 类 用 new PointConvert() 
       binder.registerCustomEditor(Point.class,new PointConvert());
    } 
	
	//转到登录的页面
	@RequestMapping(value="toLogin")
	public String toLogin(Model model){
		model.addAttribute("user",new User());
		return "user/login";
	}
	//绑定到user对象。
	@RequestMapping(value="login") 
	public String login(User user){
		System.out.println("设置名称:"+user.getName());
		System.out.println("坐标点:"+user.getPoint().toString());
		return "user/list";
	}
}

二.四 重启服务器,进行访问测试。

前端输入值,点击提交后,页面可以正确的跳转到 list.jsp 页面。

有图片。

控制台打印输出

有图片。

可以发现,利用 PropertyEditor 可以进行正常的类型转换。

但在 SpringMVC 开发中,一般使用 Convert 接口进行,而不用 PropertyEditor. SpringMVC 由于历史原因,两者方式都支持,所以讲解一下 PropertyEditor 的用法。

三. SpringMVC 的 类型转换接口。

三.一 SpringMVC 关于类型转换,共提供了三个接口。

1 . org.springframework.core.convert.converter.Converter<S, T>

package org.springframework.core.convert.converter;

public abstract interface Converter<S, T>
{
  public abstract T convert(S paramS);
}

2 . org.springframework.core.convert.converter.ConverterFactory<S, R>

package org.springframework.core.convert.converter;

public abstract interface ConverterFactory<S, R>
{
  public abstract <T extends R> Converter<S, T> getConverter(Class<T> paramClass);
}

3 . org.springframework.core.convert.converter.GenericConverter

定义了两个方法:

  public abstract Set<ConvertiblePair> getConvertibleTypes();  
  public abstract Object convert(Object paramObject, TypeDescriptor paramTypeDescriptor1, TypeDescriptor paramTypeDescriptor2);

这三个接口均在同一个包下。

有图片。

有一个support 包。 看看,支持哪些类型转换。

有图片。

这三个接口之间,有一些区别。

1 .Converter, 最简单的接口, 只是负责将一个 S 源类型 转换成 T 目标类型。

2 .ConverterFactory, 将一种类型的对象转换成另外一种类型及其子类型的对象。 比如,将String 转换成Number 以及 Number 的子类 Integer 和Double,需要定义一系列的转换器,就需要 将String 转换成 Integer的 StringToInteger 和String 转换成Double的 StringToDouble . 如果有一个Float, 那么还需要再写 一个StringToFloat.

SpringMVC 中提供了这么一个例子,可以看一下 StringToNumberConverterFactory

 final class StringToNumberConverterFactory
   implements ConverterFactory<String, Number>
{
  public <T extends Number> Converter<String, T> getConverter(Class<T> targetType)
   {
     return new StringToNumber(targetType);
   }
  
  private static final class StringToNumber<T extends Number> implements Converter<String, T>
   {
     private final Class<T> targetType;
     
     public StringToNumber(Class<T> targetType) {
       this.targetType = targetType;
    }
    
    public T convert(String source)
     {
       if (source.length() == 0) {
        return null;
       }
       return NumberUtils.parseNumber(source, targetType);
     }
   }
 }

如果想转换成 Integer, 可以 用 getConverter(Integer.class).convert(string字符串),
如果想转换成 Double, 可以用 getConverter(Double.class).convert(string字符串),
如果想转换成 Float, 可以用 getConverter(Float.class).convert(string字符串)

S 为源类型, R 为目标类型的基类, T 为目标类型基类的子类,也就是最终要转换的那个类。

这样的Factory 还有:

StringToEnumConverterFactory
CharacterToNumberFactory
NumberToNumberConverterFactory

可以方便的对有子类的 对象进行转换。

3 . GenericConverter Converter 只是将源类型转换成目标类型,并没有携带源类型和目标类型的信息,只能用于普通的转换,无法进行复杂的转换。 会根据源类型和目标类型对象的上下文信息进行类型转换。

GenericConverter接口中一共定义了两个方法,getConvertibleTypes()和convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)。getConvertibleTypes方法用于返回这个GenericConverter能够转换的原类型和目标类型的这么一个组合;convert方法则是用于进行类型转换的,我们可以在这个方法里面实现我们自己的转换逻辑。之所以说GenericConverter是最复杂的是因为它的转换方法convert的参数类型TypeDescriptor是比较复杂的。TypeDescriptor对类型Type进行了一些封装,包括value、Field及其对应的真实类型等等。

一般用 Convert 就可以达到基本的要求了。

三.二 ConversionService 接口

为了方便的对上面的三个接口进行进行统一调用,所以定义了 ConversionService 接口。

package org.springframework.core.convert;

public abstract interface ConversionService
{
	//能否转换
  public abstract boolean canConvert(Class<?> paramClass1, Class<?> paramClass2);
  
  public abstract boolean canConvert(TypeDescriptor paramTypeDescriptor1, TypeDescriptor paramTypeDescriptor2);
  //转换
  public abstract <T> T convert(Object paramObject, Class<T> paramClass);
  
  public abstract Object convert(Object paramObject, TypeDescriptor paramTypeDescriptor1, TypeDescriptor paramTypeDescriptor2);
}

该接口的实现类

有图片

故,类型转换时一般用 DefaultConversionService (默认注入到 mvc:annotation-driven 中)
类型格式化时用 DefaultFormattingConversionService.

三.三 ConverterRegistry 转换器注册接口

package org.springframework.core.convert.converter;

public abstract interface ConverterRegistry
{
  //添加 Converter 接口
  public abstract void addConverter(Converter<?, ?> paramConverter);
 
  public abstract void addConverter(Class<?> paramClass1, Class<?> paramClass2, Converter<?, ?> paramConverter);
   // 添加 GenericConverter 接口
  public abstract void addConverter(GenericConverter paramGenericConverter);
  // 添加 ConverterFactory 接口
  public abstract void addConverterFactory(ConverterFactory<?, ?> paramConverterFactory);
  
//移除转换器
  public abstract void removeConvertible(Class<?> paramClass1, Class<?> paramClass2);
}

用于 注册和移除转换器。

这儿用简单的 Convert 接口来进行讲解。

四. Convert 接口的使用

原始的错误类型转换。

四.一 前端代码

<body>
	<h2>两个蝴蝶飞,类型转换器使用</h2>
	<form:form commandName="user" action="login.action" method="post">
		<form:label path="name">姓名:</form:label>
		<form:input path="name"/><br/>
		
		<form:label path="birthday">日期:</form:label>
		<form:input path="birthday"/><br/>
		
		<form:button>提交</form:button>
	</form:form>
</body>

四.二 后端代码实现

@Controller
@RequestMapping(value="/user")
public class UserAction {
	//转到登录的页面
	@RequestMapping(value="toLogin")
	public String toLogin(Model model){
		model.addAttribute("user",new User());
		return "user/login";
	}
	//绑定到user对象。
	@RequestMapping(value="login") 
	public String login(User user){
		System.out.println("设置名称:"+user.getName());
		System.out.println("生日:"+user.getBirthday().toLocaleString());
		return "user/list";
	}
}

四.三 前端输入值,进行测试

有图片。

输入数值之后 ,点击提交,发生了错误。

有图片。

请求不通过,无法进行转换的原因。 也就是,没有默认提交 String 到Date 的转换。

进行重新构造,添加类型转换器,来达到StringToDate 的转换。

四.四 编写类型转换器 DateConvert

为了方便,将格式 pattern 改成注入的形式。

package com.yjl.convert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;

public class DateConvert implements Converter<String,Date>{
	// 自定义转换格式
	private String pattern;
	
	
	@Override
	public Date convert(String dateStr) {
		SimpleDateFormat sdf=new SimpleDateFormat(this.pattern);
		try {
			return sdf.parse(dateStr);
		} catch (ParseException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
			System.out.println(dateStr+"日期转换失败");
			return null;
		}
	}
	public String getPattern() {
		return pattern;
	}
	public void setPattern(String pattern) {
		this.pattern = pattern;
	}
	
}

四.五 在springmvc.xml 配置文件中 配置类型转换

将:

<!--提供了内置的转换器-->
<mvc:annotation-driven></mvc:annotation-driven>

改成 新写的转换器。

	<!-- 添加转换器 -->
   <mvc:annotation-driven conversion-service="conversionService">
    </mvc:annotation-driven>
    
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    	<property name="converters">
    		<list>
    			<bean class="com.yjl.convert.DateConvert">
    				<property name="pattern" value="yyyy-MM-dd"></property>
    			</bean>
    		</list>
    	</property>
    </bean>

四.六 重启服务器,再次进行验证。

输入上面相同的值,再次提交,页面可以进行跳转。

控制台打印输出:

有图片。

说明,新的日期转换器是成功的。

四.七 注解型日期转换器 @DateTimeFormat

日期转换,是非常常用的转换器。 SpringMVC 虽然没有提供,但是却提供了注解的形式。 @DateTimeFormat

JDK1.8 之前,不包括JDK1.8, 需要添加 joda-time.jar 包, JDK1.8包括,之后,不需要这个jar包,可直接使用@DateTimeFormat注解。

老蝴蝶版本是 1.8,不需要添加额外的jar包。

1 . User.java 中 在birthday 属性上面添加注解,写明格式。

 //定义一个日期类
    @DateTimeFormat(pattern="yyyy年MM月dd日")
    private Date birthday;

2 .将springmvc.xml 配置文件仍然换成以前的样式,注释掉 四.五 中的内容。
重新换成:

<mvc:annotation-driven></mvc:annotation-driven>
  •  

3 . 重启服务器,进行验证。

前端输入值之后 ,页面可以正常的跳转。

有图片。

控制台打印输出

有图片。

注解 @DateTimeFormat 是可以使用的。

@DateTimeFormat 代码为:

支持 DATE,TIME,DATE_TIME,NONE 等多种形式。

 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.ANNOTATION_TYPE})
 public @interface DateTimeFormat
 {

  ISO iso() default ISO.NONE;
   
  String pattern() default "";
   
  public static enum ISO
   {
    DATE, 
   TIME, 
 
     DATE_TIME, 

     NONE;
     private ISO() {}
   }
}

四.八 原先的类型转换器可以正常使用吗?

用的是name, 接收的是 字符串类型, 现在添加一个age 字段,要转换成int,看后台 User 能否接收呢?

注意,此时 springmvc.xml 的配置文件,又换成了 四.五 中的配置,在User.java 中去掉了 @DateTimeFormat 注解。

1 . 前端页面

<form:form commandName="user" action="login.action" method="post">
		<form:label path="name">姓名:</form:label>
		<form:input path="name"/><br/>
		
		<form:label path="age">年龄:</form:label>
		<form:input path="age"/><br/>
		
		<form:label path="birthday">日期:</form:label>
		<form:input path="birthday"/><br/>
		
		
		<form:button>提交</form:button>
	</form:form>

2 .后端输出

@RequestMapping(value="login") 
	public String login(User user){
		System.out.println("设置名称:"+user.getName()+",年龄:"+user.getAge());
		System.out.println("生日:"+user.getBirthday().toLocaleString());
		return "user/list";
	}

3 .测试输入

在这里插入图片描述
页面可以正常的进行跳转,控制台打印输出

有图片。

可以发现,原先的类型转换器还是可以使用的。(不知道为什么)

五. Formatter 数据格式化

SpringMVC的 Convert 转换器,可以在源类型对象到目标类型对象之间进行转换,重点是在不同的类型之间转换,并不承担输入和输出的格式化的工作。 不一定是String 到其他目标类型之间转换,也不一定发生在输入层,而是哪一层都是可以的。

在login 方法中,调用 DateConvert 进行格式化,也是可以的。变成了一个工具类。

@RequestMapping(value="login") 
	public String login(User user){
		System.out.println("设置名称:"+user.getName()+",年龄:"+user.getAge());
		System.out.println("生日:"+user.getBirthday().toLocaleString());
		DateConvert dc=new DateConvert();
		dc.setPattern("yyyy-MM-dd");
		System.out.println("新生日:"+dc.convert("1994-02-07").toLocaleString());
		return "user/list";
	}

如果想专门的进行数据的格式化,在前端输入到后端接收,即Controller 层进行转换时, 可以使用Formatter 接口进行数据的格式化。 前端输入都是字符串类型,或者是字符串数组类型, 而Formatter 是专门在字符串与目标类型之间进行相应的转换。

关于Formatter 主要定义了以下几个接口。

有图片。

只需要了解 Formatter 接口即可。

package org.springframework.format;

public abstract interface Formatter<T>
  extends Printer<T>, Parser<T>
{

}

SpringMVC中提供的格式化有

有图片。

要注意,有两个注解。 一个是 DateFormatter, 一个是NumberFormatter, 即常用的日期格式化和数字格式化。

六. Formatter 格式化的使用

前端传入一个工资,格式为 ###,## 即千分位分隔的形式(为了方便,直接在前台输入。 实际上,应该是前台将数字转换成千分位显示,在传入到后台时再转换一下)。

六.一 前台验证代码

<body>
	<h2>两个蝴蝶飞,类型转换器使用</h2>
	<form:form commandName="user" action="login.action" method="post">
		<form:label path="name">姓名:</form:label>
		<form:input path="name"/><br/>
		
		<form:label path="salary">工资:</form:label>
		<form:input path="salary"/><br/>
		
		<form:button>提交</form:button>
	</form:form>
</body>

六.二 编写金额格式化代码

package com.yjl.formatter;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Locale;

import org.springframework.format.Formatter;

public class MoneyFormatter implements Formatter<Double>{
	//定义格式
	private String pattern;
	@Override
	public String print(Double paramT, Locale paramLocale) {
		DecimalFormat nf=new DecimalFormat(this.pattern);
		return nf.format(paramT);
	}

	@Override
	public Double parse(String paramString, Locale paramLocale) throws ParseException {
		DecimalFormat nf=new DecimalFormat(this.pattern);
		return (Double) nf.parse(paramString);
	}

	public String getPattern() {
		return pattern;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}
	
}

六.三 springmvc.xml 中 更改格式转换

所使用的 Bean 是 FormattingConversionServiceFactoryBean, 里面有一个 formatters 的属性。

<!-- 添加formatter 转换器 -->
  <mvc:annotation-driven conversion-service="conversionService">
    </mvc:annotation-driven>
    
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    	<property name="formatters">
    		<list>
    			<bean class="com.yjl.formatter.MoneyFormatter">
    				<property name="pattern" value="###,###,###.##"></property>
    			</bean>
    		</list>
    	</property>
    </bean>

六.四 重启服务器,进行验证。

前台输入:

有图片。

控制台打印输出:

有图片。

可知,可以正确的进行相应的转换。

六.五 像 convert 一样,放置在方法中。

//绑定到user对象。
	@RequestMapping(value="login") 
	public String login(User user){
		System.out.println("名称:"+user.getName());
		System.out.println("工资:"+user.getSalary());
		
		MoneyFormatter mf=new MoneyFormatter();
		mf.setPattern("###,###,##");
		try {
			Double salary=mf.parse("1,234.34", Locale.CHINA);
			System.out.println("输出值:"+salary);
		} catch (ParseException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return "user/list";
	}

这样,也是正确的,可以正确的进行转换。 仿佛成了一个工具类。

SpringMVC 提供了三种数字格式化的接口。

NumberFormatter    普通数字格式化

CurrencyFormatter    货币类型的格式化

PercentFormatter     百分比类型的格式化

现在,已经被废弃了。

当然,也可以用注册器的形式进行注入格式化。

六.六 注册器的形式注入 金额的格式化

1 . 保留前面的 MoneyFormatter ,用来注入。

2 . 写注册器 MoneyFormatterRegister

package com.yjl.formatter;

import org.springframework.format.FormatterRegistrar;
import org.springframework.format.FormatterRegistry;

public class MoneyFormatterRegister implements FormatterRegistrar{
	
	//注入格式化类
	private MoneyFormatter mf;
	public MoneyFormatter getMf() {
		return mf;
	}
	public void setMf(MoneyFormatter mf) {
		this.mf = mf;
	}

	@Override
	public void registerFormatters(FormatterRegistry paramFormatterRegistry) {
		//添加 格式化类
		paramFormatterRegistry.addFormatter(mf);
	}

}

3 . 重写 springmvc.xml 的格式化配置

采用的属性是 formatterRegistrars

	<!-- 添加formatter 转换器 -->
  <mvc:annotation-driven conversion-service="conversionService">
    </mvc:annotation-driven>
    
   <bean id="moneyFormatter" class="com.yjl.formatter.MoneyFormatter">
    				<property name="pattern" value="###,###,###.##"></property>
    </bean>
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    	<property name="formatterRegistrars">
    		<list>
    			<bean class="com.yjl.formatter.MoneyFormatterRegister">
    				<property name="mf" ref="moneyFormatter"></property>
    			</bean>
    		</list>
    	</property>
    </bean>

4 . 重启服务器,进行验证

前端输入值

有图片。

后端控制台打印输出 (后端代码,去除了六.五 的部分。)

有图片。

六.六 @NumberFormatter 注解 (推荐使用)

像使用 @DateTimeFormatter 那样,使用 @NumberFormatter 注解。

1 .User 中salary 属性上面添加注解

 @NumberFormat(pattern="###,###,###.##")
    private double salary;

2 . springmvc.xml 中去除原先的转换器,变成最原来的模样。

<mvc:annotation-driven></mvc:annotation-driven>
  •  

3 .重启服务器,进行验证

前端输入

控制台打印

有图片。

@NumberFormatter 注解代码为

支持的样式 为: DEFAULT,NUMBER,PERCENT,CURRENCY 等样式。

@Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.ANNOTATION_TYPE})
 public @interface NumberFormat
 {
   Style style() default Style.DEFAULT;
   
  String pattern() default "";
   
  public static enum Style
  {
     DEFAULT, 
    



     NUMBER, 
    


    PERCENT, 

    CURRENCY;
    
     private Style() {}
   }
}
  •  

标签:格式化,String,java,SpringMVC,return,user,转换器,public
来源: https://blog.csdn.net/u012045045/article/details/111039937

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有