Silverlight自定义控件开发:温度计

时间:2021-07-17 14:52:48

由于在实际项目中需要实时显示采集到的空气温湿度,土壤温湿度值,需要用比较显眼并且清楚明了的方式来展示,这里我们准备采用温度计的方式来进行。一方面是因为大家都熟悉这个,知道怎么去看;同时,温度计本身也比较好封装。以下就是封装好的效果及其调用代码(水银柱和刻度线都是有动画效果的,看上去比较逼真):

Silverlight自定义控件开发:温度计

调用代码如下:

   1:              var data = new DataNotify();
   2:              data.MaxData = 30;
   3:              data.MinData = -15;
   4:   
   5:              data.MinRange = -15;
   6:              data.MaxRange = 75;
   7:   
   8:              data.CurrentData = 40;
   9:   
  10:              data.Title = "空气温度";
  11:              data.Unit = "℃";
  12:              data.ThemeSet = Theme.Red;
  13:   
  14:              var uc = new TemperatureControl(data);
  15:              uc.Margin = new Thickness(-620, 0, 10, 10);
  16:              Test.Children.Add(uc);
  17:   
  18:   
  19:              var data1 = new DataNotify();
  20:              data1.MaxData = 100;
  21:              data1.MinData = 0;
  22:              data1.MinRange = 0;
  23:              data1.MaxRange = 100;
  24:              data1.CurrentData = 83;
  25:              data1.Title = "空气湿度";
  26:              data1.Unit = "%";
  27:              data1.ThemeSet = Theme.Blue;
  28:   
  29:              var uc1 = new TemperatureControl(data1);
  30:              uc1.Margin = new Thickness(-207, 0, 10, 10);
  31:              Test.Children.Add(uc1);
  32:   
  33:              var data2 = new DataNotify();
  34:              data2.MaxData = 60;
  35:              data2.MinData = -10;
  36:              data2.MinRange = -10;
  37:              data2.MaxRange = 100;
  38:              data2.CurrentData = 36;
  39:              data2.Title = "土壤温度";
  40:              data2.Unit = "℃";
  41:              data2.ThemeSet = Theme.Orange;
  42:   
  43:              var uc2 = new TemperatureControl(data2);
  44:              uc2.Margin = new Thickness(213, 0, 10, 10);
  45:              Test.Children.Add(uc2);
  46:   
  47:              var data3 = new DataNotify();
  48:              data3.MaxData = 60;
  49:              data3.MinData = -10;
  50:              data3.MinRange = -10;
  51:              data3.MaxRange = 100;
  52:              data3.CurrentData = 36;
  53:              data3.Title = "土壤湿度";
  54:              data3.Unit = "%";
  55:              data3.ThemeSet = Theme.Mo;
  56:   
  57:              var uc3 = new TemperatureControl(data3);
  58:              uc3.Margin = new Thickness(633, 0, 10, 10);
  59:              Test.Children.Add(uc3);

由于调用代码相当简单,我就不再这里赘述了,下面着重讲解其实现方式。

首先在Silverlight项目中创建一个用户控件,我们命名为“TemperatureControl.xaml”,然后创建一个“ResData.xaml”资源文件项目,用于放置样式定义。

然后开始对项目进行布局,组织xaml代码,得到的页面显示如下:

Silverlight自定义控件开发:温度计

