Mybatis3.2不支持Ant通配符TypeAliasesPackage掃描的解決方案

業(yè)務(wù)場(chǎng)景

業(yè)務(wù)場(chǎng)景:首先項(xiàng)目進(jìn)行分布式拆分之后肝断,按照模塊再分為為api層和service層,web層祝蝠。
其中訂單業(yè)務(wù)的實(shí)體類放在com.muses.taoshop.item.entity,而用戶相關(guān)的實(shí)體類放在com.muses.taoshop.user.entity逞敷。所以就這樣,通過通配符方式去setTypeAliasesPackage 灌侣,com.muses.taoshop.*.entity

Ant通配符的3中風(fēng)格:
(1) ?:匹配文件名中的一個(gè)字符 eg: com/test/entity? 匹配 com/test/entityaa
(2) * : 匹配文件名中的任意字符 eg: com//entity 匹配 com/test/entity
(3) ** : 匹配文件名中的多重路徑 eg: com/
*/entity 匹配 com/test/test1/entity

mybatis配置類寫在common工程推捐,數(shù)據(jù)庫操作有些是可以共用的,不需要每個(gè)web工程都進(jìn)行重復(fù)配置侧啼。
所以寫了個(gè)Mybatis配置類:

package com.muses.taoshop.common.core.database.config;
public class BaseConfig {

    /**
     * 設(shè)置主數(shù)據(jù)源名稱
     */
    public static final String DATA_SOURCE_NAME = "shop";

    /**
     * 加載配置文件信息
     */
    public static final String DATA_SOURCE_PROPERTIES = "spring.datasource.shop";

    /**
     * repository 所在包
     */
    public static final String REPOSITORY_PACKAGES = "com.muses.taoshop.**.repository";

    /**
     * mapper 所在包
     */
    public static final String MAPPER_PACKAGES = "com.muses.taoshop.**.mapper";

    /**
     * 實(shí)體類 所在包
     */
    public static final String ENTITY_PACKAGES = "com.muses.taoshop.*.entity";
...

}

貼一下配置類代碼牛柒,主要關(guān)注: factoryBean.setTypeAliasesPackage(ENTITY_PACKAGES);之前的寫法是這樣的堪簿。ENTITY_PACKAGES是個(gè)常量:public static final String ENTITY_PACKAGES = "com.muses.taoshop.*.entity";,ps:注意了皮壁,這里用了通配符

package com.muses.taoshop.common.core.database.config;
//省略代碼
@MapperScan(
        basePackages = MAPPER_PACKAGES,
        annotationClass = MybatisRepository.class,
        sqlSessionFactoryRef = SQL_SESSION_FACTORY
)
@EnableTransactionManagement
@Configuration
public class MybatisConfig {
    //省略其它代碼椭更,主要看sqlSessionFactory配置
    @Primary
    @Bean(name = SQL_SESSION_FACTORY)
    public SqlSessionFactory sqlSessionFactory(@Qualifier(DATA_SOURCE_NAME)DataSource dataSource)throws Exception{
        //SpringBoot默認(rèn)使用DefaultVFS進(jìn)行掃描,但是沒有掃描到j(luò)ar里的實(shí)體類
        VFS.addImplClass(SpringBootVFS.class);
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        //factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try{
            factoryBean.setMapperLocations(resolver.getResources("classpath*:/mybatis/*Mapper.xml"));
            //String typeAliasesPackage = packageScanner.getTypeAliasesPackages();
            //設(shè)置一下TypeAliasesPackage
            factoryBean.setTypeAliasesPackage(ENTITY_PACKAGES);
            SqlSessionFactory sqlSessionFactory = factoryBean.getObject();
            return sqlSessionFactory;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

...

}

ps:原先做法:在sqlSessionFactory方法里進(jìn)行TypeAliasesPackage設(shè)置蛾魄,(讓Mybatis能夠掃描到實(shí)體類虑瀑,在xml文件里就不需要寫全實(shí)體類的全包名了。)factoryBean.setTypeAliasesPackage(ENTITY_PACKAGES);

但是運(yùn)行之后都會(huì)報(bào)錯(cuò)滴须,提示實(shí)體類不能掃描到舌狗,因?yàn)槲业膕ervice工程里都是這樣寫的,ResultType進(jìn)行用別名ItemCategory扔水,例子:

 <!-- 獲取所有的商品品類信息-->
    <select id="listCategory" resultType="ItemCategory">
        SELECT 
        <include refid="BaseColumnList" />
        FROM item_category t
    </select>

