国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

Java Spring項(xiàng)目國際化(i18n)詳細(xì)方法與實(shí)例

瀏覽:4日期:2022-09-04 09:44:00
Spring國際化概述國際化基本規(guī)則

國際化信息”也稱為“本地化信息”,一般需要兩個(gè)條件才可以確定一個(gè)特定類型的本地化信息,它們分別是“語言類型”和“國家/地區(qū)的類型”。如中文本地化信息既有中國大陸地區(qū)的中文,又有中國臺(tái)灣、中國香港地區(qū)的中文,還有新加坡地區(qū)的中文。Java通過java.util.Locale類表示一個(gè)本地化對(duì)象,它允許通過語言參數(shù)和國家/地區(qū)參數(shù)創(chuàng)建一個(gè)確定的本地化對(duì)象。

語言參數(shù)使用ISO標(biāo)準(zhǔn)語言代碼表示,這些代碼是由ISO-639標(biāo)準(zhǔn)定義的,每一種語言由兩個(gè)小寫字母表示。在許多網(wǎng)站上都可以找到這些代碼的完整列表,下面的網(wǎng)址是提供了標(biāo)準(zhǔn)語言代碼的信息:http://www.loc.gov/standards/iso639-2/php/English_list.php。

國家/地區(qū)參數(shù)也由標(biāo)準(zhǔn)的ISO國家/地區(qū)代碼表示,這些代碼是由ISO-3166標(biāo)準(zhǔn)定義的,每個(gè)國家/地區(qū)由兩個(gè)大寫字母表示。用戶可以從以下網(wǎng)址查看ISO-3166的標(biāo)準(zhǔn)代碼:http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html,部分語言和國家/地區(qū)的標(biāo)準(zhǔn)代碼如下所示:

語言 簡(jiǎn)稱 簡(jiǎn)體中文(中國) zh_CN 繁體中文(中國臺(tái)灣) zh_TW 繁體中文(中國香港) zh_HK 英語(中國香港) en_HK 英語(美國) en_US 英語(英國) en_GB 英語(全球) en_WW 英語(加拿大) en_CA 英語(澳大利亞) en_AU 英語(愛爾蘭) en_IE 英語(芬蘭) en_FI 芬蘭語(芬蘭) fi_FI 英語(丹麥) en_DK 丹麥語(丹麥) da_DK 英語(以色列) en_IL 希伯來語(以色列) he_IL 英語(南非) en_ZA 英語(印度) en_IN 英語(挪威) en_NO 英語(新加坡) en_SG 英語(新西蘭) en_NZ 英語(印度尼西亞) en_ID 英語(菲律賓) en_PH 英語(泰國) en_TH 英語(馬來西亞) en_MY 英語(阿拉伯) en_XA 韓文(韓國) ko_KR 日語(日本) ja_JP 荷蘭語(荷蘭) nl_NL 荷蘭語(比利時(shí)) nl_BE 葡萄牙語(葡萄牙) pt_PT 葡萄牙語(巴西) pt_BR 法語(法國) fr_FR 法語(盧森堡) fr_LU 法語(瑞士) fr_CH 法語(比利時(shí)) fr_BE 法語(加拿大) fr_CA 西班牙語(拉丁美洲) es_LA 西班牙語(西班牙) es_ES 西班牙語(阿根廷) es_AR 西班牙語(美國) es_US 西班牙語(墨西哥) es_MX 西班牙語(哥倫比亞) es_CO 西班牙語(波多黎各) es_PR 德語(德國) de_DE 德語(奧地利) de_AT 德語(瑞士) de_CH 俄語(俄羅斯) ru_RU 意大利語(意大利) it_IT 希臘語(希臘) el_GR 挪威語(挪威) no_NO 匈牙利語(匈牙利) hu_HU 土耳其語(土耳其) tr_TR 捷克語(捷克共和國) cs_CZ 斯洛文尼亞語 sl_SL 波蘭語(波蘭) pl_PL 瑞典語(瑞典) sv_SE 西班牙語(智利) es_CL 語言類型判斷1)基于瀏覽器語言

根據(jù)Request Headers中的Accept-language來判斷。

2)基于客戶端傳參