XAML文件代码如下:

   1:  <UserControl
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      x:Class="TinyFrame.Silverlight.TemperatureControl"
   8:      Loaded="UserControl_Loaded"
   9:      Width="200" Height="227">
  10:   
  11:      <Border Style="{StaticResource GridBorder}">
  12:          <Grid x:Name="LayoutRoot" Background="White">
  13:              <Grid.RowDefinitions>
  14:              <RowDefinition Height="30"/>
  15:              <RowDefinition/>
  16:              </Grid.RowDefinitions>
  17:              <Border Grid.Row="0" Style="{StaticResource TopBorder}">
  18:                  <TextBlock Style="{StaticResource TxtTitle}" Text="{Binding Title}" />
  19:              </Border>
  20:              <Border Grid.Row="1">
  21:                  <Grid>
  22:                      <Grid.ColumnDefinitions>
  23:                          <ColumnDefinition Width="40"/>
  24:                          <ColumnDefinition/>
  25:                          <ColumnDefinition/>
  26:                      </Grid.ColumnDefinitions>
  27:                      <Border Name="bRange" Style="{StaticResource RangeBorder}" BorderBrush="{Binding LineColor}"  VerticalAlignment="Bottom">
  28:                      <Grid>
  29:                          <Grid.RowDefinitions>
  30:                              <RowDefinition/>
  31:                              <RowDefinition/>
  32:                          </Grid.RowDefinitions>
  33:                              <TextBlock Grid.Row="0" Style="{StaticResource TxtCommon}" Name="txtMax" Foreground="{Binding lineColor}" VerticalAlignment="Top" ></TextBlock>
  34:                              <TextBlock Grid.Row="1" Style="{StaticResource TxtCommon}" Name="txtMin" Foreground="{Binding lineColor}" VerticalAlignment="Bottom"></TextBlock>
  35:                      </Grid>
  36:                      </Border>
  37:                      <Image Margin="-14,18,0,0" Source="{Binding BgImg}"  Grid.Column="1"/>
  38:                      <Border Grid.Column="2">
  39:                      <Grid>
  40:                      <TextBlock Margin="20,40,6,0" Text="{Binding CurrentData}" Foreground="{Binding IsOK}" Style="{StaticResource TxtValue}" ></TextBlock>
  41:                      <TextBlock Margin="20,65,6,0" Text="{Binding MaxData}"  Style="{StaticResource TxtValue}"></TextBlock>
  42:                      <TextBlock Margin="20,90,6,0" Text="{Binding MinData}"  Style="{StaticResource TxtValue}"></TextBlock>
  43:                      <TextBlock Style="{StaticResource TxtIntro}" Text="当前值:" Margin="-20,40,0,0" Name="i1"  />
  44:                      <TextBlock Style="{StaticResource TxtIntro}" Text="最大值:" Margin="-20,65,0,0"  Name="i2" />
  45:                      <TextBlock Style="{StaticResource TxtIntro}" Text="最小值:" Margin="-20,90,0,0"  Name="i3" />
  46:                          </Grid>
  47:                      </Border>
  48:                      <Border Style="{StaticResource BorderUnit}" Grid.Column="1" Margin="22,24,0,0">
  49:                      <TextBlock   Name="t" Text="{Binding Unit}"  />
  50:                      </Border>
  51:                      <Image x:Name="bStep" Style="{StaticResource ImgStep}" Source="{Binding BgStep}" Margin="11,0,0,27" Grid.Column="1" />
  52:                      <Canvas Height="120" Background="White" HorizontalAlignment="Left" Margin="24,47,0,0" Name="canvas1" VerticalAlignment="Top" Width="32" OpacityMask="Black" Grid.Column="1" />
  53:                  </Grid>
  54:              </Border>
  55:          </Grid>
  56:      </Border>
  57:  </UserControl>

通过第一副图的对比,我们能轻易的知道上图中那些空白的位置放什么东西。

