Android开发经验谈Android开发Android技术知识

源码笔记01:Retrofit2源码剖析

2017-09-11  本文已影响140人  19snow93

距离上次发布文章有一段时间了,这次会带来之前答应大家源码剖析系列的文章。这个系列的文章更多的是对自己源码学习的一些总结,希望有共同爱好的同学能跟我一起交流,如果我有错误,向我指正。
这次我要写的源码剖析是近一两年非常火的网络框架---Retrofit2,虽然网上关于它的分析还是比较多的,但是每个人都有自己的一些理解和总结,所以最后我还是决定将它写出来。
我们首先看看Retrofit类,短短几百行的代码,还夹杂着一大堆的注释,可以说它的封装性是特别强的。首先我们看看Retrofit类的几个变量:

// 网络请求配置对象
// 作用:存储网络请求相关的配置,如解析网络请求的方法、数据转换器(Converter)、网络请求适配器(CallAdapter)、网络请求工厂(CallFactory)、基础地址(BaseUrl)等
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

//网络请求器的工厂
//作用:生产网络请求器(Call)
//Retrofit是默认使用okhttp(后面会解释)
final okhttp3.Call.Factory callFactory;

//网络请求的基础url地址
final HttpUrl baseUrl;

//数据转换器工厂的集合
//作用:放置数据转换器工厂
//数据转换器工厂作用:生产数据转换器(converter)
final List<Converter.Factory> converterFactories;

//网络请求适配器工厂的集合
//作用:放置网络请求适配器工厂
//网络请求适配器工厂作用:生产网络请求适配器(CallAdapter)
final List<CallAdapter.Factory> adapterFactories;

//回调方法执行器
final Executor callbackExecutor;
//标志位
//作用:是否提前对业务接口中的注解进行验证转换的标志位
final boolean validateEagerly;

可以看到,Retrofit类的变量并不多,下面我们看看构造函数:

//构造函数
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }

其实就是对Retrofit的几个变量做了赋值。
下面我们来看看整个Retrofit的创建流程:

初始化步骤.png

步骤2:

步骤2.png

上面我们已经看过Retrofit的构造函数了,所以可以省略步骤1,那下面我们就开始看看步骤2,Retrofit的Builder这个类的构成:

  //Builder类
  public static final class Builder {
    //平台
    private final Platform platform;
    //一下几个变量与Retrofit的构造函数大致相同
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());
    }

    //构造函数
    public Builder() {
      this(Platform.get());
    }
}

我们可以看到Builder类除了Platform之外,其他几个变量我们都在前面见过,那究竟Platform是什么呢,我们来解密一下,首先我查了一下翻译,Platform是“平台”的意思,查了翻译我们就知道大概了,因为我们知道Retrofit支持不单止Android一个平台,那下面我们就来看看Platform类:

class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      // Class.forName(xxx.xx.xx)的作用:要求JVM查找并加载指定的类
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        // 如果是Android平台,就创建并返回一个Android对象
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      // 如果是Java平台,就创建并返回一个Java8对象
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  Executor defaultCallbackExecutor() {
    return null;
  }

  CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }

  // 用于接收服务器返回数据后进行线程切换在主线程显示结果
  static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      // 获取与Android 主线程绑定的Handler
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        // 该Handler是上面获取的与Android 主线程绑定的Handler 
        // 在UI线程进行对网络请求返回数据处理等操作。
        handler.post(r);
      }
    }
  }
}

上面看完Platform类的findPlatform()方法之后,我们就很清楚的知道这个版本的Retrofit2.0是支持Android和Java平台的,此外,在Retrofit2其他版本中还会支持IOS的版本,在这个版本被去掉了。看源码重要的是在看完源码后能获取一些收获,所以像Retrofit这个这么成功的框架中我们需要去领会到它一些代码的写法。譬如在Platform这个类中,我们以后想做到分情况考虑的时候,也可以用像findPlatform()方法的这种实现(用了两个Class.forName(xxx.xx.xx)的方法就很好的区分了使用的平台),简洁而且实用。
我们可以看到其实步骤2的Builder类最特别的Platform分析完毕之后,步骤2就基本分析完了。这里的Retrofit使用了建造者模式,我们下面再做详细的分析。
下面我们看看步骤3:

步骤3:

步骤3.png
public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      //注意点1
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      //注意点2
      return this;
    }