要求客戶端第一次(或者每次)傳遞的自定義參數(shù)值來判斷,如規(guī)定傳locale,值為zh-cn、en-us等內(nèi)容,如果只在第一次傳入則local以及timeZone先關(guān)信息要存入session或者cookie中,后面的請(qǐng)求語言方式則直接從兩者中取,其有效時(shí)間與session和cookie設(shè)置的生命周期關(guān)聯(lián)。

3)基于默認(rèn)配置

當(dāng)獲取語言類型時(shí)沒有找到對(duì)應(yīng)類型時(shí),會(huì)使用默認(rèn)的語言類型。

語言類型保存

<!-- 定義本地化變更攔截器 --><bean /><!-- 定義注解URL映射處理器,所有的請(qǐng)求映射關(guān)聯(lián)本地化攔截器,或者也可自定義該攔截器路徑映射--><bean class='org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping'> <property name='interceptors' ref=' localeChangeInterceptor ' /> <property name='order' value='1'></property></bean>基于url

該種方式需要每次都在請(qǐng)求的url上帶上local參數(shù),指定該次需要的語言類型,并且該方式的local解析器需要配置,如下:

<a href='http://www.cgvv.com.cn/bcjs/xxx.do?locale=zh_CN' rel='external nofollow' >中文</a>或<a href='http://www.cgvv.com.cn/bcjs/xxx.do?locale=en' rel='external nofollow' >英文</a><bean />

但在該配置下使用會(huì)拋Cannot change HTTP accept header - use a different locale resolution strategy異常,這是因?yàn)閟pring source做了限制,無法對(duì)本地的local賦值修改,解決辦法如下,新建一個(gè)類MyLocaleResolver繼承AcceptHeaderLocaleResolver,重寫resolveLocale和setLocale方法,并將上面的localeResolver的class指向如下MyLocaleResolver類:

public class MyLocaleResolver extends AcceptHeaderLocaleResolver { private Locale myLocal; public Locale resolveLocale(HttpServletRequest request) { return myLocal == null ? request.getLocale() : myLocal;} public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { myLocal = locale; }}基于session

基于session的狀態(tài)保存方式只需要在第一次請(qǐng)求的時(shí)候指定語言類型,localResolver會(huì)將該屬性保存到session中,后面的請(qǐng)求直接從session中獲取該語言類型,該種方式的localResolver對(duì)應(yīng)的類為SessionLocaleResolver,如下配置:

<bean />基于cookie

與session的機(jī)制類似,差異在于兩者的存儲(chǔ)和周期,鑒于安全、大小以及體驗(yàn)等因素的影響,實(shí)際使用中使用者更傾向于前者,該種cookie保存方式的localResolver為

<bean />文案數(shù)據(jù)來源

對(duì)于語言類型的資源文件,需要開發(fā)者對(duì)文案進(jìn)行搜集整理,并翻譯成相應(yīng)的語言確定關(guān)鍵字key,目前大多數(shù)情況是將這些信息置于.properties文件中,在使用的時(shí)候直接訪問獲取,當(dāng)然也可置于數(shù)據(jù)庫中,但頻繁的文案獲取會(huì)影響服務(wù)器性能及產(chǎn)品體驗(yàn),可結(jié)合數(shù)據(jù)字典以及緩存工具使用。

數(shù)據(jù)庫1)spring 配置方式

<!-- 默認(rèn)的注解映射的支持 --><mvc:annotation-driven validator='validator' conversion-service='conversionService' /><!-- 資源文件 --><bean class='org.springframework.context.support.ResourceBundleMessageSource'> <property name='basenames'> <list> <value>resource</value> <value>validation</value> </list> </property></bean> <bean class='com.obs2.util.MessageResource'> <property name='parentMessageSource' ref='propertiesMessageSource'/></bean> <bean class='com.obs2.util.MessageResourceInterpolator'> <property name='messageResource' ref='databaseMessageSource'/></bean> <!-- 驗(yàn)證器 --><bean class='org.springframework.validation.beanvalidation.LocalValidatorFactoryBean'> <property name='messageInterpolator' ref='messageInterpolator'/></bean>

