响应式网站建设公司,学校网站建设问卷调查表,网址大全4399,有专门做英文字幕的网站吗Go的反射有哪些应用#xff1f; 
IDE中代码的自动补全对象序列化fmt函数的相关实现ORM框架 
什么情况下需要使用反射#xff1f; 
不能明确函数调用哪个接口#xff0c;需要根据传入的参数在运行时决定。不能明确传入函数的参数类型#xff0c;需要在运行时处理任意对象。 …Go的反射有哪些应用 
IDE中代码的自动补全对象序列化fmt函数的相关实现ORM框架 
什么情况下需要使用反射 
不能明确函数调用哪个接口需要根据传入的参数在运行时决定。不能明确传入函数的参数类型需要在运行时处理任意对象。 反射对性能有消耗而且可读性低能不用就不要用反射。 如何比较两个对象完全相同 
Go中提供了一个函数可以实现这个功能 
func DeepEqual(x, y interface{}) boolDeepEqual 函数的参数是两个 interface实际上也就是可以输入任意类型输出 true 或者 flase 表示输入的两个变量是否是“深度”相等。 
先明白一点如果是不同的类型即使是底层类型相同相应的值也相同那么两者也不是“深度”相等。 
例如这个代码 
type MyInt int
type YourInt intfunc main() {m : MyInt(1)y : YourInt(1)fmt.Println(reflect.DeepEqual(m, y))
}这个代码的结果是false。 
上面的代码中m, y 底层都是 int而且值都是 1但是两者静态类型不同前者是 MyInt后者是 YourInt因此两者不是“深度”相等。 
来看一下源码 
func DeepEqual(x, y any) bool {if x  nil || y  nil {return x  y}v1 : ValueOf(x)v2 : ValueOf(y)if v1.Type() ! v2.Type() {return false}return deepValueEqual(v1, v2, make(map[visit]bool))
}首先查看两者是否有一个是 nil 的情况这种情况下只有两者都是 nil函数才会返回 true 
接着使用反射获取xy 的反射对象并且立即比较两者的类型根据前面的内容这里实际上是动态类型如果类型不同直接返回 false。 
最后最核心的内容在子函数 deepValueEqual 中。 
然后我们来看一下deepValueEqual的源码 
// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {if !v1.IsValid() || !v2.IsValid() {return v1.IsValid()  v2.IsValid()}if v1.Type() ! v2.Type() {return false}// We want to avoid putting more in the visited map than we need to.// For any possible reference cycle that might be encountered,// hard(v1, v2) needs to return true for at least one of the types in the cycle,// and its safe and valid to get Values internal pointer.hard : func(v1, v2 Value) bool {switch v1.Kind() {case Pointer:if v1.typ.ptrdata  0 {// go:notinheap pointers cant be cyclic.// At least, all of our current uses of go:notinheap have// that property. The runtime ones arent cyclic (and we dont use// DeepEqual on them anyway), and the cgo-generated ones are// all empty structs.return false}fallthroughcase Map, Slice, Interface:// Nil pointers cannot be cyclic. Avoid putting them in the visited map.return !v1.IsNil()  !v2.IsNil()}return false}if hard(v1, v2) {// For a Pointer or Map value, we need to check flagIndir,// which we do by calling the pointer method.// For Slice or Interface, flagIndir is always set,// and using v.ptr suffices.ptrval : func(v Value) unsafe.Pointer {switch v.Kind() {case Pointer, Map:return v.pointer()default:return v.ptr}}addr1 : ptrval(v1)addr2 : ptrval(v2)if uintptr(addr1)  uintptr(addr2) {// Canonicalize order to reduce number of entries in visited.// Assumes non-moving garbage collector.addr1, addr2  addr2, addr1}// Short circuit if references are already seen.typ : v1.Type()v : visit{addr1, addr2, typ}if visited[v] {return true}// Remember for later.visited[v]  true}switch v1.Kind() {case Array:for i : 0; i  v1.Len(); i {if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {return false}}return truecase Slice:if v1.IsNil() ! v2.IsNil() {return false}if v1.Len() ! v2.Len() {return false}if v1.UnsafePointer()  v2.UnsafePointer() {return true}// Special case for []byte, which is common.if v1.Type().Elem().Kind()  Uint8 {return bytealg.Equal(v1.Bytes(), v2.Bytes())}for i : 0; i  v1.Len(); i {if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {return false}}return truecase Interface:if v1.IsNil() || v2.IsNil() {return v1.IsNil()  v2.IsNil()}return deepValueEqual(v1.Elem(), v2.Elem(), visited)case Pointer:if v1.UnsafePointer()  v2.UnsafePointer() {return true}return deepValueEqual(v1.Elem(), v2.Elem(), visited)case Struct:for i, n : 0, v1.NumField(); i  n; i {if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {return false}}return truecase Map:if v1.IsNil() ! v2.IsNil() {return false}if v1.Len() ! v2.Len() {return false}if v1.UnsafePointer()  v2.UnsafePointer() {return true}for _, k : range v1.MapKeys() {val1 : v1.MapIndex(k)val2 : v2.MapIndex(k)if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {return false}}return truecase Func:if v1.IsNil()  v2.IsNil() {return true}// Cant do better than this:return falsecase Int, Int8, Int16, Int32, Int64:return v1.Int()  v2.Int()case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:return v1.Uint()  v2.Uint()case String:return v1.String()  v2.String()case Bool:return v1.Bool()  v2.Bool()case Float32, Float64:return v1.Float()  v2.Float()case Complex64, Complex128:return v1.Complex()  v2.Complex()default:// Normal equality sufficesreturn valueInterface(v1, false)  valueInterface(v2, false)}
}这个代码的思路很清晰就是分别递归调用deepValueEqual函数一直递归到最进本的数据类型比较int, string等可以直接得出true或者false再一层层的返回最终得到深度相等的比较结果。 
Go语言是如何实现反射的 
interface它是 Go 语言实现抽象的一个非常强大的工具。当向接口变量赋予一个实体类型的时候接口会存储实体的类型信息反射就是通过接口的类型信息实现的反射建立在类型的基础上。 
Go 语言在 reflect 包里定义了各种类型实现了反射的各种函数通过它们可以在运行时检测类型的信息、改变类型的值。 
type和interface 
我们需要先介绍一下什么叫做静态类型什么叫做动态类型。 
静态类型 
所谓的静态类型即 static type就是变量声明的时候的类型。 
var age int   // int 是静态类型
var name string  // string 也是静态类型它是你在编码时肉眼可见的类型。 
动态类型 
所谓的 动态类型即 concrete type也叫具体类型是 程序运行时系统才能看见的类型。 
这是什么意思呢 
我们都知道 空接口 可以承接什么问题类型的值什么 int 呀string 呀都可以接收。 
比如下面这几行代码 
var i interface{}   i  18  
i  Go编程时光  第一行我们在给 i 声明了 interface{} 类型所以 i 的静态类型就是 interface{} 
第二行当我们给变量 i 赋一个 int 类型的值时它的静态类型还是 interface{}这是不会变的但是它的动态类型此时变成了 int 类型。 
第三行当我们给变量 i 赋一个 string 类型的值时它的静态类型还是 interface{}它还是不会变但是它的动态类型此时又变成了 string 类型。 
从以上可以知道不管是 i18 还是 iGo编程时光都是当程序运行到这里时变量的类型才发生了改变这就是我们最开始所说的 动态类型是程序运行时系统才能看见的类型。 
Go 语言中每个变量都有一个静态类型在编译阶段就确定了的比如 int, float64, []int 等等。注意这个类型是声明时候的类型不是底层数据类型。 
Go官方的博客里面就举过一个例子 
type MyInt int 
var i int 
var j MyInt尽管 ij 的底层类型都是 int但我们知道他们是不同的静态类型除非进行类型转换否则i 和 j 不能同时出现在等号两侧。j 的静态类型就是 MyInt。 
反射跟interface{}的关系十分密切因此我们需要先学习一下interface{}的原理。 
interface{} 
非空interface{} 
type iface struct {tab  *itabdata unsafe.Pointer
}type itab struct {inter *interfacetype_type *_typehash  uint32 // copy of _type.hash. Used for type switches._     [4]bytefun   [1]uintptr // variable sized. fun[0]0 means _type does not implement inter.
}type interfacetype struct {typ     _typepkgpath namemhdr    []imethod
}type _type struct {size       uintptrptrdata    uintptr // size of memory prefix holding all pointershash       uint32tflag      tflagalign      uint8fieldAlign uint8kind       uint8// function for comparing objects of this type// (ptr to object A, ptr to object B) - ?equal func(unsafe.Pointer, unsafe.Pointer) bool// gcdata stores the GC type data for the garbage collector.// If the KindGCProg bit is set in kind, gcdata is a GC program.// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.gcdata    *bytestr       nameOffptrToThis typeOff
}itab主要由具体类型_type和结构类型interfacetype组成。 
我们可以用一张图来理顺中间的关系 空interface{} 
type eface struct {_type *_typedata  unsafe.Pointer
}相比 ifaceeface 就比较简单了。只维护了一个 _type 字段表示空接口所承载的具体的实体类型。data 描述了具体的值。 接口变量可以存储任何实现了接口定义的所有方法的变量。 Go中常见的接口Reader和Writer接口 
type Reader interface {Read(p []byte) (n int, err error)
}type Writer interface {Write(p []byte) (n int, err error)
}var r io.Reader
tty, err : os.OpenFile(./, os.O_RDWR, 0)
if err ! nil {return nil, err
}
r  tty首先声明 r 的类型是 io.Reader注意这是 r 的静态类型此时它的动态类型为 nil并且它的动态值也是 nil。 
之后r  tty 这一语句将 r 的动态类型变成 *os.File动态值则变成非空表示打开的文件对象。这时r 可以用value, type对来表示为 tty, *os.File。 注意看上图此时虽然 fun 所指向的函数只有一个 Read 函数其实 *os.File 还包含 Write 函数也就是说 *os.File 其实还实现了 io.Writer 接口。因此下面的断言语句可以执行 
var w io.Writer
w  r.(io.Writer)之所以用断言而不能直接赋值是因为 r 的静态类型是 io.Reader并没有实现 io.Writer 接口。断言能否成功看 r 的动态类型是否符合要求。 
这样w 也可以表示成 tty, *os.File仅管它和 r 一样但是 w 可调用的函数取决于它的静态类型 io.Writer也就是说它只能有这样的调用形式 w.Write() 。w 的内存形式如下图 最后我们再来一个赋值 
var empty interface{}
empty  w由于 empty 是一个空接口因此所有的类型都实现了它w 可以直接赋给它不需要执行断言操作。 从上面的三张图可以看到interface包含三部分的信息_type是类型信息*data指向实际类型的实际值itab包含实际类型的信息包括大小包路径还包含绑定在类型上的各种方法。 
反射的基本函数 
reflect包里面定义了一个接口和一个结构体reflect.Type是一个接口reflect.Value是一个结构体它们提供很多函数来存储在接口里面的类型信息。 
reflect.Type 主要提供关于类型相关的信息所以它和 _type 关联比较紧密reflect.Value 则结合 _type 和 data 两者因此程序员可以获取甚至改变类型的值。 
reflect包中提供了两个基础的关于反射的函数来获取上述的接口和结构体 
func TypeOf(i interface{}) Type 
func ValueOf(i interface{}) ValueTypeOf函数用来提取一个接口中值的类型信息。由于它的输入参数是一个空的interface{}调用这个函数的时候实参会先被转化为interface{}类型。这样实参的类型信息方法集值信息都存储到interface{}变量里了。 
func TypeOf(i interface{}) Type {eface : *(*emptyInterface)(unsafe.Pointer(i))return toType(eface.typ)
}这里的 emptyInterface 和上面提到的 eface 是一回事字段名略有差异字段是相同的并且在不同的源码包前者在 reflect 包后者在 runtime 包。 eface.typ 就是动态类型。 
type emptyInterface struct {typ  *rtypeword unsafe.Pointer
}然后toType函数只是做了一个类型转换而已 
func toType(t *rtype) Type {if t  nil {return nil}return t
}注意看返回值Type实际上是一个接口定义了很多方法用来获取类型的相关的各种信息而*rtype实现了Type接口。 
type Type interface {// Methods applicable to all types.// 此类型的变量对齐后占用的字节数Align() int// 如果是struct自动对齐后占用的字节数FieldAlign() int// 返回类型方法集李的第i(传入的参数)个方法Method(int) Method// 通过名称获取方法MethodByName(string) (Method, bool)// 获取类型方法集里导出的方法个数NumMethod() int// 类型名称Name() string// 返回类型所在的路径如: encoding/base64PkgPath() string// 返回类型的大小和unsafe.Sizeof功能类似Size() uintptr// 返回类型的字符串表示形式String() string// 返回类型的类型值Kind() Kind// 类型是否实现了接口 uImplements(u Type) bool// 是否可以赋值给 uAssignableTo(u Type) bool// 是否可以类型转换成 uConvertibleTo(u Type) bool// 类型是否可以比较Comparable() bool// 类型占据的位数Bits() int// 返回通道的方向只能是chan类型调用ChanDir() ChanDir// 返回类型是否是可变参数只能是func类型调用IsVariadic() bool// 返回内部子元素类型, 只能由类型Array, Chan, Map, Ptr, or Slice调用Elem() Type// 返回结构体类型的第i个字段只能是结构体类型调用// 如果i超过了字段数就会panicField(i int) StructField// 返回嵌套的结构体的字段FieldByIndex(index []int) StructField// 通过字段名获取字段FieldByName(name string) (StructField, bool)// 返回名称符合func函数的字段FieldByNameFunc(match func(string) bool) (StructField, bool)// 获取函数类型的第i个参数的类型In(i int) Type// 返回map的key类型只能由类型map调用Key() Type// 返回Array的长度只能由Array调用Len() int// 返回类型字段的数量只能由类型Struct调用NumField() int// 返回函数类型的输入参数个数NumIn() int// 返回函数类型的返回值个数NumOut() int// 返回函数类型的第i个值的类型Out(i int) Type// 返回类型结构体的相同部分common() *rtype// 返回类型结构体的不同部分uncommon() *uncommonType
}可见Type定义了非常多的方法通过它们可以获取到类型的所有信息。 
注意到Type方法集的倒数第二个方法common返回的rtype类型它和_type是一回事而且源代码里面也注释了两边要保持同步。 
type rtype struct {size       uintptrptrdata    uintptrhash       uint32tflag      tflagalign      uint8fieldAlign uint8kind       uint8alg        *typeAlggcdata     *bytestr        nameOffptrToThis  typeOff
}所有的类型都会包含 rtype 这个字段表示各种类型的公共信息另外不同类型包含自己的一些独特的部分。 
比如下面的 arrayType 和 chanType 都包含 rytpe而前者还包含 slicelen 等和数组相关的信息后者则包含 dir 表示通道方向的信息。 
// arrayType represents a fixed array type.
type arrayType struct {rtype reflect:arrayelem  *rtype // array element typeslice *rtype // slice typelen   uintptr
}// chanType represents a channel type.
type chanType struct {rtype reflect:chanelem  *rtype  // channel element typedir   uintptr // channel direction (ChanDir)
}注意到Type 接口实现了 String() 函数满足 fmt.Stringer 接口因此使用 fmt.Println 打印的时候输出的是 String() 的结果。另外fmt.Printf() 函数如果使用 %T 来作为格式参数输出的是 reflect.TypeOf 的结果也就是动态类型。例如 
fmt.Printf(%T, 3) // intTypeOf函数讲完了我们接下来来看一下ValueOf函数。返回值reflect.Value表示interface{}里面存储的实际变量它能提供实际变量的各种信息。 
源码如下 
func ValueOf(i interface{}) Value {if i  nil {return Value{}}// ……return unpackEface(i)
}// 分解 eface
func unpackEface(i interface{}) Value {e : (*emptyInterface)(unsafe.Pointer(i))t : e.typif t  nil {return Value{}}f : flag(t.Kind())if ifaceIndir(t) {f | flagIndir}return Value{t, e.word, f}
}从源码看比较简单将先将 i 转换成 *emptyInterface 类型 再将它的 typ 字段和 word 字段以及一个标志位字段组装成一个 Value 结构体而这就是 ValueOf 函数的返回值它包含类型结构体指针、真实数据的地址、标志位。 
Value 结构体定义了很多方法通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据 
// 设置切片的 len 字段如果类型不是切片就会panicfunc (v Value) SetLen(n int)// 设置切片的 cap 字段func (v Value) SetCap(n int)// 设置字典的 kvfunc (v Value) SetMapIndex(key, val Value)// 返回切片、字符串、数组的索引 i 处的值func (v Value) Index(i int) Value// 根据名称获取结构体的内部字段值func (v Value) FieldByName(name string) Value// ……// 用来获取 int 类型的值
func (v Value) Int() int64// 用来获取结构体字段成员数量
func (v Value) NumField() int// 尝试向通道发送数据不会阻塞
func (v Value) TrySend(x reflect.Value) bool// 通过参数列表 in 调用 v 值所代表的函数或方法
func (v Value) Call(in []Value) (r []Value) // 调用变参长度可变的函数
func (v Value) CallSlice(in []Value) []Value另外通过 Type() 方法和 Interface() 方法可以打通 interface、Type、Value 三者。Type() 方法也可以返回变量的类型信息与 reflect.TypeOf() 函数等价。Interface() 方法可以将 Value 还原成原来的 interface。 总结一下TypeOf() 函数返回一个接口这个接口定义了一系列方法利用这些方法可以获取关于类型的所有信息 ValueOf() 函数返回一个结构体变量包含类型信息以及实际值。 上图中rtye 实现了 Type 接口是所有类型的公共部分。emptyface 结构体和 eface 其实是一个东西而 rtype 其实和 _type 是一个东西只是一些字段稍微有点差别比如 emptyface 的 word 字段和 eface 的 data 字段名称不同但是数据型是一样的。 
反射的三大的定律 
根据 Go 官方关于反射的博客反射有三大定律 Reflection goes from interface value to reflection object.Reflection goes from reflection object to interface value.To modify a reflection object, the value must be settable. 第一条是最基本的反射是一种检测存储在 interface 中的类型和值机制。这可以通过 TypeOf 函数和 ValueOf 函数得到。 
第二条实际上和第一条是相反的机制它将 ValueOf 的返回值通过 Interface() 函数反向转变成 interface 变量。 
前两条就是说 接口型变量 和 反射类型对象 可以相互转化反射类型对象实际上就是指的前面说的 reflect.Type 和 reflect.Value。 
第三条不太好懂如果需要操作一个反射变量那么它必须是可设置的。反射变量可设置的本质是它存储了原变量本身这样对反射变量的操作就会反映到原变量本身反之如果反射变量不能代表原变量那么操作了反射变量不会对原变量产生任何影响这会给使用者带来疑惑。所以第二种情况在语言层面是不被允许的。 
举一个经典例子 
var x float64  3.4v : reflect.ValueOf(x)v.SetFloat(7.1) // Error: will panic.执行上面的代码会产生 panic原因是反射变量 v 不能代表 x 本身为什么因为调用 reflect.ValueOf(x) 这一行代码的时候传入的参数在函数内部只是一个拷贝是值传递所以 v 代表的只是 x 的一个拷贝因此对 v 进行操作是被禁止的。 
可设置是反射变量 Value 的一个性质但不是所有的 Value 都是可被设置的。 
就像在一般的函数里那样当我们想改变传入的变量时使用指针就可以解决了。 
var x float64  3.4p : reflect.ValueOf(x)fmt.Println(type of p:, p.Type())fmt.Println(settability of p:, p.CanSet())输出是这样的 
type of p: *float64settability of p: falsep 还不是代表 xp.Elem() 才真正代表 x这样就可以真正操作 x 了 
v : p.Elem()v.SetFloat(7.1)fmt.Println(v.Interface()) // 7.1fmt.Println(x) // 7.1关于第三条记住一句话如果想要操作原变量反射变量 Value 必须要 hold 住原变量的地址才行。 
参考自码神桃花源的博客。 文章转载自: http://www.morning.kwpnx.cn.gov.cn.kwpnx.cn http://www.morning.mjctt.cn.gov.cn.mjctt.cn http://www.morning.kklwz.cn.gov.cn.kklwz.cn http://www.morning.qqhmg.cn.gov.cn.qqhmg.cn http://www.morning.nxzsd.cn.gov.cn.nxzsd.cn http://www.morning.wnkjb.cn.gov.cn.wnkjb.cn http://www.morning.rglzy.cn.gov.cn.rglzy.cn http://www.morning.mygbt.cn.gov.cn.mygbt.cn http://www.morning.ychrn.cn.gov.cn.ychrn.cn http://www.morning.tsyny.cn.gov.cn.tsyny.cn http://www.morning.kdldx.cn.gov.cn.kdldx.cn http://www.morning.mlyq.cn.gov.cn.mlyq.cn http://www.morning.rkxqh.cn.gov.cn.rkxqh.cn http://www.morning.svrud.cn.gov.cn.svrud.cn http://www.morning.knswz.cn.gov.cn.knswz.cn http://www.morning.tzlfc.cn.gov.cn.tzlfc.cn http://www.morning.tkyry.cn.gov.cn.tkyry.cn http://www.morning.wmhqd.cn.gov.cn.wmhqd.cn http://www.morning.tfqfm.cn.gov.cn.tfqfm.cn http://www.morning.bpds.cn.gov.cn.bpds.cn http://www.morning.tnktt.cn.gov.cn.tnktt.cn http://www.morning.splcc.cn.gov.cn.splcc.cn http://www.morning.kstlm.cn.gov.cn.kstlm.cn http://www.morning.jwxmn.cn.gov.cn.jwxmn.cn http://www.morning.srgsb.cn.gov.cn.srgsb.cn http://www.morning.zbjfq.cn.gov.cn.zbjfq.cn http://www.morning.ssgqc.cn.gov.cn.ssgqc.cn http://www.morning.qnpyz.cn.gov.cn.qnpyz.cn http://www.morning.bjsites.com.gov.cn.bjsites.com http://www.morning.mmsf.cn.gov.cn.mmsf.cn http://www.morning.hqbk.cn.gov.cn.hqbk.cn http://www.morning.ktmbr.cn.gov.cn.ktmbr.cn http://www.morning.dmtbs.cn.gov.cn.dmtbs.cn http://www.morning.ykwqz.cn.gov.cn.ykwqz.cn http://www.morning.ltbwq.cn.gov.cn.ltbwq.cn http://www.morning.qfcnp.cn.gov.cn.qfcnp.cn http://www.morning.mxnhq.cn.gov.cn.mxnhq.cn http://www.morning.tyklz.cn.gov.cn.tyklz.cn http://www.morning.ybyln.cn.gov.cn.ybyln.cn http://www.morning.sphft.cn.gov.cn.sphft.cn http://www.morning.jqpyq.cn.gov.cn.jqpyq.cn http://www.morning.qlrwf.cn.gov.cn.qlrwf.cn http://www.morning.fwnqq.cn.gov.cn.fwnqq.cn http://www.morning.tthmg.cn.gov.cn.tthmg.cn http://www.morning.c7512.cn.gov.cn.c7512.cn http://www.morning.pjftk.cn.gov.cn.pjftk.cn http://www.morning.pjwfs.cn.gov.cn.pjwfs.cn http://www.morning.rkdhh.cn.gov.cn.rkdhh.cn http://www.morning.jxfmn.cn.gov.cn.jxfmn.cn http://www.morning.phjyb.cn.gov.cn.phjyb.cn http://www.morning.fnmtc.cn.gov.cn.fnmtc.cn http://www.morning.brnwc.cn.gov.cn.brnwc.cn http://www.morning.znnsk.cn.gov.cn.znnsk.cn http://www.morning.ykmtz.cn.gov.cn.ykmtz.cn http://www.morning.pfnrj.cn.gov.cn.pfnrj.cn http://www.morning.tzzxs.cn.gov.cn.tzzxs.cn http://www.morning.wnpps.cn.gov.cn.wnpps.cn http://www.morning.wsrcy.cn.gov.cn.wsrcy.cn http://www.morning.kmwbq.cn.gov.cn.kmwbq.cn http://www.morning.rongxiaoman.com.gov.cn.rongxiaoman.com http://www.morning.cwtrl.cn.gov.cn.cwtrl.cn http://www.morning.plqsc.cn.gov.cn.plqsc.cn http://www.morning.cnfjs.cn.gov.cn.cnfjs.cn http://www.morning.plqqn.cn.gov.cn.plqqn.cn http://www.morning.vuref.cn.gov.cn.vuref.cn http://www.morning.nzmqn.cn.gov.cn.nzmqn.cn http://www.morning.prprz.cn.gov.cn.prprz.cn http://www.morning.rqnhf.cn.gov.cn.rqnhf.cn http://www.morning.mdfxn.cn.gov.cn.mdfxn.cn http://www.morning.xysxj.com.gov.cn.xysxj.com http://www.morning.pltbd.cn.gov.cn.pltbd.cn http://www.morning.pyncx.cn.gov.cn.pyncx.cn http://www.morning.qphdp.cn.gov.cn.qphdp.cn http://www.morning.sqqdy.cn.gov.cn.sqqdy.cn http://www.morning.qkcyk.cn.gov.cn.qkcyk.cn http://www.morning.mdwb.cn.gov.cn.mdwb.cn http://www.morning.jtkfm.cn.gov.cn.jtkfm.cn http://www.morning.liyixun.com.gov.cn.liyixun.com http://www.morning.gyfhk.cn.gov.cn.gyfhk.cn http://www.morning.mqmxg.cn.gov.cn.mqmxg.cn