由于在XAML中我采用了Binding来进行数据绑定,那么就展示下我用于数据绑定的类:

   1:   public class DataNotify : INotifyPropertyChanged
   2:      {
   3:          private double minData = 0;  //最小值
   4:          public double MinData
   5:          {
   6:              get
   7:              {
   8:                  return minData;
   9:              }
  10:              set
  11:              {
  12:                  if (value != minData)
  13:                  {
  14:                      minData = value;
  15:                      Notify("MinData");
  16:                  }
  17:              }
  18:          }
  19:   
  20:          private double maxData = 1;  //最大值
  21:          public double MaxData
  22:          {
  23:              get
  24:              {
  25:                  return maxData;
  26:              }
  27:              set
  28:              {
  29:                  if (value != maxData)
  30:                  {
  31:                      maxData = value;
  32:                      Notify("MaxData");
  33:                  }
  34:              }
  35:          }
  36:   
  37:          private double minRange = 0;  //刻度最小范围
  38:          public double MinRange
  39:          {
  40:              get { return minRange; }
  41:              set
  42:              {
  43:                  if (minRange != value)
  44:                  {
  45:                      minRange = value;
  46:                      Notify("MinRange");
  47:                  }
  48:              }
  49:          }
  50:   
  51:          private double maxRange = 90; //刻度最大范围
  52:          public double MaxRange
  53:          {
  54:              get { return maxRange; }
  55:              set
  56:              {
  57:                  if (maxRange != value)
  58:                  {
  59:                      maxRange = value;
  60:                      Notify("MaxRange");
  61:                  }
  62:              }
  63:          }
  64:   
  65:          private double currentData;  //当前值
  66:          public double CurrentData
  67:          {
  68:              get
  69:              {
  70:                  return currentData;
  71:              }
  72:              set
  73:              {
  74:                  if (value != currentData)
  75:                  {
  76:                      currentData = value;
  77:                      Notify("CurrentData");
  78:                  }
  79:              }
  80:          }
  81:   
  82:          private Brush isOK;   //指标是否正常
  83:          public Brush IsOK
  84:          {
  85:              get
  86:              {
  87:                  if (currentData >= minData && currentData <= maxData)
  88:                      return new SolidColorBrush(Colors.Black);
  89:                  else
  90:                      return new SolidColorBrush(Colors.Red);
  91:              }
  92:          }
  93:   
  94:          private string title;  //标题
  95:          public string Title
  96:          {
  97:              get
  98:              {
  99:                  return title;
 100:              }
 101:              set
 102:              {
 103:                  if (value != title)
 104:                  {
 105:                      title = value;
 106:                      Notify("Title");
 107:                  }
 108:              }
 109:          }
 110:   
 111:          private string unit;   //单位
 112:          public string Unit
 113:          {
 114:              get
 115:              {
 116:                  return unit;
 117:   
 118:              }
 119:              set
 120:              {
 121:                  if (value != unit)
 122:                  {
 123:                      unit = value;
 124:                      Notify("Unit");
 125:                  }
 126:              }
 127:          }
 128:   
 129:          private Theme themeSet;
 130:          public Theme ThemeSet
 131:          {
 132:              get
 133:              {
 134:                  return themeSet;
 135:              }
 136:              set
 137:              {
 138:                  if (value != themeSet)
 139:                  {
 140:                      themeSet = value;
 141:                      Notify("ThemeSet");
 142:                  }
 143:              }
 144:          }
 145:   
 146:          private string bgImg;  //背景默认值
 147:          public string BgImg
 148:          {
 149:              get
 150:              {
 151:                  return bgImg;
 152:              }
 153:              set
 154:              {
 155:                  if (value != bgImg)
 156:                  {
 157:                      bgImg = value;
 158:                      Notify("BgImg");
 159:                  }
 160:              }
 161:          }
 162:   
 163:          private string bgStep;  //水银柱块默认值
 164:          public string BgStep
 165:          {
 166:              get
 167:              {
 168:                  return bgStep;
 169:              }
 170:              set
 171:              {
 172:                  if (value != bgStep)
 173:                  {
 174:                      bgStep = value;
 175:                      Notify("BgStep");
 176:                  }
 177:              }
 178:          }
 179:   
 180:          private string lineColor;  //刻度范围条的颜色
 181:          public string LineColor
 182:          {
 183:              get
 184:              {
 185:                  return lineColor;
 186:              }
 187:              set
 188:              {
 189:                  if (lineColor != value)
 190:                  {
 191:                      lineColor = value;
 192:                      Notify("LineColor");
 193:                  }
 194:              }
 195:          }
 196:   
 197:          public event PropertyChangedEventHandler PropertyChanged;
 198:   
 199:          public void Notify(string propertyName)
 200:          {
 201:              if (PropertyChanged != null)
 202:                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 203:          }
 204:      }

上面的代码中,封装了温度计的所有需要的属性,包括刻度值,最大最小范围值,当前值,主题,标题,指标正常与否等。