這里定義了一個(gè)propertiesMessageSource,一個(gè)databaseMessageSourcer,和一個(gè)messageInterpolator。propertiesMessageSource用于讀取properties文件databaseMessageSourcer用于讀取數(shù)據(jù)庫的數(shù)據(jù)配置,其中,有一個(gè)屬性設(shè)置它的父MessageSource為propertiesMessageSource。意思是如果數(shù)據(jù)庫找不到對(duì)應(yīng)的數(shù)據(jù),到properties文件當(dāng)中查找。messageInterpolator是個(gè)攔截器。

2)數(shù)據(jù)庫的POJO定義

@Entity@SuppressWarnings('serial')@Table(name='resource') public class Resource implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name='resource_id') private long resourceId; @Column(name='name', length=50, nullable=false) private String name; @Column(name='text', length=1000, nullable=false) private String text; @Column(name='language', length=5, nullable=false) private String language; public long getResourceId() { return resourceId; } public void setResourceId(long resourceId) { this.resourceId = resourceId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getText() { return text; } public void setText(String text) { this.text = text;} public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; }}

定義了一張表[resource],字段有:[resource_id]、[name]、[text]、[language]。

3)讀取數(shù)據(jù)庫的MessageResource類

/*** 取得資源數(shù)據(jù)* @author Robin*/public class MessageResource extends AbstractMessageSource implements ResourceLoaderAware { @SuppressWarnings('unused') private ResourceLoader resourceLoader; @Resource private ResourceService resourceService; /** * Map切分字符 */ protected final String MAP_SPLIT_CODE = '|'; protected final String DB_SPLIT_CODE = '_'; private final Map<String, String> properties = new HashMap<String, String>(); public MessageResource() { reload(); } public void reload() { properties.clear(); properties.putAll(loadTexts()); } protected Map<String, String> loadTexts() { Map<String, String> mapResource = new HashMap<String, String>(); List<com.obs2.service.bean.Resource> resources = resourceService.findAll(); for (com.obs2.service.bean.Resource item : resources) { String code = item.getName() + MAP_SPLIT_CODE + item.getLanguage(); mapResource.put(code, item.getText()); } return mapResource; } private String getText(String code, Locale locale) { String localeCode = locale.getLanguage() + DB_SPLIT_CODE + locale.getCountry(); String key = code + MAP_SPLIT_CODE + localeCode; String localeText = properties.get(key); String resourceText = code; if(localeText != null) { resourceText = localeText; }else { localeCode = Locale.ENGLISH.getLanguage(); key = code + MAP_SPLIT_CODE + localeCode; localeText = properties.get(key); if(localeText != null) { resourceText = localeText; }else { try { if(getParentMessageSource() != null) { resourceText = getParentMessageSource().getMessage(code, null, locale); } } catch (Exception e) { logger.error('Cannot find message with code: ' + code); } } } return resourceText; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader()); } @Override protected MessageFormat resolveCode(String code, Locale locale) { String msg = getText(code, locale); MessageFormat result = createMessageFormat(msg, locale); return result; } @Override protected String resolveCodeWithoutArguments(String code, Locale locale) { String result = getText(code, locale); return result; }}

主要是重載AbstractMessageSource和ResourceLoaderAware,以實(shí)現(xiàn)Spring MVC的MessageSource國際化調(diào)用。類中的reload()方法,我把它寫到了一個(gè)ServletListener當(dāng)中,讓項(xiàng)目啟動(dòng)時(shí),自動(dòng)加載數(shù)據(jù)到static的map中。

4)Listener