源碼簡(jiǎn)單分析

針對(duì)上面的業(yè)務(wù)場(chǎng)景痛侍,首先的分析一下,我們知道Mybatis的執(zhí)行都會(huì)通過SQLSessionFactory去調(diào)用魔市,調(diào)用前都是先用SqlSessionFactoryBean的setTypeAliasesPackage可以看一下SqlSessionFactoryBean的源碼:

/**
   * Packages to search for type aliases.
   *
   * @since 1.0.1
   *
   * @param typeAliasesPackage package to scan for domain objects
   *
   */
  public void setTypeAliasesPackage(String typeAliasesPackage) {
    this

我們看一下SqlSessionFactoryBean的初步Build方法:

/**
   * Build a {@code SqlSessionFactory} instance.
   *
   * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
   * {@code SqlSessionFactory} instance based on an Reader.
   * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
   *
   * @return SqlSessionFactory
   * @throws IOException if loading the config file failed
   */
  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;
//創(chuàng)建一個(gè)XMLConfigBuilder讀取配置文件的一些信息
    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      configuration = this.configuration;
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);//添加Properties屬性
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      }
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }

    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    if (this.vfs != null) {
      configuration.setVfsImpl(this.vfs);
    }
/*
重點(diǎn)看這里主届,其它源碼先不看,這里獲取到typeAliasesPackage字符串之后待德,調(diào)用tokenizeToStringArray進(jìn)行字符串分隔返回一個(gè)數(shù)組君丁,`String CONFIG_LOCATION_DELIMITERS = ",; \t\n";`
*/
    if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {//遍歷,注冊(cè)到configuration對(duì)象上
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
      }
    }
    ...
    //省略其它代碼
}

然后可以看到注冊(cè)所有別名的方法 磅网,registerAliases是怎么做的谈截?

configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);

要掃描注冊(cè)所有的別名之前先要掃描包下面的所有類

public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        resolverUtil.find(new IsA(superType), packageName);
        //通過ResolverUtil獲取到的所有類都賦值給一個(gè)集合
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        /*遍歷集合,然后一個(gè)個(gè)注冊(cè)*/
        Iterator var5 = typeSet.iterator();
        while(var5.hasNext()) {
            Class<?> type = (Class)var5.next();
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                this.registerAlias(type);
            }
        }

    }

ResolverUtil是怎么通過packageName去查找的呢涧偷,可以再繼續(xù)跟一下

 public ResolverUtil<T> find(ResolverUtil.Test test, String packageName) {
    //獲取包路徑
        String path = this.getPackagePath(packageName);

        try {
        //VFS類用單例模式實(shí)現(xiàn),先獲取集合
            List<String> children = VFS.getInstance().list(path);
            Iterator var5 = children.iterator();
       //遍歷,.class文件的選出來
            while(var5.hasNext()) {
                String child = (String)var5.next();
                if (child.endsWith(".class")) {
                    this.addIfMatching(test, child);
                }
            }
        } catch (IOException var7) {
            log.error("Could not read package: " + packageName, var7);
        }

        return this;
    }

獲取packPath只是獲取一下相對(duì)路徑

protected String getPackagePath(String packageName) {
        return packageName == null ? null : packageName.replace('.', '/');
    }

校驗(yàn)方法addIfMatching:

 protected void addIfMatching(ResolverUtil.Test test, String fqn) {
        try {
            String externalName = fqn.substring(0, fqn.indexOf(46)).replace('/', '.');
            ClassLoader loader = this.getClassLoader();//類加載器
            if (log.isDebugEnabled()) {
                log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
            }

            Class<?> type = loader.loadClass(externalName);//通過類加載器加載類
            if (test.matches(type)) {//校驗(yàn)是否符合
                this.matches.add(type);
            }
        } catch (Throwable var6) {
            log.warn("Could not examine class '" + fqn + "' due to a " + var6.getClass().getName() + " with message: " + var6.getMessage());
        }

    }

VFS類具體是怎么setInstance的毙死?繼續(xù)跟:

//這里的關(guān)鍵點(diǎn)就是getResources燎潮,Thread.currentThread().getContextClassLoader().getResources(),其實(shí)總結(jié)一下Mybatis的類掃描還是要依賴與jdk提供的類加載器
 protected static List<URL> getResources(String path) throws IOException {
 //獲取到資源路徑以列表形式放在集合里
        return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    }

    ...
    public List<String> list(String path) throws IOException {
        List<String> names = new ArrayList();
        Iterator var3 = getResources(path).iterator();
    //遍歷封裝成列表
        while(var3.hasNext()) {
            URL url = (URL)var3.next();
            names.addAll(this.list(url, path));
        }

        return names;
    }

ok扼倘,本博客只是稍微跟一下源碼确封,并沒有對(duì)Mybatis的源碼進(jìn)行比較系統(tǒng)更高層次的分析。
跟了一下源碼知道再菊,稍微總結(jié)一下Mybatis對(duì)別名的注冊(cè)是先將從sqlSessionFactoryBean類set的別名報(bào)名進(jìn)行tokenizeToStringArray拆分成數(shù)組爪喘,然后將包名數(shù)組丟給ResolverUtil類和VFS等類進(jìn)行一系列類加載遍歷,之后將 resolverUtil.getClasses()獲取的類都賦值給Set<Class<? extends Class<?>>> typeSet 一個(gè)集合纠拔。其中也是依賴與類加載器秉剑。

支持Ant通配符方式setTypeAliasesPackage解決方案

從這個(gè)源碼比較簡(jiǎn)單的分析過程,我們并沒有找到支持所謂通配符的方法稠诲,通過類加載的話也是要傳個(gè)相對(duì)路徑去遍歷侦鹏,不過我上面描述的業(yè)務(wù)場(chǎng)景是要兼容通配符的情況的诡曙,一般不會(huì)去改包名,假如這個(gè)項(xiàng)目有一定規(guī)模的話略水。

下面給出我的解決方案:

package com.muses.taoshop.common.core.database.annotation;

import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;

import static com.muses.taoshop.common.core.database.config.BaseConfig.ENTITY_PACKAGES;

public class AnnotationConstants {

    public static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    public final static String PACKAGE_PATTERN =
            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
            + ClassUtils.convertClassNameToResourcePath(ENTITY_PACKAGES)
            + DEFAULT_RESOURCE_PATTERN;

}

寫一個(gè)掃描類价卤,代碼參考Hibernate的AnnotationSessionFactoryBean源碼

package com.muses.taoshop.common.core.database.annotation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.util.*;

import org.springframework.core.io.Resource;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.util.StringUtils;

import static com.muses.taoshop.common.core.database.annotation.AnnotationConstants.PACKAGE_PATTERN;

/**
 * <pre>
 *  TypeAlicsesPackage的掃描類
 * </pre>
 *
 * @author nicky
 * @version 1.00.00
 * <pre>
 * 修改記錄
 *    修改后版本:     修改人:  修改日期: 2018.12.01 18:23    修改內(nèi)容:
 * </pre>
 */
@Component
public class TypeAliasesPackageScanner {

    protected final static Logger LOGGER = LoggerFactory.getLogger(TypeAliasesPackageScanner.class);
    private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
   /* private TypeFilter[] entityTypeFilters = new TypeFilter[]{new AnnotationTypeFilter(Entity.class, false),
            new AnnotationTypeFilter(Embeddable.class, false),
            new AnnotationTypeFilter(MappedSuperclass.class, false),
            new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)};*/


    public static String getTypeAliasesPackages() {
        Set<String> packageNames = new TreeSet<String>();
        //TreeSet packageNames = new TreeSet();
        String typeAliasesPackage ="";
        try {
            //加載所有的資源
            Resource[] resources = resourcePatternResolver.getResources(PACKAGE_PATTERN);
            MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            //遍歷資源
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader reader = readerFactory.getMetadataReader(resource);
                    String className = reader.getClassMetadata().getClassName();
                    //eg:com.muses.taoshop.item.entity.ItemBrand
                    LOGGER.info("className : {} "+className);
                    try{
                        //eg:com.muses.taoshop.item.entity
                        LOGGER.info("packageName : {} "+Class.forName(className).getPackage().getName());
                        packageNames.add(Class.forName(className).getPackage().getName());
                    }catch (ClassNotFoundException e){
                        LOGGER.error("classNotFoundException : {} "+e);
                    }
                }
            }
        } catch (IOException e) {
            LOGGER.error("ioException =>: {} " + e);
        }
        //集合不為空的情況,拼裝一下數(shù)據(jù)
        if (!CollectionUtils.isEmpty(packageNames)) {
            typeAliasesPackage = StringUtils.join(packageNames.toArray() , ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        }else{
            LOGGER.info("set empty,size:{} "+packageNames.size());
        }
        return typeAliasesPackage;
    }

    
}