最后就是具体的事件组织方法:

   1:   public partial class TemperatureControl : UserControl
   2:      {
   3:          public TemperatureControl(DataNotify dataNotify)
   4:          {
   5:              InitializeComponent();
   6:              this.dataNotify = dataNotify;
   7:   
   8:              //绑定数据上下文
   9:              this.DataContext = dataNotify;
  10:   
  11:              //左侧刻度线的最大值最小值
  12:              double tempMinData = dataNotify.MinData;
  13:              if (timer == null)
  14:                  timer = new DispatcherTimer();
  15:              timer.Interval = new TimeSpan(100);
  16:              timer.Tick += (sender, e) =>
  17:              {
  18:                  tempMinData++;
  19:                  bRange.Dispatcher.BeginInvoke((Action)(() =>
  20:                  {
  21:                      bRange.Margin = GetThicknessByMaxMin();
  22:                      bRange.Height = (tempMinData - dataNotify.MinData) * THeight / (dataNotify.MaxRange - dataNotify.MinRange);
  23:                      txtMax.Text = tempMinData.ToString("0.0");
  24:                      txtMin.Text = dataNotify.MinData.ToString("0.0");
  25:                  }));
  26:                  if (tempMinData == dataNotify.MaxData)
  27:                      timer.Stop();
  28:              };
  29:              timer.Start();
  30:   
  31:              //当前值的显示
  32:              double tempMinRange = dataNotify.MinRange;
  33:              if (timerStep == null)
  34:                  timerStep = new DispatcherTimer();
  35:              timerStep.Interval = new TimeSpan(100);
  36:              timerStep.Tick += (sender, e) =>
  37:              {
  38:                  tempMinRange++;
  39:                  double value;
  40:                  if (dataNotify.MinRange < 0)
  41:                      value = (THeight / (dataNotify.MaxRange - dataNotify.MinRange)) * tempMinRange + (Math.Abs(dataNotify.MinRange)) * (120 / (dataNotify.MaxRange - dataNotify.MinRange));
  42:                  else
  43:                      value = (THeight / (dataNotify.MaxRange - dataNotify.MinRange)) * tempMinRange;
  44:                  if (value < 0)
  45:                      value = 0;
  46:                  bStep.Height = value;
  47:                  if (Math.Abs(tempMinRange - dataNotify.CurrentData) < 0.6)
  48:                  {
  49:                      timerStep.Stop();
  50:                  }
  51:              };
  52:              timerStep.Start();
  53:          }
  54:   
  55:          private readonly DispatcherTimer timer = null;
  56:          private readonly DispatcherTimer timerStep = null;
  57:          public DataNotify dataNotify;
  58:   
  59:          private const int THeight = 120;   //水银柱高度为120
  60:          private const int FHeight = 30;    //水银球高度为30
  61:   
  62:          //动态设置水银柱的Margin属性,以便于适应 带有正负值的场景
  63:          private Thickness GetThicknessByMaxMin()
  64:          {
  65:              double range = dataNotify.MaxRange - dataNotify.MinRange;
  66:              if (dataNotify.MinRange < 0)
  67:                  return new Thickness(0, 0, 0, FHeight + Math.Abs(dataNotify.MinRange)*THeight/range + (dataNotify.MinData) * THeight / range);
  68:              else
  69:                  return new Thickness(0, 0, 0, FHeight + (dataNotify.MinData) * THeight / range);
  70:          }
  71:   
  72:          private void UserControl_Loaded(object sender, RoutedEventArgs e)
  73:          {
  74:              //设置温度计的主题
  75:              switch (dataNotify.ThemeSet)
  76:              {
  77:                  case Theme.Red:
  78:                      dataNotify.BgImg = "Image/tem-red.png";
  79:                      dataNotify.BgStep = "Image/step-red.png";
  80:                      dataNotify.LineColor = "Red";
  81:                      break;
  82:                  case Theme.Blue:
  83:                      dataNotify.BgImg = "Image/tem-blue.png";
  84:                      dataNotify.BgStep = "Image/step-blue.png";
  85:                      dataNotify.LineColor = "Blue";
  86:                      break;
  87:   
  88:                  case Theme.Mo:
  89:                      dataNotify.BgImg = "Image/tem-mo.png";
  90:                      dataNotify.BgStep = "Image/step-mo.png";
  91:                      dataNotify.LineColor = "#00ACAE";
  92:                      break;
  93:   
  94:                  case Theme.Yellow:
  95:                      dataNotify.BgImg = "Image/tem-yellow.png";
  96:                      dataNotify.BgStep = "Image/step-yellow.png";
  97:                      dataNotify.LineColor = "#849C00";
  98:                      break;
  99:   
 100:                  case Theme.Orange:
 101:                      dataNotify.BgImg = "Image/tem-orange.png";
 102:                      dataNotify.BgStep = "Image/step-orange.png";
 103:                      dataNotify.LineColor = "#C88600";
 104:                      break;
 105:   
 106:                  case Theme.Green:
 107:                      dataNotify.BgImg = "Image/tem-green.png";
 108:                      dataNotify.BgStep = "Image/step-green.png";
 109:                      dataNotify.LineColor = "#178A00";
 110:                      break;
 111:   
 112:                  default:
 113:                      dataNotify.BgImg = "Image/tem-red.png";
 114:                      dataNotify.BgStep = "Image/step-red.png";
 115:                      dataNotify.LineColor = "Red";
 116:                      break;
 117:              }
 118:   
 119:              Draw(dataNotify.MinRange,dataNotify.MaxRange);
 120:          }
 121:   
 122:          //根据用户输入的最大刻度值,最小刻度值,来划刻度线
 123:          private void Draw(double minRange,double maxRange)
 124:          {
 125:              var step = (maxRange - minRange) / 120;
 126:              Line redLine = new Line();
 127:              redLine.X1 = 0;
 128:              redLine.Y1 = 0;
 129:              redLine.X2 = 0;
 130:              redLine.Y2 = 120;
 131:              redLine.StrokeThickness = 1;
 132:              redLine.Stroke = new SolidColorBrush(Colors.Black);
 133:              canvas1.Children.Add(redLine);
 134:              int j = 6;
 135:              for (int i = 0; i <= 6; i++)
 136:              {
 137:                  Line line = new Line();
 138:                  line.X1 = 0;
 139:                  line.Y1 = i * 20;
 140:   
 141:                  line.X2 =10;
 142:                  line.Y2 = i * 20;
 143:   
 144:                  TextBlock tb = new TextBlock();
 145:                  tb.Margin = new Thickness(12,i*20-8,0,0);
 146:                  tb.FontSize = 9;
 147:                  tb.Text = (((maxRange - minRange) / 6) * j + minRange).ToString("0");
 148:   
 149:                  line.StrokeThickness = 1;
 150:                  line.Stroke = new SolidColorBrush(Colors.Black);
 151:                  canvas1.Children.Add(line);
 152:                  canvas1.Children.Add(tb);
 153:                  for (int x = 0; x < 30; x++)
 154:                  {
 155:                      Line lineInner = new Line();
 156:                      lineInner.X1 = 0;
 157:                      lineInner.Y1 = (x + 1) * 4;
 158:   
 159:                      lineInner.X2 = 6;
 160:                      lineInner.Y2 = (x + 1) * 4;
 161:   
 162:                      lineInner.StrokeThickness = 1;
 163:                      lineInner.Stroke = new SolidColorBrush(Colors.Black);
 164:                      canvas1.Children.Add(lineInner);
 165:                  }
 166:   
 167:                  j--;
 168:              }
 169:          }
 170:   
 171:      }