/*** 系統(tǒng)啟動(dòng)監(jiān)聽* @author Robin*/public class SystemListener implements ServletContextListener {/*** context初始化時(shí)激發(fā)*/@Overridepublic void contextInitialized(ServletContextEvent e) {// 取得ServletContextServletContext context = e.getServletContext();WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context);// 設(shè)置國際化多語言MessageResource messageSource = applicationContext.getBean(MessageResource.class); messageSource.reload();} /*** context刪除時(shí)激發(fā)*/@Overridepublic void contextDestroyed(ServletContextEvent e) {} /*** 創(chuàng)建一個(gè) session時(shí)激發(fā)* @param e*/ public void sessionCreated(HttpSessionEvent e) {} /*** 當(dāng)一個(gè) session失效時(shí)激發(fā)* @param e*public void sessionDestroyed(HttpSessionEvent e) {}/*** 設(shè)置 context的屬性,它將激發(fā)attributeReplaced或attributeAdded方法* @param e*/public void setContext(HttpSessionEvent e) {} /*** 增加一個(gè)新的屬性時(shí)激發(fā)* @param e*/public void attributeAdded(ServletContextAttributeEvent e) {} /***刪除一個(gè)新的屬性時(shí)激發(fā)* @param e*/ public void attributeRemoved(ServletContextAttributeEvent e) { } /** 屬性被替代時(shí)激發(fā)* @param e*/public void attributeReplaced(ServletContextAttributeEvent e) {}}

該Listener需要加入到web.xml當(dāng)中:

<!-- 系統(tǒng)啟動(dòng)監(jiān)聽 --><listener> <listener-class>com.obs2.util.SystemListener</listener-class></listener>5)Interceptor攔截器

/*** 攔截Annotation驗(yàn)證信息* @author Robin**/ public class MessageResourceInterpolator implements MessageInterpolator {@Resourceprivate MessageResource messageResource; public void setMessageResource(MessageResource messageResource) { this.messageResource = messageResource;} @Overridepublic String interpolate(String messageTemplate, Context context) { String messageTemp = null; if(messageTemplate.startsWith('{') && messageTemplate.endsWith('}')) { messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1); }else { return messageTemplate; } String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get('params'); MessageBuilder builder = new MessageBuilder().code(messageTemp); if (params != null) { for (String param : params) { builder = builder.arg(param); } } String result = builder.build().resolveMessage(messageResource, Locale.ENGLISH).getText(); return result; } @Overridepublic String interpolate(String messageTemplate, Context context, Locale locale) { String messageTemp = null; if(messageTemplate.startsWith('{') && messageTemplate.endsWith('}')) { messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1); }else { return messageTemplate; } String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get('params'); MessageBuilder builder = new MessageBuilder().code(messageTemp); if (params != null) { builder = builder.args(params); } String result = builder.build().resolveMessage(messageResource, locale).getText(); return result } }靜態(tài)資源

<!-- 資源文件綁定器,文件名稱:messages.properties(沒有找到時(shí)的默認(rèn)文件), messages_en.properties(英文),messages_zh_CN.properties(中文),等等--><bean class='org.springframework.context.support.ResourceBundleMessageSource'> <property name='basename' value='config.messages.messages' /> <property name='defaultEncoding' value='UTF-8'/> <property name='basename' value='i18n.messages'/> <property name='useCodeAsDefaultMessage' value='true' /></bean>文案獲取資源獲取接口MessageSource詳解

Spring定義了訪問國際化信息的MessageSource接口,并提供了幾個(gè)易用的實(shí)現(xiàn)類。首先來了解一下該接口的幾個(gè)重要方法:

1)String getMessage(String code, Object[] args, String defaultMessage, Locale locale) code

表示國際化資源中的屬性名;args用于傳遞格式化串占位符所用的運(yùn)行期參數(shù);當(dāng)在資源找不到對(duì)應(yīng)屬性名時(shí),返回defaultMessage參數(shù)所指定的默認(rèn)信息;locale表示本地化對(duì)象;

2)String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException

與上面的方法類似,只不過在找不到資源中對(duì)應(yīng)的屬性名時(shí),直接拋出NoSuchMessageException異常;

3)String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException

MessageSourceResolvable 將屬性名、參數(shù)數(shù)組以及默認(rèn)信息封裝起來,它的功能和第一個(gè)接口方法相同。

MessageSource類結(jié)構(gòu)

MessageSource分別被HierarchicalMessageSource和ApplicationContext接口擴(kuò)展,這里我們主要看一下HierarchicalMessageSource接口的幾個(gè)實(shí)現(xiàn)類

Java Spring項(xiàng)目國際化(i18n)詳細(xì)方法與實(shí)例

HierarchicalMessageSource接口添加了兩個(gè)方法,建立父子層級(jí)的MessageSource結(jié)構(gòu),類似于前面我們所介紹的HierarchicalBeanFactory。該接口的setParentMessageSource (MessageSource parent)方法用于設(shè)置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。