然后剛才的Mybatis配置類改一下渊涝,主要改 String typeAliasesPackage = packageScanner.getTypeAliasesPackages();通過掃描類去獲取一下慎璧,重點(diǎn)代碼在TypeAliasesPackageScanner 掃描類

package com.muses.taoshop.common.core.database.config;
//省略代碼
@MapperScan(
        basePackages = MAPPER_PACKAGES,
        annotationClass = MybatisRepository.class,
        sqlSessionFactoryRef = SQL_SESSION_FACTORY
)
@EnableTransactionManagement
@Configuration
public class MybatisConfig {
    //省略其它代碼,主要看sqlSessionFactory配置
    @Primary
    @Bean(name = SQL_SESSION_FACTORY)
    public SqlSessionFactory sqlSessionFactory(@Qualifier(DATA_SOURCE_NAME)DataSource dataSource)throws Exception{
        //SpringBoot默認(rèn)使用DefaultVFS進(jìn)行掃描跨释,但是沒有掃描到j(luò)ar里的實(shí)體類
        VFS.addImplClass(SpringBootVFS.class);
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        //factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try{
            factoryBean.setMapperLocations(resolver.getResources("classpath*:/mybatis/*Mapper.xml"));
            String typeAliasesPackage = packageScanner.getTypeAliasesPackages();
            //設(shè)置一下TypeAliasesPackage
            factoryBean.setTypeAliasesPackage(typeAliasesPackage);
            SqlSessionFactory sqlSessionFactory = factoryBean.getObject();
            return sqlSessionFactory;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胸私,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子煤傍,更是在濱河造成了極大的恐慌盖文,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚯姆,死亡現(xiàn)場(chǎng)離奇詭異五续,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)龄恋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門疙驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人郭毕,你說我怎么就攤上這事它碎。” “怎么了显押?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵扳肛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我乘碑,道長(zhǎng)挖息,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任兽肤,我火速辦了婚禮套腹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘资铡。我一直安慰自己电禀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布笤休。 她就那樣靜靜地躺著尖飞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葫松,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天瓦糕,我揣著相機(jī)與錄音,去河邊找鬼腋么。 笑死咕娄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的珊擂。 我是一名探鬼主播圣勒,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼摧扇!你這毒婦竟也來了圣贸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤扛稽,失蹤者是張志新(化名)和其女友劉穎吁峻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體在张,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡用含,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帮匾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啄骇。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瘟斜,靈堂內(nèi)的尸體忽然破棺而出缸夹,到底是詐尸還是另有隱情,我是刑警寧澤螺句,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布虽惭,位于F島的核電站,受9級(jí)特大地震影響蛇尚,放射性物質(zhì)發(fā)生泄漏趟妥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一佣蓉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧亲雪,春花似錦勇凭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灌砖,卻和暖如春璧函,著一層夾襖步出監(jiān)牢的瞬間傀蚌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工蘸吓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留善炫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓库继,卻偏偏與公主長(zhǎng)得像箩艺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宪萄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis 艺谆? MyBatis 是支持定制化 SQL、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,516評(píng)論 0 4
  • question 1 逆序遍歷二分樹 給定一個(gè)二分搜索樹(BST)拜英,對(duì)于每一個(gè)節(jié)點(diǎn)静汤,加上所有比它大的節(jié)點(diǎn)的和,然后...
    vincehxb閱讀 135評(píng)論 0 0
  • 跨越2017居凶,啟程2018虫给,讓我們一起朗誦感恩詞,懂得感恩是宇宙給我們的最大的智慧排监!伴著新年的鐘聲讓我們感恩一切狰右,...
    實(shí)現(xiàn)幸福的人生閱讀 1,552評(píng)論 0 3
  • int main(int argc, const char * argv[]) { @autoreleas...
    cmhfx1閱讀 378評(píng)論 0 0