上面我加了一部分注释,解释的比较清楚了。由于写这个温度计的时候,我们时刻需要测量好屏幕上的水银柱高度和当前值的对应关系,所以里面有比较多的运算,具体的运算方式还希望能够自己推敲。代码我将在下一篇中附上。

Silverlight自定义控件开发:温度计的更多相关文章

  1. Silverlight自定义控件开发:仪表盘

    在项目中,由于使用到了活动积温运算,也就是指当日平均气温稳定上升到10℃以上时,大多数农作物才能活跃生长.把大于等于10℃持续期内的日平均气温累加起来,得到的气温总和,叫做活动积温.所以我决定采用do ...

  2. iOS 自定义控件开发(中)

    <iOS 自定义控件开发(上)> <iOS 自定义控件开发(中)> 接上篇iOS自定义控件开发之后,我们尝试另外一种. 在Xcode的右边,会看到如下的图 其中,上面有一个:C ...

  3. iOS 自定义控件开发(上)

    工作需要,最近在进行iOS方面的图表工作.找了很多第三方库都无法实现效果,所以决定自己写一个控件. <iOS 自定义控件开发(上)> <iOS 自定义控件开发(中)> #0 目 ...

  4. C&num;自定义控件开发

    自定义控件开发 一般而言,Visual Studio 2005中自带的几十种控件已经足够我们使用了,但是,在一些特殊的需求中,可能需要一些特殊的控件来与用户进行交互,这时,就需要我们自己开发新的.满足 ...

  5. 自定义控件开发的调试及DesignMode的状态处理

    在开发Winform程序的时候,我们往往需要根据需要做一些自定义的控件模块,这样可以给系统模块重复利用,或者实现更好的效果等功能.但在使用的时候,我们又往往设计时刻发现一些莫名其妙的错误,那么我们该如 ...

  6. ZLComboBox自定义控件开发详解

    [引言]距离上一回写博客已经有一些时日了,之前的爱莲iLinkIT系列主要是讲解了如何用NodeJS来实现一个简单的“文件传送”软件,属于JavaScript中在服务器端的应用. 今天,我们就回归到J ...

  7. &period;net的自定义JS控件,运用了 面向对象的思想 封装 了 控件&lpar;&period;net自定义控件开发的第一天&rpar;

    大家好!我叫刘晶,很高兴你能看到我分享的文章!希望能对你有帮助! 首先我们来看下几个例子 ,就能看到 如何 自定义控件! 业务需求: 制作  一个   属于 自己的    按钮 对象    ,然后 像 ...

  8. TemplateBinding与Binding区别,以及WPF自定义控件开发的遭遇

    在上一次的文章WPF OnApplyTemplate 不执行 或者执行滞后的疑惑谈到怎么正确的开发自定义控件,我们控件的样式中,属性的绑定一般都是用TemplateBinding来完成,如下一个基本的 ...

  9. (WPF&amp&semi;Silverlight)silverlight自定义控件

    2个半小时弄懂了自定义控件是怎么回事儿. 在silverlight中创建一个UserControl,把上面sliderbar的外观和功能都封装在里面. 以自定义控件mapslider控件为例: 1.首 ...