HierarchicalMessageSource接口最重要的兩個(gè)實(shí)現(xiàn)類是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它們基于Java的ResourceBundle基礎(chǔ)類實(shí)現(xiàn),允許僅通過資源名加載國際化資源。ReloadableResourceBundleMessageSource提供了定時(shí)刷新功能,允許在不重啟系統(tǒng)的情況下,更新資源的信息。StaticMessageSource主要用于程序測(cè)試,它允許通過編程的方式提供國際化信息。而DelegatingMessageSource是為方便操作父MessageSource而提供的代理類。

ResourceBundleMessageSource與ReloadableResourceBundleMessageSource對(duì)比1)通過ResourceBundleMessageSource配置資源

<bean id=' messageSource 'class='org.springframework.context.support.ResourceBundleMessageSource'> <!--①通過基名指定資源,相對(duì)于類根路徑--> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> </bean>2)通過ReloadableResourceBundleMessageSource配置資源

<bean id='messageSource 'class='org.springframework.context.support.ReloadableResourceBundleMessageSource'> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property> <!--①刷新資源文件的周期,以秒為單位--> <property name='cacheSeconds' value='5'/></bean>3)對(duì)比

兩者都是利用資源名通過getMessage()接口就可以加載整套的國際化資源文件,唯一區(qū)別在于ReloadableResourceBundleMessageSource可以定時(shí)刷新資源文件,以便在應(yīng)用程序不重啟的情況下感知資源文件的變化。很多生產(chǎn)系統(tǒng)都需要長(zhǎng)時(shí)間持續(xù)運(yùn)行,系統(tǒng)重啟會(huì)給運(yùn)行帶來很大的負(fù)面影響,這時(shí)通過該實(shí)現(xiàn)類就可以解決國際化信息更新的問題。上面的配置中cacheSeconds屬性讓ReloadableResourceBundleMessageSource每5秒鐘刷新一次資源文件(在真實(shí)的應(yīng)用中,刷新周期不能太短,否則頻繁的刷新將帶來性能上的負(fù)面影響,一般不建議小于30分鐘)。cacheSeconds默認(rèn)值為-1表示永不刷新,此時(shí),該實(shí)現(xiàn)類的功能就蛻化為ResourceBundleMessageSource的功能。

頁面獲取文案利用Spring標(biāo)簽獲取

引入標(biāo)簽庫:

<%@ taglib prefix='spring' uri='http://www.springframework.org/tags' %>

獲取文案:

<s:message code='test.app'/><spring:message code='main.title' />利用JSTL標(biāo)簽獲取

引入標(biāo)簽庫:

<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core_rt'%>

獲取文案:

<fmt:message key='test.app'/>Java代碼中獲取文案利用MessageSource接口獲取1)自動(dòng)注入

@Autowiredprivate MessageSource messageSource;String s = messageSource.getMessage('SystemError', new Object[]{}, Locale.US);2)手動(dòng)bean獲取

a. 獲取容器

容器已經(jīng)初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器沒有初始化:

String[] configs = {'com/baobaotao/i18n/beans.xml'};ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 獲取bean跟文案

MessageSource ms = (MessageSource) wac.getBean('myResource');Object[] params = {'John', new GregorianCalendar().getTime()};String str1 = ms.getMessage('greeting.common',params,Locale.US);利用Spring容器獲取

在前面的MessageSource類圖結(jié)構(gòu)中我們發(fā)現(xiàn)ApplicationContext實(shí)現(xiàn)了MessageSource的接口,也就是說ApplicationContext的實(shí)現(xiàn)類本身也是一個(gè)MessageSource對(duì)象。

將ApplicationContext和MessageSource整合起來, Spring此處的設(shè)計(jì)人為:在一般情況下,國際化信息資源應(yīng)該是容器級(jí)。我們一般不會(huì)將MessageSource作為一個(gè)Bean注入到其他的Bean中,相反MessageSource作為容器的基礎(chǔ)設(shè)施向容器中所有的Bean開放。只要我們考察一下國際化信息的實(shí)際消費(fèi)場(chǎng)所就更能理解Spring這一設(shè)計(jì)的用意了。國際化信息一般在系統(tǒng)輸出信息時(shí)使用,如Spring MVC的頁面標(biāo)簽,控制器Controller等,不同的模塊都可能通過這些組件訪問國際化信息,因此Spring就將國際化消息作為容器的公共基礎(chǔ)設(shè)施對(duì)所有組件開放。

