看一个例子 FactoryBeanDemo类
@Service public class FactoryBeanDemo implements FactoryBean { @Override public Object getObject() throws Exception { return new FactoryB(); } @Override public Class<?> getObjectType() { return FactoryB.class; } } FactoryB类`
public class FactoryB { } 测试代码
public void test4() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(cn.com.dq); FactoryB factoryB = (FactoryB) applicationContext.getBean(factoryBeanDemo); System.out.println(factoryB); FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo) applicationContext.getBean(&factoryBeanDemo); System.out.println(factoryBeanDemo); } 输出结果 从上面的代码可以启发2点思考 1.我们通过beanName=[factoryBeanDemo]拿到的bean不是FactoryBeanDemo的实例,而是FactoryB 的实例 2.我们要想拿到FactoryBeanDemo的实例而是通过&factoryBeanDemo拿到 下面我们从源码上分析上述情况
源码分析 在bean的实例化中,当bean实例化完成以后都会看到这样一行代码,如下:
AbstractBeanFactory类 doGetBean()方法 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { .
// 以下是基础介绍,以通俗易懂的语言进行说明,对零基础编程小白特别友好!大佬慎入 哈哈
1. 一些很基础的介绍 面向对象,我们常用 “OOP” 来代称,英文全称是 “Object-oriented Programming” 。
至于什么是面向对象?教材里是这么说的:OOP是建立模型,体现抽象思维。
如果是零基础开始学OOP的,想必听了会很懵(本人经历过这种迷茫 哈哈) 不过个人感觉,这个学着学着就懂了,不需要特别强求在一开始就把它搞明白。
学习OOP的意义主要有三点:
让我们程序的代码更简短(方便管理和维护) 让我们的代码更清晰(容易读 + 理解) 让我们的代码设计更普遍(容易修改 & 适应变动) 重点我们再重复两遍,简短!清晰!普遍!...... 简短!清晰!普遍!
2. “面向对象”,什么是对象? 这里有两个比较重要的概念需要大致了解:对象 & 抽象。
A. 对象 对象可以是有形 / 无形的。
通俗地理解,万物皆可是 “对象” 。车可以是一个对象,小狗勾可以是一个对象,甚至风也可以是一个对象。
对象有静态特征 + 动态特征。
静态特征:可以用数据来描述的,例:车有4个轮胎、蚂蚁有6条腿 动态特征:对象的形为 / 功能,例:狗会吠、容器可以收纳 对象是属性和服务的结合体。
例如:车有轮胎,喇叭等,而轮胎可以充气/泄气,喇叭可以鸣笛之类的
变量是存储数据的基本单位 声明变量的三种方法: (1)先声明再赋值: 使用var关键字声明变量,在给变量命名时需要注意的几个规则: 1.第一个字符必须是字母、下划线(_)、或者美元符号($) 2.其他字符可以是字母、下划线(_)、美元符号($)或数字 3.区分大小写 4.不能与关键字同名,比如while、for、if等 (2)同时声明与赋值: 可以一条语句声明多个变量,只需在变量之间用逗号隔开 (3)不声明直接赋值(容易出错,不推荐使用): 对变量的输出测试使用alert()方法弹出对话框方式,alert()弹出对话框是一种原始且有效的测试方法,它会终止当前运行代码,直到用户点击提示对话框确认按钮。 alert语法:alert(输出提示内容或变量) <script>var x=11;var y=12;var z=x+y;alert(x=+x+,y=+y+,z=+z); typeof操作符:检测变量数据类型的操作符object类型:是指JavaScript中的对象、数组或nullnull类型:变量已有值,但值为空对象,被typeof检测后会返回object类型undefined类型:未初始化或未被声明的变量,在被typeof操作符检测后会返回undefinednumber类型:代表整数或浮点数(小数)boolean类型:也被称为真假类型,有两个标准值:true和false,通常用作逻辑判断string类型:字符串类型(数字、字母、下划线组成的一串字符) var d=new Date();document.write(typeof d +<br/>);var f=null;document.write(typeof f +<br/>);var b;document.write(typeof b +<br/>);var c=1.0;document.write(typeof c +<br/>);var e=3>10;document.write(typeof e +<br/>);var a=message;document.write(typeof a +<br/>); 运算符算术运算符:+ - * / % ++ --赋值运算符:=比较运算符:> < >= <= == !=逻辑运算符:&& || ! var A=2; var B=3; document.write(A=+A+,B=+B+ <br/>); document.write(A+B= + (A+B) +<br/>); document.write(A-B= + (A-B) +<br/>); document.
<script>顺序结构:按先后顺序执行语句选择结构:常用if、swith语句 if语句形式有:单分支、双分支、多分支 在if单分支中: 若表达式结果为true,先执行大括号中的语句,再按顺序执行if后的其他代码 若表达式结果为false,跳过大括号中语句,直接执行if后的其他代码 var a; if(typeof a==undefined) { a=hello world; } document.write(a+<br/>); 在if双分支中: 若表达式结果为true,执行if后大括号中的语句 若表达式结果为false,执行false后大括号中的语句 var x=-4,y; if(x>0){ y=x; }else{ y=-x; } document.write(x+的绝对值是:+y +<br/>); 在if多分支中:使用else if可进行多条件判断,不同条件对应不同代码块 当前条件不符合时,顺序判断接下来的条件是否满足,若都不满足,执行else语句 整个语句中,只要有一个条件满足,则后面的else if停止运算 var A=new Date();//创建Date对象 var B=A.getHours();//获取当前小时数 if(B<=11){ document.write(早上好+<br/>); }else if(B<=18){ document.write(下午好+<br/>); }else{ document.write(晚上好+<br/>); } switch结构语句: case定义了一个标记位置,根据switch表达式的结果,直接跳转到匹配的标记位置,顺序执行后面的代码。直到遇到break语句,才跳出switch语句 如果switch表达式的结果没有匹配的标记位置,则执行default语句 var C=new Date(); var D=C.getDay(); switch(D){ case 1: case 2: case 3: case 4: case 5: document.write(今天是星期 +D+努力工作吧!); break; default: document.
在工作中经常听到用事件完成功能的说法,然而review code时发现这些代码并没有采用到典型的事件订阅机制,而是将委托作为参数传递,和直接把函数作为参数传递无异,趁五一假期翻看了相关书籍,才把两者之间的关系理清。
首先,委托的本质还就是方便将函数作为参数传递,也就是不直接调用函数,而是通过相应的委托调用对应的函数,这也是为什么委托必须和被调用的函数返回同样类型和参数,
delegate string trans(string s); string input(string s); main() { trans t=input; string getstring = t(string); // t=>input(string); } 如果仅仅满足单一的功能调用,利用委托传递确实和直接将函数作为参数传递给目标函数无异,参考Java并没有委托这一特性,难道Java就无法实现相应的功能?这种想法显然是可笑的。委托是满足“将函数作为参数传递”这一需求的总结,别忘了,委托也是一种“类”,是C#的引用类型之一,和interface,class一样。
当你需要应对多个类之间的调用时,相比直接将函数作为参数,委托(通常进一步声明为事件)更加方便,安全,
public delegate void ValueChanged(object sender, EventArgs e); public event ValueChanged DefaultValueChanged; 在声明委托变量时加上event,当本类在别处调用时,也可像静态方法一样调用本事件,不过采用的是订阅机制来自定义触发事件后要执行的函数,
this.DefaultValueChanged += OnDefaultValueChanged; ...... private void OnDefaultValueChanged(object sender,EventArgs e) { this.Text = Default value changed to + defaultvalue.ToString(); } 当然,我们还需要设定什么操作会触发本事件,在本例中,我们设置当DefaultValue值被改变时触发,
private int defaultvalue = 0; public int DefaultValue { get { return defaultvalue; } set { if (value == defaultvalue) return; defaultvalue = value; DefaultValueChanged?
接口测试时,测试数据有多种方式来存放(比如yaml、excel、MySQL、甚至txt文件等等),使用哪种方式需要根据接口的实际情况(请求数据量大小)来决定。当然也可以写死在代码里,不过从可维护性的角度来看这显然是一个糟糕的做法。还有另外一个场景,大多数情况下,POST或PUT接口会向业务数据库(MySOL)中插入数据,如果想进一步增强接口校验的可信度,除了对返回的response body做校验之外,业务数据库中是否完成插入数据的动作也可以作为一个校验点。
无论是用MySOL来存放测试数据还是校验业务数据库(MySOL)中是否插入数据,都需要做到使用Python脚本完成MySQL的增删改查操作。MySQL操作有两种方式,第一种最直接的方式是用SQL语句拼接成字符串,然后执行SQL,这种方式同样面临维护性的问题(如果表名发生了变化,需要在引用表名的所有位置全部完成替换),同时大量SQL字符串的存在会使得代码看起来异常臃肿。第二种是使用ORM框架,ORM框架把MySOL表和Python的类一一对应,MySQL的增删改查操作对应类中的各个方法,这大大降低了维护难度。
Python中,SQLAlchemy是最有名的ORM框架之一。
安装
pip3 install sqlalchemy SQLAlchemy架构:
Schema / Types 架构和类型 SQL Expression Language 封装好的 SQL 语句 Engine 框架引擎 Connection Pooling 数据库连接池 Dialect 根据用户的配置,调用不同的数据库 API(Oracle, postgresql, Mysql) 并执行对应的 SQL语句 SQLAlchemy本身无法操作数据库,其必须依赖pymsql等第三方插件,从而实现对数据库的操作,如:
pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] 如果和我一样用的是Python3,需要先确保pymysql已经安装。
SQLAlchemy操作数据表CURD
直接上代码
创建数据表(model.py):
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker # 创建对象的基类 Base = declarative_base() # 初始化数据库连接 engine = create_engine('mysql+pymysql://root:root@localhost/0501_db', echo=True, # 程序运行时反馈执行过程中的关键对象,包括ORM构建的sql语句 max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) ''' 创建数据表的映射类 ''' # 定义Users对象 class Users(Base): # 表的名字 __tablename__ = 'users' # 表的结构 id = Column(Integer, primary_key=True) age = Column(String(3)) name = Column(String(20)) # 定义Cat对象 class Cat(Base): __tablename__ = 'cat' id = Column(Integer, primary_key=True) name = Column(String(20)) color = Column(String(20)) # 创建表到数据库表中 Base.
目录前言背景过程总结
记录下遇到的dubbo坑, No provider available for the service xxx
前言 通常dubbo调用出现 No provider available for the service xxx,有以下几种情况:
1.服务方未启动
2.代码内客户端和服务端的group、version不匹配
3.有dubbo tag路由过滤,标签不匹配
4.动态配置过滤,没有匹配的服务(比如disable等)
但是最近遇到一个非以上问题,但是服务调用异常 No provider available for the service xxx,很是奇怪,因此研究了一番,发现了dubbo此处的一点瑕疵。
背景 在做JT808协议指令数据上行指令,指令通过808采集平台(netty长连接),解析后,通过dubbo调用服务,做指令的业务逻辑处理,奇怪是服务存在,但是却报错No provider available for the service com.xxx.ioc.api.service.JTService,错误截图如下:
很奇怪,服务明明是启动的,也没有动态配置,为什么服务竟然会很奇怪的找不到呢?然后debug下看了代码,发现是dubbo编码阶段报错io.netty.handler.codec.EncoderException: java.lang.RuntimeException: Serialized class com.xxx.ioc.codec.util.KeyValuePair must implement java.io.Serializable Java field: private com.xxx.ioc.codec.util.KeyValuePair com.xxx.ioc.protocol.t808.T0900.message 原来是参数T009的内部类KeyValuePair未实现序列化导致,但是如果是未实现序列化,应该报错Serialized class xxx must implement java.io.Serializable的错误,但是为什么收到的错误却是No provider available for the service xxx,带着这个问题,分析一波。
过程 调用链路根据之前自己分析的dubbo transport层记录,dubbo客户端调用时序图如下(可以参考链接的泳道图):
dubbo客户端的调用,经netty pipeline的TailContext处理,业务线程切换到reactor IO线程,业务线程在DefaultFuture.
dubbo发送过程编码失败,会唤醒发送(客户端业务)线程吗?如何实现的?
在上篇文章 dubbo坑- No provider available for the service xxx 中,如果dubbo请求阶段,编码异常,而业务线程依然在等待响应,dubbo如何处理的?总不能等待超时,响应个超时异常吧,这不合理,接下来看dubbo编码异常,如何处理的
回顾下之前自己分析的dubbo transport层记录,详细记录了dubbo调用链路(包括netty channelhandler chain,dubbo channelhandler chain)的执行,在简单回顾下:
客户端发送:此时是在业务线程执行,客户端业务线程依次执行 InvokerInvocationHandler入口 -> RegistryDirectory获取服务集合 -> tagRouter过滤 -> 负载均衡选取一个Invoker -> dubbo consumer filter chain -> DubboInvoker执行调用invoke -> Exchange层封装请求模型Request且创建DefaultFuture并阻塞等待 -> 网络客户端NettyClient发送消息 -> dubbo Channel即NettyChannel发送 -> 调用netty channle发送 io.netty.channel.Channel.writeAndFlush(message),接着触发执行netty pipeline的outbound事件,执行顺序TailContext->NettyClientHandler->InternalEncoder->InternalDecoder->HeadContext,其中在TailContext内由业务线程切换到reactor IO线程,接着InternalEncoder进行编码,最终由HeadContext把数据发送给服务方;
客户端接收服务端响应:reactor IO线程监听到selector有read事件,执行processSelectedKeys(),触发执行netty pipeline的inbound事件,执行顺序【HeadContext->InternalDecoder->InternalEncoder->NettyClientHandler->TailContext】,由InternalDecoder解码,接着在NettyClientHandler#channelRead执行,NettyClientHandler持有dubbo channelhandler chain,因此链式执行dubbo channelhandler chain,即NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->DubboProtocol$1,这里重要的是在AllChannelHandler内由IO线程切换到业务线程(封装响应Response为ChannelEventRunnable,提交到dubbo业务线程池),接着在业务线程上HeaderExchangeHandler判断mesage是Response,处理响应,唤醒DefaultFuture的阻塞等待。
接着客户端业务线程被唤醒,根据Response内容处理,获取返回的数据结果/异常。
以上就是dubbo整个发送-响应的整个流程,重点是netty pipeline和dubbo channelhandler chain,都是责任链模式增加功能。
接着分析我们这个问题,从上面回顾的流程可知,未实现序列化,那么异常肯定发生在dubbo编码阶段,即InternalEncoder抛出异常,异常是java.lang.RuntimeException,异常信息Serialized class com.zzz.ioc.codec.util.KeyValuePair must implement java.io.Serializable Java field: private com.zzz.ioc.codec.util.KeyValuePair com.zzz.ioc.protocol.t808.T0900.message,异常封装在netty DefaultChannelPromise,因此在NettyClientHandler获取InternalEncoder.write结果(此过程在IO线程执行),判断是否有无异常,代码如下
消息发送有异常,则mock response返回,接着执行NettyClientHandler持有的dubbo channelhandler chain[NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->DubboProtocol$1],AllChannelHandler执行,IO线程切换到业务线程(dubbo客户端线程池,线程名称DubboClientHandler-开头),接着业务线程上执行HeaderExchangeHandler,处理response,唤醒发送等待线程。此过程为了避免write操作失败,mock Request,然后和正常接收响应基本一样的处理方式(不同之处是不经过selector的processSelectedKeys())唤醒发送线程。通信框架出现这个问题也不容易,但是dubbo设计的非常巧妙。
-hood代表一个范围
原文链接:https://www.cnblogs.com/ysmc/p/16223154.html
Bootstrap Blazor 官方链接:https://www.blazor.zone/tables
上一篇文章说到 Table 组件的智能生成,有了自动生成,肯定会有自定义的。
一、指定生成列 除了可以在 AutoGenerateColumnAttribute 特性中指定每一列的行为外,我们可以手动在 Table 的 TableColumns 标签中自定义要展现的列与列具有的行为,在此之前,我们要先将 AutoGenerateColumns 属性设置成 false(该属性默认为 false):
<Table TItem=Foo IsPagination=true PageItemsSource=PageItemsSource ShowFooter=true IsStriped=true IsBordered=true ShowSkeleton=true IsMultipleSelect=true ShowToolbar=true ShowSearch=true ShowExtendButtons=true OnQueryAsync=@OnQueryAsync AutoGenerateColumns=false EditMode=EditMode.Popup> <TableColumns> <TableColumn @
[email protected]></TableColumn> <TableColumn @
[email protected]></TableColumn> <TableColumn @
[email protected]></TableColumn> <TableColumn @
[email protected]></TableColumn> </TableColumns> </Table> 二、定义列功能 我们还可以在 TableColumn 中指定每一列具有的功能,如过滤、排序、是否可编辑等等;在此,我们将日期(DateTime) 与 数量(Count) 两列分别赋予排序与过滤功能
<Table TItem=Foo IsPagination=true PageItemsSource=PageItemsSource ShowFooter=true IsStriped=true IsBordered=true ShowSkeleton=true IsMultipleSelect=true ShowToolbar=true ShowSearch=true ShowExtendButtons=true OnQueryAsync=@OnQueryAsync AutoGenerateColumns=false EditMode=EditMode.Popup> <TableColumns> <TableColumn @bind-Field=@context.