随机推荐

  1. throw er&semi; &sol;&sol; Unhandled &&num;39&semi;error&&num;39&semi; event

    events.js:72 throw er; // Unhandled 'error' event ^Error: listen EADDRINUSE at errnoException (net.j ...

  2. Asp&period;net mvc 各个组件的分离

    1. 系统常见的分层 在开发asp.net mvc应用的时候,visual studio 给我们创建了默认的文档结构,一般情况下我们在一个项目下工作,参考微软的官方例子:ContosoUniversi ...

  3. ASP&period;NET四则运算--策略模式

    在ASP.NET中实现四则运算,同样使用了类的封装,以及策略模式.只不过是把封装的类.前台代码以及后台的代码分离开来,但同样是要达到功能的实现. Calculator.cs using System; ...

  4. 关于Javascript函数的几点笔记

    函数本质上是一个有名字的程序块,程序块使得多条语句可以一起执行. 变量类型: 1.复杂类型:Object.Array等. 2.原始类型:String.Integer等. 函数参数: 1.复杂类型:传递 ...

  5. Nodejs in Visual Studio Code 07&period;学习Oracle

    1.开始 Node.js:https://nodejs.org OracleDB: https://github.com/oracle/node-oracledb/blob/master/INSTAL ...

  6. python之元组

    一.元组定义 元组(tuple)使用小括号()来定义,它是不可变数据类型,它的元素不可变,但元素的元素是可变的,比如tuple中一个元素为list列表时,list中的元素是可变的 # 在定义tuple ...

  7. jsp笔记----jsp常用的的获取项目的根路径

    <% String path = request.getContextPath(); String basePath = request.getScheme() + "://&quot ...

  8. 转: Laravel 自定义公共函数的引入

    来源:Laravel 自定义公共函数的引入 背景习惯了 使用 ThinkPHP 框架,有一个公共方法类在代码编写上会快捷很多,所以有必要在此进行配置一番.测试框架:Laravel 5.5步骤指导1. ...

  9. Docker 快速验证 HTML 导出 PDF 高效方案

    需求分析 项目中用到了 Echarts,想要把图文混排,当然包括 echarts 生成的 Canvas 图也导出 PDF. 设计和实现时,分析了 POI.iText.freemaker.world 的 ...

  10. Spring 注解原理(一)组件注册

    Spring 注解原理(一)组件注册 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 当我们需要使用 Spring 提供的 ...