既然一般情況下我們不會(huì)直接通過引用MessageSource Bean使用國際信息,那如何聲明容器級(jí)的國際化信息呢? Spring容器啟動(dòng)過程時(shí),在初始化容器的時(shí)候通過initMessageSource()方法所執(zhí)行的工作就是初始化容器中的國際化信息資源,它根據(jù)反射機(jī)制從BeanDefinitionRegistry中找出名稱為“messageSource”且類型為org.springframework.context.MessageSource的Bean,將這個(gè)Bean定義的信息資源加載為容器級(jí)的國際化信息資源。請(qǐng)看下面的配置:

<!--注冊(cè)資源Bean,其Bean名稱只能為messageSource --><bean class='org.springframework.context.support.ResourceBundleMessageSource'> <property name='basenames'> <list> <value>com/baobaotao/i18n/fmt_resource</value> </list> </property></bean>然后通過ApplicationContext直接訪問國際化信息:

a. 獲取容器

容器已經(jīng)初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器沒有初始化:

String[] configs = {'com/baobaotao/i18n/beans.xml'};ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 獲取bean跟文案

Object[] params = {'John', new GregorianCalendar().getTime()};String str1 = ctx.getMessage('greeting.common',params,Locale.US);注意事項(xiàng)1)編碼問題

a. 改變properties文件編碼為UTF-8/GBK,然而ResourceBundleMessageSource的默認(rèn)編碼defaultEncoding是ISO-8859-1,需要在xml中增加一個(gè)相應(yīng)屬性將其改變?yōu)槟阈枰腢TF-8/GBK之類。

b. 如果資源文件想統(tǒng)一使用ISO-8859-1格式,可以將原本用UTF-8寫好的中文資源文件使用jdk自帶的工具native2ascii將UTF-8文件和內(nèi)容轉(zhuǎn)為ISO-8859-1文件,其中的中文內(nèi)容會(huì)使用16進(jìn)制unicode編碼為u****格式:

cmd命令:

JAVA_HOMEbinnative2ascii -encoding UTF-8 messages_zh_CN.properties messages_zh_C1N.properties

本文主要講解了Java Spring項(xiàng)目國際化(i18n)詳細(xì)方法與實(shí)例,更多關(guān)于Java Spring項(xiàng)目國際化技巧請(qǐng)查看下面的相關(guān)鏈接

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 91精品国产免费 | 毛片天堂 | 成人做爰免费网站 | 久草勉费视频 | 国产精品黄网站免费观看 | 欧美中文在线 | 国产主播精品福利19禁vip | 亚洲国产精品日韩在线观看 | 大看蕉a在线观看 | 三级伦理网站 | 99久久伊人一区二区yy5o99 | 一级特黄牲大片免费视频 | 国产91精品高清一区二区三区 | 日本在线视频播放 | 韩日黄色 | 国产午夜精品理论片久久影视 | 泷泽萝拉亚洲精品中文字幕 | 92午夜国产福利视频1000 | 玖玖精品视频在线 | 日韩美女网站 | 日本特爽特黄特刺激大片 | 2021国产精品自拍 | 成人免费视频网 | 波多野结衣视频在线观看地址免费 | 美女wc | 老司机深夜影院入口aaaa | 免费乱淫视频 | 久久天天躁综合夜夜黑人鲁色 | 亚洲天堂色网站 | 色视频在线观看视频 | 久久日本精品一区二区免费 | 一男一女搞黄 | 日本特黄网站 | 成人国产一区 | 日本阿v精品视频在线观看 日本阿v视频在线观看高清 | 精品视频自拍 | 2017天天爽夜夜爽精品视频 | 亚洲欧美久久精品一区 | 亚洲精品国产成人99久久 | 日韩精品一区二区三区不卡 | 欧美日韩精品一区二区视频在线观看 |