我们可以看到,代码很简单,就是两个方法,至于看到方法名大家更加清楚明白,这是一个设置基础URL的方法。上面我们只需要关注两个注意点:
1.baseUrl必须要以“/”结束,否则会抛出异常,其实他的源码注释也解释了一些特殊的情况。

baseUrl注释.png
2.return一个Builder类型,其实是建造者模式一个常用的手法,以供使用者很好的链式配置你想要的相关配置。

继续,步骤4:

步骤4(可选):

配置client.png
public Builder client(OkHttpClient client) {
      //注意点1
      return callFactory(checkNotNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
     this.callFactory = checkNotNull(factory, "factory == null");
     return this;
}

我们都知道Retrofit其实只是名义上可以称为一个网络框架,但是其实,完成网络请求这块的功能并不是Retrofit,而是我们耳熟能详的OKhttp。关于步骤4,我们看到client()方法传的参数是OkHttpClient 就知道这里是配置一个OkHttp的OkHttpClient,其实Retrofit默认就是使用OKhttp作为他的网络请求的载体,所以如果对OKhttp没有特殊要求(如设置拦截器之类)的同学可以忽略步骤4。
注意点1:我们可以上面两个方法看到其实Retrofit类里面的callFactory指的就是OkHttpClient

步骤5,6:

设置Factory.png
//设置数据交换器
public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
//设置网络请求适配器
 public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
 }

我们可以看到步骤5,6其实可以归到同一类去讲,就是添加适配器,我们更值得说的是,“···Factory”用的都是设计模式里面的工厂模式,而“···Adapter”用的就是设计模式里面的适配器模式

步骤7:

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        //注意点1
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      // 如果没指定,则默认使用Platform检测环境时的默认callbackExecutor
      // 即Android默认的callbackExecutor
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

     
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 在步骤2中已经添加了内置的数据转换器BuiltInConverters()(添加到集合器的首位)
      // 在步骤5中又插入了一个Gson的转换器 - GsonConverterFactory(添加到集合器的首二位)
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
     
      // 最终返回一个Retrofit的对象,并传入上述已经配置好的成员变量
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

我们可以看到,步骤7也就是最后一步,是通过前面几步设置的变量,最后返回一个新的Retrofit对象,创建一个Retrofit实例,很明显,通过这几步的配置,是用了设计模式里面的建造者模式,当然在这里我们又学到了,当我们自定义东西的时候,通过建造者模式,我们可以灵活的配置自己想要的东西,而不是一味的把代码写死,每次都是需要触碰到源代码的修改,适当的使用设计模式,会使你的代码更加灵活。

总结:

总结.png

上面我们看完了Retrofit是怎么配置参数源码,我们大致了解Retrofit是怎么进行初始化的,下面我们看看Retrofit在使用时是怎么运作的:

//步骤1
//Retrofit与很多网络框架不一样,使用前是需要先设置好JavaBean
public class WXItemBean {
}

//步骤2  定义网络请求的接口类
public interface TestApi {
    //注解GET:采用Get方法发送网络请求
    @GET("wxnew")
    rx.Observable<WXHttpResponse<List<WXItemBean>>> getWXHot(@Query("key") String key, @Query("num") int num, @Query("page") int page);
}

//步骤3  创建接口类实例
testApi = retrofit.create(TestApi.class);

//步骤4 网络请求
Subscription subscribe = testApi.getWXHot(key, num, page)
                .compose(RxUtil.<WXHttpResponse<List<WXItemBean>>>rxSchedulerHelper())
                .compose(RxUtil.<List<WXItemBean>>handleResult1())
                .subscribe(new BaseSubscriber1<List<WXItemBean>>() {
                    @Override
                    public void onNextT(List<WXItemBean> wxItemBeen) {
                        mView.testSuccess(wxItemBeen);
                    }

                    @Override
                    public void onErrorT(String msg) {

                    }
                });
 addSubscrebe(subscribe);

下面我们主要来分析一下步骤3,也就是create()。在介绍这个方法之前,我先告诉大家这个方法使用了设计模式的外观模式(大大降低了耦合度)和代理模式,其中代理模式用的是动态代理,什么是动态代理?其实Java早已经帮我们封装好了方法,我们来举个栗子:

public interface ITest
{
    @GET("/wx")
    public void add(int a, int b);

}
public static void main(String[] args)
{
    ITest iTest = (ITest) Proxy.newProxyInstance(ITest.class.getClassLoader(), new Class<?>[]{ITest.class}, new InvocationHandler()
    {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            Integer a = (Integer) args[0];
            Integer b = (Integer) args[1];
            System.out.println("方法名:" + method.getName());
            System.out.println("参数:" + a + " , " + b);

            GET get = method.getAnnotation(GET.class);
            System.out.println("注解:" + get.value());
            return null;
        }
    });
    iTest.add(3, 5);
}

结果为:

方法名:add
参数:3 , 5
注解:/wx

是不是觉得很神奇?有了这个例子我们接下来就很好理解create()方法了。

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    //判断是否需要提前验证
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // 创建了网络请求接口的动态代理对象,即通过动态代理创建网络请求接口的实例 (并最终返回)
    // 该动态代理是为了拿到网络请求接口实例上所有注解
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //注意点1
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //注意点2
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //注意点3
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

很明显,我们看到create()方法最核心的东西是使用了动态代理的Proxy.newProxyInstance()方法,通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。而Proxy.newProxyInstance()方法的核心正正是我们标注的注意点1,2,3。我可以这样说,Retrofit这个框架运作的时候,最核心的就是我们看到的这三句话。
我们首先来看看注意点1:

ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);

我们来看看loadServiceMethod(method)这个方法:

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    //获取缓存
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    设置同步锁
    synchronized (serviceMethodCache) {
       //获取缓存
      result = serviceMethodCache.get(method);
      //如果没有缓存,则创建一个新的ServiceMethod实例
      if (result == null) {   
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

那下面我们来看看ServiceMethod又是怎么创建出来的:
首先看看ServiceMethod的构造函数:

  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }

我们可以看到在ServiceMethod设置了各种不同的网络参数,然后我们看看Builder<>(this, method)这个方法:

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      // 网络请求接口方法里的注释(如@GET("wxnew"))
      this.methodAnnotations = method.getAnnotations();
      // 网络请求接口方法里的参数类型        
      this.parameterTypes = method.getGenericParameterTypes();    
      //网络请求接口方法里的注解内容    
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

接下来我们看看建造者模式的最后一步build()方法:

public ServiceMethod build() {
      //注意点1,从Retrofit对象中获取对应的网络请求适配器
      callAdapter = createCallAdapter();
     // 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取该网络适配器返回的数据类型
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        // 解析获取Http请求的方法,如GET、POST、HEAD
        parseMethodAnnotation(annotation);
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
        // 为方法中的每个参数创建一个ParameterHandler<?>对象并解析每个参数使用的注解类型
        // 该对象的创建过程就是对方法参数中注解进行解析
        // 这里的注解包括:Body、PartMap、Part、FieldMap、Field、Header、QueryMap、Query、Path、Url
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }
      //最后创建一个新的ServiceMethod实例
      return new ServiceMethod<>(this);
    }

我们看看注意点1,在ServiceMethod 中是如何创建CallAdapter的:

private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        //调用Retrofit类里面的callAdapter方法
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

  public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
  }

在上面这段代码中,通过遍历adapterFactories并根据我们的接口方法所中返回值类型来获取响应的适配器callAdapter。例如上面的例子中返回值是一个Call对象,将会采用默认的适配器,如果我们返回的是RxJava中Observable对象,如果我们添加了RxJavaCallAdapterFactory,那么返回的就是RxJavaCallAdapter。如果没有添加那么此处的adapter为null,便会抛出异常。

设置多个CallAdapterFactory.png
当我们初始配置的时候设置多个CallAdapterFactory,Retrofit会根据返回不同的对象,返回相应的CallAdapter。
ConverterFactory和CallAdapterFactory的创建原理基本一样,所以这里不多说。
下面我们看看整个CallAdapter创建的流程图:
CallAdapter创建过程.png

注意点1总结:

看完ServiceMethod这个类,我们就发现,这是一个非常非常重要的类,它把所有有关网络请求method,都转换成了Call的信息。

那下面我们看看注意点2

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
public class OkHttpCall {
    private final ServiceMethod<T> serviceMethod; // 含有所有网络请求参数信息的对象  
    private final Object[] args; // 网络请求接口的参数 
    private okhttp3.Call rawCall; //实际进行网络访问的类  
    private Throwable creationFailure;  
    private boolean executed;  
    private volatile boolean canceled;  

