- 博客(86)
- 资源 (5)
- 收藏
- 关注
原创 一文搞懂什么是事务
一文搞懂什么是事务[toc]事务概念我们要理解下事务概念: 什么是事务呢?事务是并发控制的单位,是用户定义的一个操作序列。有四个特性(ACID):原子性(Atomicity): 事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。一致性(Consistency): 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。隔离性(Isolation): 一个事务的执行不能被其他事务干扰。持续性/永久性(Durability): 一个事
2020-12-18 16:57:43
969
1
原创 Mybatis映射器源码解析
Mybatis映射器映射器是MyBatis最强大的⼯具,也是我们使用MyBatis时⽤得最多的工具,因此熟练掌握它⼗分必要。MyBatis是针对映射器构造的SQL构建的轻量级框架,并且通过配置生成对应的JavaBean返回给调用者,⽽这些配置主要便是映射器,在MyBatis中你可以根据情况定义动态SQL来满足不同场景的需要,它比其他框架灵活得多。MyBatis还支持⾃动绑定JavaBean,我们只要让SQL返回的字段名和JavaBean 的属性名保持一致(或者采⽤驼峰式命名),便可以省掉这些繁琐
2020-12-11 09:20:13
383
原创 Github出暗黑模式了,加班写代码不再刺眼啦!
开源代码托管平台 GitHub 在本周宣布了一系列更新,不过最让熬夜写代码的开发者们感到欣喜的,莫过于期待已久的黑暗模式。其它方面,GitHub 现允许企业直接向开发者提供援助和投资开源项目,自动合并查询请求,以及有关公共存储库的讨论和依赖性审查(Beta 测试阶段)。GitHub Universe 定于 12 月 8~10 日在线上虚拟举办官方表示,黑暗模式有助于缓解过亮的屏幕对用户造成刺激,以及为开发者提供跨文本编辑器(Text Editor)、集成开发环境(IDE)和终端(Terminal
2020-12-09 15:57:59
848
1
原创 Mybatis中重要组件的生命周期
Mybatis中重要组件的生命周期在之前的章节中我们讲了Mybatis一些主要组件的基本用法和配置方式,但是掌握这些主要组件的生命周期也是非常重要的,不能只是单单了解基本的使用方法。这对于MyBatis应⽤的正确性和高性能是极其重要的,我们必须掌握他们本单主要内容:以下各组件的生命周期理解:SqlSessionFactoryBuilderSqlSessionFactorySqlSessionMapperSqlSessionFactoryBuilderSqlSessionFactory
2020-12-09 14:58:44
238
原创 GitHub 开源项目文章爬虫,懒人必备!
hello,小伙伴们大家好今天给大家介绍的开源项目是文章爬虫利器爱收集的小伙伴们的福利哦!假如你在工作中接到产品小姐姐的需求,需求是获取V2EX,知乎,简书,知否(SegmentFault),掘金,CSDN博客,微信公众号文章,cnblogs等中文网站中输出正文内容、标题、作者、发布时间、正文中的图片地址和正文所在的标签源代码 转化为Markdown,你会怎么做,假如你 code 功力还没有经过九九八十一天的修炼,还没有练到元婴期,你脑子里应该想的是我要写这么多规则,可怎么办,要累死人呢!要是有一个
2020-12-09 10:40:57
328
原创 Mybatis数据源结构解析之连接池
对于 ORM 框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题。本文将通过对 MyBatis 框架的数据源结构进行详尽的分析,找出什么时候创建 Connection ,并且深入解析 MyBatis 的连接池。本章的组织结构:零、什么是连接池和线程池一、MyBatis 数据源 DataSource 分类二、数据源 DataSource 的创建过程三、 DataSource 什么时候创建 Connection 对象四、不使用连接池的 UnpooledDataSource
2020-12-01 10:55:59
3824
原创 mybatis整体架构以及执行流程
一、MyBatis简介MyBatis框架是一种轻量级的ORM框架,当下十分流行,配合Spring+Spring MVC组成SSM框架,能够胜任几乎所有的web项目工程。MyBatis不同于Hibernate的最重要的地方就是SQL部分,MyBatis中你可以自己编写灵活的SQL代码,而在Hibernate基本上将SQL代码封装起来的,几乎不会用到程序员去编写,这是Hibernate的优秀之处(简化了程序开发),同时也是其病脚(无法灵活开发)。MyBatis的轻量级是相对于Hibernate的重量级而言
2020-12-01 09:31:57
303
1
原创 mybatis源码分析(一) 配置文件的解析过程
mybatis的源码有人已经做过一个中文的注释,代码github上有mybatis中文注释源码mybatis框架有两个非常重要的xml文件,一个是mybatis的config文件,一个就是mapper文件,mybatis会根据config的xml文件去生成一个Configuration类,在这个过程中也会根据配置的mapper文件生成MappedStatement,这篇博客探究的就是这样一个过程,往下看如果单单使用mybatis,我们的做法是导包,配置,然后如下String resource = &q
2020-11-30 22:03:45
280
原创 mybatis源码分析(二) 执行过程
这边博客衔接上一篇mybatis的xml解析的博客,在xml解析完成之后,首先会解析成一个Configuration对象,然后创建一个DefaultSqlSessionFactory的session工厂。在这一切的准备过程完成之后,就可以开始对数据库的操作了。首先看openSession()方法private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boole
2020-11-30 22:01:26
256
原创 Mybatis源码阅读(二)
本文主要介绍Java中,不使用XML和使用XML构建SqlSessionFactory,通过SqlSessionFactory 中获取SqlSession的方法,使用SqlsessionManager管理Sqlsession复用等等..以及相关的示例代码SqlSessionSqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的各种方法。而 SqlSessionFactory 本身是由 SqlSessi
2020-11-27 20:08:31
678
原创 Mybatis源码分析(一)
准备在阅读源码前,需要先clone源码 地址:https://github.com/mybatis/mybatis-3Mybatis框架使用大量常见的设计模式,学习Mybatis源码我们主要学习以下几点:学习大佬们的编码思想及规范学习一些传承下来的设计模式实践java基础理论带着问题阅读源码 问题1:源码中用了哪些设计模式?为什么要用这些设计模式?问题2.Mybatis打开调试模式之后,能打印sql语句等信息,这是怎么实现的?实现过程中使用了什么设计模式?假如有多个日志实现,加
2020-11-26 12:52:53
284
原创 数据结构之二叉树
1 重点概念1.1 结点概念一棵二叉树是节点的一个有限集合,该集合或者为空,或者由一个根节点加上两棵左子树和右子树组成结点是数据结构中的基础,是构成复杂数据结构的基本组成单位。1.2 树结点声明本系列文章中提及的结点专指树的结点。例如:结点A在图中表示为:2 树2.1 定义树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:1)有且仅有一个特定的称为根(Root)的结点;2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2
2020-11-10 14:41:52
1520
原创 ReetrantLock源码分析
ReentrantLock类的大部分逻辑,都是其均继承自AQS的内部类Sync实现的啥是AQS:Java并发编程核心在于java.concurrent.util包而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如「等待队列、条件队列、独占获取、共享获取」等,而这个行为的抽象就是基于AbstractQueuedSynchronizer简称AQS 它定义了一套多线程访问共享资源的同步器框架,是一个「依赖状态(state)的同步器」。以公平锁为例子: public static void
2020-11-06 15:52:23
288
2
转载 深入理解JAVA并发锁
深入理解 Java 并发锁1. 并发锁简介确保线程安全最常见的做法是利用锁机制(Lock、sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的。在工作、面试中,经常会听到各种五花八门的锁,听的人云里雾里。锁的概念术语很多,它们是针对不同的问题所提出的,通过简单的梳理,也不难理解。1.1. 可重入锁可重入锁,顾名思义,指的是线程可以重复获取同一把锁。即同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁。
2020-11-05 09:28:10
315
1
原创 Java多线程之CAS
CAS (Compare and Swap)CAS字面意思为比较并交换.CAS 有 3 个操作数,分别是:内存值 M,期望值 E,更新值 U。当且仅当内存值 M 和期望值 E 相等时,将内存值 M 修改为 U,否则什么都不做。1.CAS的应用场景CAS 只适用于线程冲突较少的情况。CAS 的典型应用场景是:原子类自旋锁1.1 原子类原子类是 CAS 在 Java 中最典型的应用。我们先来看一个常见的代码片段。if(a==b) { a++;}如果 a++ 执行前, a
2020-11-03 09:36:23
330
原创 Java创建多线程的几种方式
Java创建多线程的几种方式[TOC]1、继承Thread类,重写run()方法//方式1package cn.itcats.thread.Test1;public class Demo1 extends Thread{ //重写的是父类Thread的run() public void run() { System.out.println(getName()+"is running..."); } public stati
2020-11-02 17:22:58
223
原创 java并发之无同步方案-ThreadLocal
JAVA多线程并发容易引发的问题及如何保证线程安全之前的章节中我们介绍了在并发时,容易引发的问题及如何保证线程安全,本章节我们主讲JAVA并发中的无同步方案: ThreadLocal无同步方案:1.可重入代码:可重入代码:可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回之后,原来的程序不会出现任何的错误。可重入代码有一些公共的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数传入、不调用非可重入的方法等。简而言之:如果一个方法,它的返回结果是可以预测的.
2020-11-02 17:10:06
426
原创 JAVA对象布局之对象头(Object Header)
由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能 。在学习并发编程知识synchronized时,我们总是难以理解其实现原理,因为偏向锁、轻量级锁、重量级锁都涉及到对象头,所以了解java对象头是我们深入了解synchronized的前提条件,以下我们使用64位JDK示例1.对象布局的总体结构2.获取一个对象布局实例1.首先在maven项目中 引入查看对象布局的神器 <dependency>.
2020-10-29 09:16:38
4927
5
原创 Java创建多线程的几种方式
Java创建多线程的几种方式[TOC]1、继承Thread类,重写run()方法//方式1package cn.itcats.thread.Test1;public class Demo1 extends Thread{ //重写的是父类Thread的run() public void run() { System.out.println(getName()+"is running..."); } public stati
2020-10-27 10:04:04
196
原创 JAVA并发之加锁导致的活跃性问题
首先提及一下前置知识:1.JAVA并发之基础概念 2.JAVA并发之进程VS线程 3.JAVA并发之多线程引发的问题剖析及如何保证线程安全 在前三章我们讨论了多线程并发的优点以及如何加锁来处理并发带来的安全性问题 但是加锁也为我们带来了诸多问题 如:死锁,活锁,线程饥饿等问题 这一章我我们主要处理锁带来的问题.首先就是最出名的死锁1.死锁(Deadlock)什么是死锁死锁是当线程进入无限期等待状态时发生的情况,因为所请求的锁被另一个线程持有,而另一个
2020-10-27 09:22:54
473
原创 JAVA并发之多线程引发的问题剖析以及如何保证线程安全
JAVA多线程中的各种问题剖析首先开始之前 需要提及一下前置章节能够更加深入了解本节所讲JAVA并发之基础概念篇JAVA并发之进程VS线程篇首先我们来说一下并发的优点,根据优点特性,引出并发应当注意的安全问题1并发的优点技术在进步,CPU、内存、I/O 设备的性能也在不断提高。但是,始终存在一个核心矛盾:CPU、内存、I/O 设备存在速度差异。CPU 远快于内存,内存远快于 I/O 设备。根据木桶短板理论可知,一只木桶能装多少水,取决于最短的那块木板。程序整体性能取决于最慢的操作—
2020-10-26 09:40:25
722
1
原创 JAVA并发之进程VS线程
进程和线程进程:进程可理解为是一个正在运行的程序整体进程是具有一定独立功能的程序,是操作系统进行资源分配的基本单位线程:线程是操作系统进行调度的基本单位进程 vs. 线程一个程序至少有一个进程,一个进程至少有一个线程2. 线程比进程划分更细,执行开销更小,并发性更高进程是一个实体,拥有独立的资源,而同一进程中的多个线程是共享进程的资源的如下图:JVM在单进程中运行,JVM中的线程共享属于该进程的堆,所以几个线程可以同时访问堆中的某个对象,这就导致了线程不安全线程在
2020-10-23 10:53:54
275
原创 Java中的OutOfMemoryError的各种情况及解决和JVM内存结构
在JVM中内存一共有3种:Heap(堆内存),Non-Heap(非堆内存) [3]和Native(本地内存)。 [1]堆内存是运行时分配所有类实例和数组的一块内存区域。非堆内存包含方法区和JVM内部处理或优化所需的内存,存放有类结构(如运行时常量池、字段及方法结构,以及方法和构造函数代码)。本地内存是由操作系统管理的虚拟内存。当一个应用内存不足时就会抛出java.lang.OutOfMemoryError 异常。 [1] 问题 表象 诊断工具
2020-10-22 16:16:21
963
原创 JAVA并发之基础概念
1.并发和并行举个例子并发(concurrency):并发的关键在于有处理多个任务的能力,但并不一定就是同时处理.例1: 吃饭时,电话响起,暂时停下吃饭的动作,接完电话后继续吃饭.例2: 一个人吃3个馒头并行(parallelism):并行的关键在于同时处理多个任务例子1: 吃饭时,电话响起,同时进行吃饭和电话2个动作例子2: 三个人吃3个馒头“并发”指的是程序的结构,“并行”指的是程序运行时的状态即使不看详细解释,也请记住这句话.并行(parallelism)这个概念很好
2020-10-22 11:11:46
254
原创 Docker之两小时入门
1、引言1.1 Docker是什么Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 使用 Google 公司推出的 Go 语言 进行开发实现。docker是linux容器的一种封装,提供简单易用的容器使用接口。它是最流行的Linux容器解决方案。docker的接口相当简单,用户可以方便的创建、销毁容器。docker
2020-10-22 09:37:12
287
原创 JAVA构造函数没有返回值是怎么赋值的?
众所周知,在java里是不能给构造函数写返回值的,如果在低版本的编译器定义一个构造器写上返回值可能会报错,高版本里面他就是一个普通的方法。可是如果构造函数没有返回值,那么比如Test t = new Test()我们new一个对象的时候是怎么赋值的呢?构造函数有返回值吗写一段代码测试一下:publicclassTest{publicTest(){}publicstaticvoidmain(String[]args){...
2020-10-22 09:26:15
1026
原创 请勿过度依赖Redis的过期监听!!
作者:迪壳https://juejin.im/post/6844904158227595271Redis 过期监听场景业务中有类似等待一定时间之后执行某种行为的需求 , 比如 30 分钟之后关闭订单 . 网上有很多使用 Redis 过期监听的 Demo , 但是其实这是个大坑 , 因为 Redis 不能确保 key 在指定时间被删除 , 也就造成了通知的延期 . 不多说 , 跑个测试测试情况先说环境 , redis 运行在 Docker 容器中 , 分配了 一个 cpu 以及 512MB 内存.
2020-10-22 09:22:33
245
原创 如何快速创建百万级测试数据
场景进行SQL优化或查询性能测试时,我们需要大量数据测试来模拟,这个时候引出一个问题:数据的创建如何快速创建大量数据创建数据无非几种操作下面一一列举;~ 1 手动输入 (可忽略)~ 2 使用编写好的存储过程和函数执行 (下面会有介绍)~ 3 编写代码,通过代码插入数据 (例:使用mybatis的foreach循环插入..步骤多,速度慢)~ 4 临时数据表方式执行 (强烈推荐,速度快,简单)准备操作前提首先 我们不管选哪种操作 都要先准备一张表,这个是毫无疑问的;那么我们就简单的创建一个
2020-10-22 09:12:44
922
原创 公司线上虚拟机大量GC导致STW和CPU飙升--抽丝剥茧定位的过程
作者: bryan31原链接:https://www.v2ex.com/t/701513背景公司线上有个 tomcat 服务,里面合并部署了大概 8 个微服务,之所以没有像其他微服务那样单独部署,其目的是为了节约服务器资源,况且这 8 个服务是属于边缘服务,并发不高,就算宕机也不会影响核心业务。因为并发不高,所以线上一共部署了 2 个 tomcat 进行负载均衡。这个 tomcat 刚上生产线,运行挺平稳。大概过了大概 1 天后,运维同事反映 2 个 tomcat 节点均挂了。无法接受新的请求.
2020-10-21 15:52:06
13423
原创 这是一篇来源于阿里内部技术论坛的文章
这是一篇来源于阿里内部技术论坛的文章,原文在阿里内部获得一致好评。作者已经把这篇文章开放到云栖社区中供外网访问。Hollis对文章内容做了部分删减,主要删减掉了其中只有阿里内部才能使用的工具的介绍,并删减掉部分只有通过阿里内网才能访问到的链接。前言平时的工作中经常碰到很多疑难问题的处理,在解决问题的同时,有一些工具起到了相当大的作用,在此书写下来,一是作为笔记,可以让自己后续忘记了可快速翻阅,二是分享,希望看到此文的同学们可以拿出自己日常觉得帮助很大的工具,大家一起进步。闲话不多说,开搞。Linux
2020-10-21 15:40:37
1727
原创 一文彻底弄懂如何选择抽象类还是接口
前言抽象类、接口对于不论是Java、C++等程序猿都不陌生,但你知道如何正确地使用抽象类和接口吗?你是否还在模棱两可、只是简单记忆了两者的区别,遇到实际情况就不知道如何选择?今天,我就来带你彻底弄清楚这俩的区别,当然,本文基于Java,但是对其他语言的程序猿一样适用,包会~什么是抽象类?什么是接口?这里照顾刚入门的程序猿,简单介绍一下这俩的定义。抽象类(abstract class):为了继承而存在。接口(interface):比抽象类更抽象的存在。介绍的确实挺简单,但还是根本就不知道怎么..
2020-10-21 15:33:10
1679
原创 JDK 从老版本升级到 1.8的问题总结
JDK8 升级常见问题JDK8 发布很久了,它提供了许多吸引人的新特性,能够提高编程效率。如果是新的项目,使用 JDK8 当然是最好的选择。但是,对于一些老的项目,升级到 JDK8 则存在一些兼容性问题,是否升级需要酌情考虑。近期,我在工作中遇到一个任务,将部门所有项目的 JDK 版本升级到 1.8 (老版本大多是 1.6)。在这个过程中,遇到一些问题点,并结合在网上看到的坑,在这里总结一下。Intellij 中的 JDK 环境设置Settings点击 File > Settings.
2020-10-21 15:28:27
3937
原创 这10道springboot常见面试题你需要了解下
1.什么是Spring Boot?多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects 页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能。如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置。因此,开始一个新的spring项目需要很多努力,因为我们现在必须从头开始做所有事情。Spring Boot是解决这个问题的方法。Spring Boot
2020-07-02 13:38:27
719
原创 面试常问题:为什么要使用MQ消息中间?
在面试大型互联网公司的时候,很可能会被问到消息队列的问题: 1.在何种场景下使用了消息中间件? 2.为什么要在系统里引入消息中间件? 3.如何实现幂等?链式调用是我们在写程序时候的一般流程,为了完成一个整体功能,会将其拆分成多个函数(或子模块),比如模块A调用模块B,模块B调用模块C,模块C调用模块D。但在大型分布式应用中,系统间的RPC交互繁杂,一个功能背后要调用上百个接口并非不可能,这种架构有如下几个劣势:1、 这些接口之间耦合比较严重,每新增一个下游功能,都要对上有的相关接口进行改造;举个例
2020-07-02 13:33:39
723
原创 MyBatis动态SQL(认真看看, 以后写SQL就爽多了)
作者:阿进的写字台cnblogs.com/homejim/p/9909657.html温馨提示:文中代码看不全可左右滑动MyBatis 令人喜欢的一大特性就是动态 SQL。 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的。 MyBatis 动态 SQL 的出现, 解决了这个麻烦。MyBatis通过 OGNL 来进行动态 SQL 的使用的。目前, 动态 SQL 支持以下几种标签:1 数据准备为了后面的演示, 创建了一个 Maven 项目 mybatis-dynam
2020-07-02 13:18:50
595
原创 深入理解硬盘原理,Mysql索引底层数据结构与算法的来龙去脉(多图)
前言:如何触发尽量少的磁盘io 找到数据?数据库中的索引是什么?硬盘原理 现在大部分存储设备依然是硬盘 信息存储在硬盘里,把它拆开也看不见里面有任何东西,只有些盘片。假设,你用显微镜把盘片放大,会看见盘片表面凹凸不平,凸起的地方被磁化,凹的地方是没有被磁化;凸起的地方代表数字1(磁化为1),凹的地方代表数字0。因此硬盘可以以二进制来存储表示文字、图片等信息。硬盘的组成一般说来,无论哪种硬盘,都是由盘片、磁头、盘片主轴、控制电机、磁头控制器、数据转换器、接口、缓存等几个部份组成。所
2020-06-29 21:19:07
959
原创 深入理解硬盘原理,Mysql索引底层数据结构与算法的来龙去脉(多图)
前言:如何触发尽量少的磁盘io 找到数据?数据库中的索引是什么?硬盘原理 现在大部分存储设备依然是硬盘 信息存储在硬盘里,把它拆开也看不见里面有任何东西,只有些盘片。假设,你用显微镜把盘片放大,会看见盘片表面凹凸不平,凸起的地方被磁化,凹的地方是没有被磁化;凸起的地方代表数字1(磁化为1),凹的地方代表数字0。因此硬盘可以以二进制来存储表示文字、图片等信息。硬盘的组成一般说来,无论哪种硬盘,都是由盘片、磁头、盘片主轴、控制电机、磁头控制器、数据转换器、接口、缓存等几个部份组成。所
2020-06-29 21:18:10
882
原创 Java 基本功 (基础概念与常识)
主要涉及知识点:JVM JDK 和 JRE 最详细通俗的解答Oracle JDK 和 OpenJDK 的对比Java 和 C++的区别?什么是 Java 程序的主类 应用程序和小程序的主类有何不同?Java 应用程序与小程序之间有哪些差别?import java 和 javax 有什么区别?为什么说 Java 语言“编译与解释并存”?1.Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字
2020-06-29 14:22:46
825
2
原创 对java文件头的解析
java对象保存在内存中时有3个部分1.对象头2.实例数据3.对齐填充字节一. 对象头 java的对象头有3部分组成: 1.Mark Word 2.指向类的指针 3.数组长度(如果是数组对象的话才有)1.Mark WordMark Word 存储的内容 32bit 中如下所示:其中无锁和偏向锁的锁标志位都是01 只是用前一位来表示是无锁还是偏向锁 JDK1.6后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁
2020-06-28 10:31:56
1281
原创 SpringBoot项目集成sentinel降级快速失败
项目的配置:pom.xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> ...
2020-04-02 15:32:07
2156
notion_2.0.11.dmg notion 2020年最新 mac版本
2020-12-14
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人