c return nulll 后是否还会进行下面的代码

js 中return null是什么意思_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
js 中return null是什么意思
function asb(_key,_do){
var arr=document,cookie.splie(&;&);
for (n in arr){
var arrs=arr[n],splie(&=&);
if(arrs[0]==_key){
if(_do){
return unescape(arrs[1])
}eles arrs[1];
1、return返回null,起到中断方法执行的效果;2、return false,事件处理函数会取消事件,不再继续向下执行。
return unescape(arrs[1])如果先执行这个,这个函数就不是返回null了,因为下面的 return null
不会执行了
本回答被提问者采纳
这行代码写错了吧var arrs=arr[n].splie(&=&quot,return *** 是返回值var arrs=arr[n],splie(&=&没错误;);)
返回一个null对象...
你这个是截取cookie吧。 那里没问题啊,如果找不到返回个空,否则返回结果。。。
其他2条回答
为您推荐:
其他类似问题
return的相关知识
换一换
回答问题,赢新手礼包Java代码中return value 为null 是不是在任何情况下都可以,为什么不会throw NullPointerException?
Java语言层面:null值自身是不会引起任何问题的。它安安静静的待在某个地方(局部变量、成员字段、静态字段)不会有任何问题;它从一个地方被搬运到另一个地方也不会有任何问题(变量赋值、返回值等)。唯一会因为null值而引起NullPointerException的动作是“解引用”(dereference)——也就是通过这个引用要对其引用的对象做操作。俗话说就是所有隐含“obj.xxx”的操作中,obj为null值的情况。
在Java里,下述操作隐含对引用的解引用:
&读字段(字节码 getfield):x.y,当x为null时抛NPE;
&&& 写字段(字节码 putfield):x.y = z,当x为null时抛NPE。注意:z的值是什么没关系;
&&& 读数组长度(字节码 arraylength):a.length,当a为null时抛NPE;
&&& 读数组元素(字节码 &x&aload,&x&为类型前缀):a[i],当a为null时抛NPE;
&&& 写数组元素(字节码 &x&astore,&x&为类型前缀):a[i] = x,当a为null时抛NPE。注意:x的值时什么没关系;
&&& 调用成员方法(字节码 invokevirtual、invokeinterface、invokespecial):obj.foo(x, y, z),当obj为null时抛NPE。注意:参数的值是什么没关系;
&&& 增强for循环(也叫foreach循环):
&&&&&&& 对数组时(实际隐含a.length操作):for (E e : a) { ... } , 当a为null时抛NPE;
&&&&&&& 对Iterable时(实际隐含对Iterable.iterator()的调用):for (E e : es) { ... } ,当es为null时抛NPE;
&&& 自动拆箱(实际隐含 &XXX&.&xxx&Value() 的调用,&XXX&为包装类型名,&xxx&为对应的原始类型名): (int) integerObj,当integerObj为null时抛NPE;
&&& 对String做switch(实际隐含的操作包含对String.hashCode()的调用):switch (s) { case &abc&: ... } ,当s为null时抛NPE;
&&& 创建内部类对象实例(字节码 new,但这里特指创建内部类实例的情况):outer.new Inner(x, y, z),当outer为null时抛NPE;
&&& 抛异常(字节码 athrow):throw obj,当obj(throw表达式的参数)为null时抛NPE;
&&& 用synchronized关键字给对象加锁(字节码 monitorenter / monitorexit):synchronized (obj) { ... },当obj为null时抛NPE。
Java语言里所有其它语法结构都不会因为null值而隐含抛NPE的语义。当然,用户可以在自己需要的地方显式检查null值然后自己抛出NPE,就像:
java.util.Objects.requireNonNull(Object)
* Checks that the specified object reference is not {@code null}. This
* method is designed primarily for doing parameter validation in methods
* and constructors, as demonstrated below:
* &blockquote&&pre&
* public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
* &/pre&&/blockquote&
* @param obj the object reference to check for nullity
* @param &T& the type of the reference
* @return {@code obj} if not {@code null}
* @throws NullPointerException if {@code obj} is {@code null}
public static &T& T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
自己主动throw new NullPointerException()这种情况JVM管不着,用户代码主动指定的,用户想怎么搞就怎么搞。
趣味题:在Java语言里,只使用Java语言及标准库的功能而不依赖第三方库,检查一个引用obj是否为null并在null时抛NPE的代码是什么?
答案:obj.getClass()。这是因为getClass()是java.lang.Object类上的方法,因而无论什么引用类型都可以使用。这在Java源码层面和在Java字节码层面上都是最短的。
当然这是个很邪恶的歪招,然而在OpenJDK的标准库内部实现中并不少见。大家…还是用Objects.requireNonNull()就好了.
return null主要多了一个麻烦,凡是调用它的地方,都要想一想,是不是要判断if (xxx == null),这样代码不够优雅。
语言层面上讲,返回null没有任何问题,大家都赞同。
工程实践中,返回null是否就是个不好的习惯?我倒不这么认为。我的观点是,所有的方法调用,无论是自己工程的内部类方法还是第三方包中的方法,除非对方在Java Doc中显式的说明了不会返回空,其它情况都应该怀疑有返回空指针的可能性。多一个判断并没有什么不好,还能大大增加代码的健壮性。在另一方面,方法的编写者也应该仔细的维护Java Doc,如果会返回空指针,那应该说明原因和语义,让调用者有章可依。
本文已收录于以下专栏:
相关文章推荐
相信很多人都遇到过下面这些异常:"Parameter 'xxx' not found. Available parameters are [...]"等等
不只是上面提到的这几个,我认为有很多的错误都...
无返回值的return语句
在return语句的各类文章中,大多仅仅介绍了return语句用于有返回值(非void返回值)的方法中。
而很少或没有介绍return语句在void返回值方法中...
Java中,null对象可以被打印出来,也可以用来与另一个对象比较。下面是示例代码:
package test
public class Person {...
这几天在看设计模式,看到一个观点,就是在需要返回值的方法中,使用空对象(empty object)来代替返回null。理由很简单,空对象与其他有意义的对象一样,使得调用方法的用户不需要区分返回值,即不...
以前对UUID的了解很少,只知道是128位整数(16字节)的全局唯一标识符(Universally Unique Identifier)。
刚才google了下,算是有了点深入的了解。
(1) Annotation(注解方式)依赖额外的两个包 aspectjrt.jar aspectjweaver.jar发现aspect,在以往的方式中通过配置xml文件来定义某个类具有某个特性。A...
他的最新文章
讲师:董晓杰
讲师:姚远
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Binder学习(四)利用AIDL、Messenger实现IPC在利用Binder进行IPC的时候,会经常需要创建一个Server端,Android中通常的实现是利用Service来实现,所以再进行IPC之前先了解之前先复习一下Service:
Intent intent = new Intent();
intent.setClass(this, MyService.class);
startService(intent); Intent intent = new Intent();
intent.setClass(this, MyService.class);
bindService(intent, new ServiceConnection() {
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
public void onServiceDisconnected(ComponentName componentName) {
}, Context.BIND_AUTO_CREATE);Service生命周期
常见方法回调时机
在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBind()之前,我们可以在这个方法中进行一些一次性的初始化工作。
当其他组件通过startService()方法启动service时,此方法将会被调用。
当其他组件通过bindService()方法与service相绑定之后,此方法将会被调用。这个方法有一个IBinder的返回值,这意味着在重写它的时候必须返回一个IBinder对象,它是用来支撑其他组件与service之间的通信的——另外,如果你不想让这个service被其他组件所绑定,可以通过在这个方法返回一个null值来实现。
当调用UnBindService的时候 ,此方法会被调用
这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。
startService()启动,stopService()销毁的生命周期如下:-&onCreate()-&onStartCommand()-&Service running-& onDestroy()
bindService()启动,unbindService()销毁的生命周期如下:-&onCreate()-&onStartCommand()-&Service running-& onDestroy()
如果结束只调用unbindService(),那么只会执行到onUnbind(),将不会执行onDestroy():-&onCreate()-&onStartCommand()-&onBind()-&Service running-& onUnbind()。
如果在unbindService后,在调用stopService(),那么:-&onCreate()-&onStartCommand()-&onBind()-&Service running-& onUnbind()-&onDestroy() 。
其实starService跟Binder机制没有太大关系,通过此方式虽然可以启动一个本地或者远程的Service,但是我们拿不到Binder对象,不能直接通过AIDL的方式进行远程通信,只能通过其它的IPC方式进行通信,即时如此,这种方式启动的Service还是有一个方法需要注意一下,就是onStartCommand。
@Override public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
启动service的时候,onCreate方法只有第一次会调用,onStartCommand每次都被调用。onStartCommand会告诉系统如何重启服务,如判断是否异常终止后重新启动,在何种情况下异常终止
启动服务时依次执行onCreate,onStartCommand;如果在系统显示调用stopService和stopSelf之前终止服务,service再次重启,onStartCommand会被调用,重启服务时依次执行onStartCommand。
我们发现onStartCommand这个方法有个int类型返回值,实际上有四种类型,都是定义在Service中的静态常量:
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
public static final int START_STICKY = 1;
public static final int START_STICKY_COMPATIBILITY = 0;下面依次解释下这几种返回值的含义:
1):START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
2):START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
3):START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
4):START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
flags表示启动服务的方式:通过startService启动时为0;onStartCommand返回为START_FLAG_REDELIVERY, or START_FLAG_RETRY.
START_FLAG_RETRY:表示服务之前被设为START_STICKY,则会被传入这个标记。
START_FLAG_REDELIVERY:如果你实现onStartCommand()来安排异步工作或者在另一个线程中工作, 那么你可能需要使用START_FLAG_REDELIVERY来让系统重新发送一个intent。这样如果你的服务在处理它的时候被Kill掉, Intent不会丢失.
前面说了那么多,bindService才是重点,通过bindService启动的Service会调用onBind方法,我们现在 分析一下如何拿到我们想要的Binder对象,因为不管是Client还是Server,想要通过AIDL进行IPC通信,就必须拿到一个Binder对象,但是如果通过Messenger的话就不需要了,因为Messenger底层自己封装了AIDL。其实通过前面的分析,很容易看出Client在进行bindService的时候传入了一个ServiceConnection,当跟Server端连接成功的时候会在onServiceConnected中返回一个Binder对象,那么这个Binder对象是从哪儿来的呢?结合Server端的onBind方法,就很明显了,这个Binder对象就是服务端传递过来的。其实通过之前的AIDL分析,也很容易能够判断出来,只要是通过Binder机制进行IPC通信的,无论是Client还是Server端,都会涉及到Binder,我们只要找到了Binder,整个流程就很清晰了,下面分别描述一下Client跟Server中关于Binder的两个方法
ServiceConnection
mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
public void onServiceDisconnected(ComponentName name) {
};Client通过onServiceConnected可以拿到服务端返回的Binder对象,因为Binder实现了IBinder接口,返回的是IBinder。
public IBinder onBind(Intent intent) {
return null;
}Server通过onBind返回IBinder对象,默认的而实现为null,我们有多种方式可以实现Binder,通过继承Binder类,AIDL以及Messenger都可以做到,下面简要说明一下自定义Binder,Messenger跟AIDL下面会重点进行讲解:
继承Binder
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
public IBinder onBind(Intent intent) {
}比较简单,这种方式一般用在Service跟Activity进行通信的过程中,进行方法调用,大多数是在同一个进程中,我们看到在LocalBinder 中定义了getService方法,可以获取到Service的实例,比如我在项目中的定位是放在Service中的,但是拿到定位数据之后需要在Activity中显示地域切换的对话框,所以Service就需要跟Activity进行交互,Service可以调Activity的方法,同样由于Activity在Binder中拿到了Service的引用,也可以调用Service的中的方法。
ServiceConnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mPeopleManager = PeopleManager.Stub.asInterface(service);
mBound = true;
public void onServiceDisconnected(ComponentName name) {
mBound = false;
};开始连接
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, AIDLService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}addPeople
public void addBook(View view) {
if (!mBound) {
attemptToBindService();
if (mPeopleManager == null)
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
mPeopleManager.addPeople(people);
Log.e(getLocalClassName(), people.toString());
} catch (RemoteException e) {
e.printStackTrace();
}getPeople
public void getPeople(View view) {
if (!mBound) {
attemptToBindService();
Log.d("client--&", "正在连接Server");
if (mPeopleManager == null)
Toast.makeText(this, String.valueOf(mPeopleManager.getPeople().size()), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}创建一个Stub对象
private final PeopleManager.Stub mPeopleManager = new PeopleManager.Stub() {
public List&People& getPeople() throws RemoteException {
synchronized (this) {
if (mPeoples != null) {
return new ArrayList&&();
public void addPeople(People book) throws RemoteException {
synchronized (this) {
if (mPeoples == null) {
mPeoples = new ArrayList&&();
if (book == null) {
Log.e(TAG, "People is null in In");
mPeoples.add(book);
};onBind进行返回
public IBinder onBind(Intent intent) {
return mPeopleM
}开启远程线程
android:name=".AIDLService"
android:enabled="true"
android:exported="true"
android:process=":server"&
&/service&AIDL进行IPC
Messenger通信原理
创建一个Handler
public class ClientHandler extends Handler {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESS_FROM_SERVER:
//接收服务器传过来的值
Bundle bundle = msg.getData();
int peopleSize = bundle.getInt("server");
Toast.makeText(MessengerActivity.this, String.valueOf(peopleSize), Toast.LENGTH_SHORT).show();
}创建一个Messenger
Messenger messenger = new Messenger(new ClientHandler());ServiceConnection
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
isBound = true;
mService = new Messenger(service);
public void onServiceDisconnected(ComponentName name) {
mService = null;
isBound = false;
};开始连接
private void attemptToBindService() {
Intent intent = new Intent();
intent.setClass(this, MessengerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}addPeople
public void addPeople(View view) {
if (!isBound) {
attemptToBindService();
People people = new People();
people.setAge(18);
people.setGender("male");
people.setHobby("travel");
Message message = Message.obtain(null, MESS_ADD_PEOPLE);
Bundle bundle = new Bundle();
bundle.putParcelable("people", people);
message.setData(bundle);
message.replyTo =
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}getPeople
public void getPeople(View view) {
if (!isBound) {
attemptToBindService();
Log.d("client--&", "正在连接Server");
Message message = Message.obtain(null, MESS_GET_PEOPLE);
message.replyTo =
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}创建一个Handler用来收发消息,由于我们是在一个应用里面的开启的多进程,所以这边接收到Client的请求,不能直接将其转化为People对象,因为People对象属于应用进程,而MessengerService属于另外一个进程,是不能共享这个数据的,这里采用了收到消息后,用一个int 类型的数据来模拟集合的数量
mHandler = new Handler() {
public void handleMessage(Message msgFromClient) {
super.handleMessage(msgFromClient);
Message replyToClient = Message.obtain(msgFromClient);
switch (msgFromClient.what) {
case MESS_ADD_PEOPLE:
size += 2;
case MESS_GET_PEOPLE:
replyToClient.what = MESS_FROM_SERVER;
Bundle serverBundle = new Bundle();
serverBundle.putInt("server", size);
replyToClient.setData(serverBundle);
msgFromClient.replyTo.send(replyToClient);
} catch (RemoteException e) {
e.printStackTrace();
};创建一个Messenger
messenger = new Messenger(mHandler);onBind进行返回
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}开启远程线程
android:process=":server"
android:name=".MessengerService"/&测试结果使用Messenger进行IPC
通过利用AIDL跟Messenger来实现Android应用层的IPC,可以更加方便的帮助我们理解Binder机制,当然Android系统还可以利用其它的方式来进行IPC,例如通过文件共享,intent传值等,下面简单就这些方式做一个对比:
进程间通信的方式
只能传输Bundle所支持的数据类型
四大组件间的进程间通信
不适合高并发
简单的数据共享,无高并发场景
功能强大,支持一对多并发实时通信
使用稍微复杂,需要注意线程同步
复杂的进程间调用,Android中最常用
比AIDL稍微简单易用些
比AIDL功能弱,只支持一对多串行实时通信
简单的进程间通信
ContentProvider
强大的数据共享能力,可通过call方法扩展
受约束的AIDL,主要对外提供数据线的CRUD操作
进程间的大量数据共享
RemoteViews
在跨进程访问UI方面有奇效
比较小众的通信方式
某些特殊的场景
跨主机,通信范围广
只能传输原始的字节流
常用于网络通信中
6分享到微信扫一扫掘金翻译计划是一个翻译优质互联网技术文章的社区,文章翻译自国外优秀英文文章。内容覆盖区块链、人工智能、Android、iOS、React、前端、后端、产品、设计等领域。欢迎加入掘金翻译计划。相关文章6C语言关于预处理的问题:下面是代码:_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
C语言关于预处理的问题:下面是代码:
/* Program 13.1 Debugging using preprocessing directives */
#include &stdio.h&
#include &stdlib.h&
#include &time.h&
/* Macro to generate pseudo-random number from 0 to NumValues */
#define random (NumValues) ((int)(((double)(rand())...
我有更好的答案
如果定义了则打印,否则不打印,而repeatable则是用于在定义了之后能够产生一定范围的随机数,使程序处于可控状态。2、sizeof(pfun)是数组的大小1、test、减,sizeof(pfun【0】)是数组第一个元素的大小,除法之后自然就是数组的个数该程序随机从加,testf在程序中起到决定是否打印调试信息
采纳率:32%
为您推荐:
其他类似问题
c语言的相关知识
换一换
回答问题,赢新手礼包今天看啥 热点:
深入了解MyBatis参数,深入了解mybatis
深入了解MyBatis参数
相信很多人可能都遇到过下面这些异常:
"Parameter 'xxx' not found. Available parameters are [...]"
"Could not get property 'xxx' from xxxClass.
"The expression 'xxx' evaluated to a null value."
"Error evaluating expression 'xxx'.
Return value (xxxxx) was not iterable."
不只是上面提到的这几个,我认为有很多的错误都产生在和参数有关的地方。
想要避免参数引起的错误,我们需要深入了解参数。
想了解参数,我们首先看MyBatis处理参数和使用参数的全部过程。
本篇由于为了便于理解和深入,使用了大量的源码,因此篇幅较长,需要一定的耐心看完,本文一定会对你起到很大的帮助。
参数处理过程
处理接口形式的入参
在使用MyBatis时,有两种使用方法。一种是使用的接口形式,另一种是通过SqlSession调用命名空间。这两种方式在传递参数时是不一样的,命名空间的方式更直接,但是多个参数时需要我们自己创建Map作为入参。相比而言,使用接口形式更简单。
接口形式的参数是由MyBatis自己处理的。如果使用接口调用,入参需要经过额外的步骤处理入参,之后就和命名空间方式一样了。
在MapperMethod.java会首先经过下面方法来转换参数:
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasNamedParameters && paramCount == 1) {
return args[params.keySet().iterator().next()];
final Map&String, Object& param = new ParamMap&Object&();
int i = 0;
for (Map.Entry&Integer, String& entry : params.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
在这里有个很关键的params,这个参数类型为Map&Integer, String&,他会根据接口方法按顺序记录下接口参数的定义的名字,如果使用@Param指定了名字,就会记录这个名字,如果没有记录,那么就会使用它的序号作为名字。
例如有如下接口:
List&User& select(@Param('sex')String sex,Integer age);
那么他对应的params如下:
继续看上面的convertArgsToSqlCommandParam方法,这里简要说明3种情况:
注意:从第3种情况来看,建议各位有多个入参的时候通过@Param指定参数名,方便后面(动态sql)的使用。
经过上面方法的处理后,在MapperMethod中会继续往下调用命名空间方式的方法:
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.&E&selectList(command.getName(), param);
从这之后开始按照统一的方式继续处理入参。
不管是selectOne还是selectMap方法,归根结底都是通过selectList进行查询的,不管是delete还是insert方法,都是通过update方法操作的。在selectList和update中所有参数的都进行了统一的处理。
在DefaultSqlSession.java中的wrapCollection方法:
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap&Object& map = new StrictMap&Object&();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
} else if (object != null && object.getClass().isArray()) {
StrictMap&Object& map = new StrictMap&Object&();
map.put("array", object);
这里特别需要注意的一个地方是map.put("collection", object),这个设计是为了支持Set类型,需要等到MyBatis 3.3.0版本才能使用。
wrapCollection处理的是只有一个参数时,集合和数组的类型转换成Map2类型,并且有默认的Key,从这里你能大概看到为什么&foreach&中默认情况下写的array和list(Map类型没有默认值map)。
参数的使用
参数的使用分为两部分:
第一种就是常见#{username}或者${username}。
第二种就是在动态SQL中作为条件,例如&if test="username!=null and username !=''"&。
下面对这两种进行详细讲解,为了方便理解,先讲解第二种情况。
在动态SQL条件中使用参数
关于动态SQL的基础内容可以查看官方文档。
动态SQL为什么会处理参数呢?
主要是因为动态SQL中的&if&,&bind&,&foreache&都会用到表达式,表达式中会用到属性名,属性名对应的属性值如何获取呢?获取方式就在这关键的一步。不知道多少人遇到Could not get property xxx from xxxClass或: Parameter ‘xxx’ not found. Available parameters are[…],都是不懂这里引起的。
在DynamicContext.java中,从构造方法看起:
public DynamicContext(Configuration configuration, Object parameterObject) {
if (parameterObject != null && !(parameterObject instanceof Map)) {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
bindings = new ContextMap(metaObject);
bindings = new ContextMap(null);
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
这里的Object parameterObject就是我们经过前面两步处理后的参数。这个参数经过前面两步处理后,到这里的时候,他只有下面三种情况:
看上面构造方法,如果参数是1,2情况时,执行代码bindings = new ContextMap(null);参数是3情况时执行if中的代码。我们看看ContextMap类,这是一个内部静态类,代码如下:
static class ContextMap extends HashMap&String, Object& {
private MetaObject parameterMetaO
public ContextMap(MetaObject parameterMetaObject) {
this.parameterMetaObject = parameterMetaO
public Object get(Object key) {
String strKey = (String)
if (super.containsKey(strKey)) {
return super.get(strKey);
if (parameterMetaObject != null) {
return parameterMetaObject.getValue(strKey);
return null;
我们先继续看DynamicContext的构造方法,在if/else之后还有两行:
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
其中两个Key分别为:
public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";
也就是说1,2两种情况的时候,参数值只存在于"_parameter"的键值中。3情况的时候,参数值存在于"_parameter"的键值中,也存在于bindings本身。
当动态SQL取值的时候会通过OGNL从bindings中获取值。MyBatis在OGNL中注册了ContextMap:
OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
当从ContextMap取值的时候,会执行ContextAccessor中的如下方法:
public Object getProperty(Map context, Object target, Object name)
throws OgnlException {
Map map = (Map)
Object result = map.get(name);
if (map.containsKey(name) || result != null) {
Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
if (parameterObject instanceof Map) {
return ((Map)parameterObject).get(name);
return null;
参数中的target就是ContextMap类型的,所以可以直接强转为Map类型。
参数中的name就是我们写在动态SQL中的属性名。
下面举例说明这三种情况:
null的时候:
不管name是什么(name="_databaseId"除外,可能会有值),此时Object result = map.get(name);得到的result=null。
在Object parameterObject = map.get(PARAMETER_OBJECT_KEY);中parameterObject=null,因此最后返回的结果是null。
在这种情况下,不管写什么样的属性,值都会是null,并且不管属性是否存在,都不会出错。
此时Object result = map.get(name);一般也不会有值,因为参数值只存在于"_parameter"的键值中。
然后到Object parameterObject = map.get(PARAMETER_OBJECT_KEY);,此时获取到我们的参数值。
在从参数值((Map)parameterObject).get(name)根据name来获取属性值。
在这一步的时候,如果name属性不存在,就会报错:
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
name属性是什么呢,有什么可选值呢?这就是处理接口形式的入参和处理集合处理后所拥有的Key。
如果你遇到过类似异常,相信看到这儿就明白原因了。
数组、集合和Map以外的Object类型:
这种类型经过了下面的处理:
MetaObject metaObject = configuration.newMetaObject(parameterObject);
bindings = new ContextMap(metaObject);
MetaObject是MyBatis的一个反射类,可以很方便的通过getValue方法获取对象的各种属性(支持集合数组和Map,可以多级属性点.访问,如user.username,user.roles[1].rolename)。
现在分析这种情况。
首先通过name获取属性时Object result = map.get(name);,根据上面ContextMap类中的get方法:
public Object get(Object key) {
String strKey = (String)
if (super.containsKey(strKey)) {
return super.get(strKey);
if (parameterMetaObject != null) {
return parameterMetaObject.getValue(strKey);
return null;
可以看到这里会优先从Map中取该属性的值,如果不存在,那么一定会执行到下面这行代码:
return parameterMetaObject.getValue(strKey)
如果name刚好是对象的一个属性值,那么通过MetaObject反射可以获取该属性值。如果该对象不包含name属性的值,就会报错:
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".
Cause: " + t.toString(), t);
理解这三种情况后,使用动态SQL应该不会有参数名方面的问题了。
在SQL语句中使用参数
SQL中的两种形式#{username}或者${username},虽然看着差不多,但是实际处理过程差别很大,而且很容易出现莫名其妙的错误。
${username}的使用方式为OGNL方式获取值,和上面的动态SQL一样,这里先说这种情况。
${propertyName}参数
在TextSqlNode.java中有一个内部的静态类BindingTokenParser,现在只看其中的handleToken方法:
public String handleToken(String content) {
Object parameter = context.getBindings().get("_parameter");
if (parameter == null) {
context.getBindings().put("value", null);
} else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
context.getBindings().put("value", parameter);
Object value = OgnlCache.getValue(content, context.getBindings());
String srtValue = (value == null ? "" : String.valueOf(value));
checkInjection(srtValue);
return srtV
从put("value"这个地方可以看出来,MyBatis会创建一个默认为"value"的值,也就是说,在xml中的SQL中可以直接使用${value},从else if可以看出来,只有是简单类型的时候,才会有值。
关于这点,举个简单例子,如果接口为List&User& selectOrderby(String column),如果xml内容为:
id="selectOrderby" resultType="User"&
select * from user order by ${value}
这种情况下,虽然没有指定一个value属性,但是MyBatis会自动把参数column赋值进去。
再往下的代码:
Object value = OgnlCache.getValue(content, context.getBindings());
String srtValue = (value == null ? "" : String.valueOf(value));
这里和动态SQL就一样了,通过OGNL方式来获取值。
看到这里使用OGNL这种方式时,你有没有别的想法?
特殊用法:你是否在SQL查询中使用过某些固定的码值?一旦码值改变的时候需要改动很多地方,但是你又不想把码值作为参数传进来,怎么解决呢?你可能已经明白了。
就是通过OGNL的方式,例如有如下一个码值类:
package com.abel533.
public interface Code{
public static final String ENABLE = "1";
public static final String DISABLE = "0";
如果在xml,可以这么使用:
id="selectUser" resultType="User"&
select * from user where enable = ${@com.abel533.mybatis.Code@ENABLE}
除了码值之外,你可以使用OGNL支持的各种方法,如调用静态方法。
#{propertyName}参数
这种方式比较简单,复杂属性的时候使用的MyBatis的MetaObject。
在DefaultParameterHandler.java中:
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List&ParameterMapping& parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i & parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterO
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
上面这段代码就是从参数中取#{propertyName}值的方法,这段代码的主要逻辑就是if/else判断的地方,单独拿出来分析:
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterO
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
首先看第一个if,当使用&foreach&的时候,MyBatis会自动生成额外的动态参数,如果propertyName是动态参数,就会从动态参数中取值。
第二个if,如果参数是null,不管属性名是什么,都会返回null。
第三个if,如果参数是一个简单类型,或者是一个注册了typeHandler的对象类型,就会直接使用该参数作为返回值,和属性名无关。
最后一个else,这种情况下是复杂对象或者Map类型,通过反射方便的取值。
下面我们说明上面四种情况下的参数名注意事项。
&foreach&详解
所有动态SQL类型中,&foreach&似乎是遇到问题最多的一个。
例如有下面的方法:
id="insertUserList"&
INSERT INTO user(username,password)
collection="userList" item="user" separator=","&
(#{user.username},#{user.password})
对应的接口:
int insertUserList(@Param("userList")List&User& list);
我们通过foreach源码,看看MyBatis如何处理上面这个例子。
在ForEachSqlNode.java中的apply方法中的前两行:
Map&String, Object& bindings = context.getBindings();
final Iterable&?& iterable = evaluator.evaluateIterable(collectionExpression, bindings);
这里的bindings参数熟悉吗?上面提到过很多。经过一系列的参数处理后,这儿的bindings如下:
"_parameter":{
"param1":list,
"userList":list
"_databaseId":null,
collectionExpression就是collection="userList"的值userList。
我们看看evaluator.evaluateIterable如何处理这个参数,在ExpressionEvaluator.java中的evaluateIterable方法:
public Iterable&?& evaluateIterable(String expression, Object parameterObject) {
Object value = OgnlCache.getValue(expression, parameterObject);
if (value == null) {
throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");
if (value instanceof Iterable) {
return (Iterable&?&)
if (value.getClass().isArray()) {
int size = Array.getLength(value);
List&Object& answer = new ArrayList&Object&();
for (int i = 0; i & i++) {
Object o = Array.get(value, i);
answer.add(o);
if (value instanceof Map) {
return ((Map) value).entrySet();
throw new BuilderException("Error evaluating expression '" + expression + "'.
Return value (" + value + ") was not iterable.");
首先通过看第一行代码:
Object value = OgnlCache.getValue(expression, parameterObject);
这里通过OGNL获取到了userList的值。获取userList值的时候可能出现异常,具体可以参考上面动态SQL部分的内容。
userList的值分四种情况。
通过上面处理后,返回的值,是一个Iterable类型的值,这个值可以使用for (Object o : iterable)这种形式循环。
在ForEachSqlNode中对iterable循环的时候,有一段需要关注的代码:
if (o instanceof Map.Entry) {
@SuppressWarnings("unchecked")
Map.Entry&Object, Object& mapEntry = (Map.Entry&Object, Object&)
applyIndex(context, mapEntry.getKey(), uniqueNumber);
applyItem(context, mapEntry.getValue(), uniqueNumber);
applyIndex(context, i, uniqueNumber);
applyItem(context, o, uniqueNumber);
如果是通过((Map) value).entrySet()返回的Set,那么循环取得的子元素都是Map.Entry类型,这个时候会将mapEntry.getKey()存储到index中,mapEntry.getValue()存储到item中。
如果是List,那么会将序号i存到index中,mapEntry.getValue()存储到item中。
这篇文章很长,写这篇文章耗费的时间也很长,超过10小时,写到半夜两点都没写完。
这篇文章真的非常有用,如果你对Mybatis有一定的了解,这篇文章几乎是必读的一篇。
如果各位发现文中错误或者其他问题欢迎留言或加群详谈。
MyBatis分页插件
http://git.oschina.net/free/Mybatis_PageHelper
MyBatis通用Mapper
http://git.oschina.net/free/Mapper
Mybatis专栏:
Mybatis示例
Mybatis问题集
作者博客:
http://my.oschina.net/flags/blog
http://blog.csdn.net/isea533
作者邮箱:
Mybatis工具群:
(欢迎各位加群)
相关搜索:
相关阅读:
相关频道:
&&&&&&&&&&&&&&&&
WEB编程教程最近更新}

我要回帖

更多关于 return blank as null 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信