  //构造函数
  public OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {  
    // 传入ServiceMethod对象和请求参数
    this.serviceMethod = serviceMethod;  
    this.args = args;  
}

我们知道Retrofit实际上是用okhttp做网络请求的,所以这句话大家都很清楚,把刚刚配置的ServiceMethod对象传进OkHttpCall,并创建一个事例。
注意点3:

return serviceMethod.callAdapter.adapt(okHttpCall);

看到adapt()这个方法,我们大家都懂,就是用callAdapter去绑定okHttpCall。那下面我们再看adapt()这个方法到底做了什么:

adapt().png

这里有三种不用的CallAdapter,我们使用的是RxJavaCallAdpter,所以点进去看看:

@Override public Object adapt(Call<R> call) {
     //判断是同步还是异步请求,创建不同的OnSubscribe
    OnSubscribe<Response<R>> callFunc = isAsync
        ? new CallEnqueueOnSubscribe<>(call)
        : new CallExecuteOnSubscribe<>(call);

    OnSubscribe<?> func;
    if (isResult) {
      func = new ResultOnSubscribe<>(callFunc);
    } else if (isBody) {
      func = new BodyOnSubscribe<>(callFunc);
    } else {
      func = callFunc;
    }
    Observable<?> observable = Observable.create(func);

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }
    ···
    return observable;
  }

最后是返回一个Observable对象。
大致源码我们都看过分析过了,不清楚的流程的同学可以看看下面的流程图:

流程图.png
有同学可能最后会觉得有点奇怪,流程图的最后一步调用的是OkHttpCall.execute()(同步)方法,而不是使用OkHttpCall.enqueue()(异步)方法,为什么会这样呢?其实当Retrofit自己单独用的时候,网络请求确实调用的是OkHttpCall.enqueue()方法,但是当与RxJava一起使用的时候,Retrofit将切换线程这个工作交给了RxJava完成,所以Retrofit调用的是OkHttpCall.execute()方法,这点也是自己慢慢断点和一位师兄的提点才发现的。
从源码角度看,Retrofit更多算是一个封装库,把很多各种各样的框架连接在一起的桥梁,其中用了很多设计模式去封装,为大家带来了便捷。对于我来说,看完源码之后发现还是很多细节没弄懂,也促使自己慢慢去学习去探索,第一次发表关于源码的文章可能很多东西都有不足之处,希望大家一起指正我,跟我交流,大家一起成长!
基于以下项目讲解:
手把手为你封装一个MVP+RxJava+Retrofit2+Dagger2+BaseRecyclerView快速开发框架,两周一个版本不是梦

基于以下几位大神的文章编写:
Android:手把手带你深入剖析 Retrofit 2.0 源码
Retrofit2 完全解析 探索与okhttp之间的关系
Retrofit2.0源码解析
几个月前我在一个朋友的毕业照上再次遇到了她,其实我到现在也想不懂当初为什么会去参加。从那时候起,我就有了一种奇怪的感觉,大概那种就叫好感吧?但事以愿为,我来迟了。。。那一刻我在想,之前有多少次机会能拿到她的联系方式呢,最后因为自己的问题一次又一次的错过了。我很懊恼,那一晚我已经数不清重播了多少次《单城双恋》里面陈展鹏唱的那首《差半步》。虽然她平时很少跟我分享她开心、伤心的事,她也从来不告诉我她喜欢什么,但是我觉得能跟她普通的聊聊天我已经很知足了(她经常说知足常乐)。她说过她是一个比较特立独行的女孩,我也知道她外里总给人一种很坚强、独立的感觉,但是我觉得她的内心更需要别人的呵护、关心和督促。我很想成为那个当她迷路彷徨无助时帮助呵护她的人,很想成为那个当她伤心失落时陪伴关心她的人,也很想成为那个当她每晚晚睡督促她早睡的人。重遇她我改变了很多,以前别人都说我闷骚,现在哪怕遇到陌生人我也会慢慢试着表现自己。现在的我也敢于尝试很多东西,因为我不想再错过什么而让自己未来会后悔的事。重遇她,我才发现自己要努力成为一个优秀的人。不管以后怎样,我都希望笨笨的她能得到幸福,每天开开心心,她的学生不要欺负她,惹她生气。虽然昨天才是教师节,可能昨天赶不出文章是一个缩影,但是我也想给她一个迟来的祝福,教师节快乐!

上一篇下一篇

猜你喜欢

热点阅读