Java的反射特性一般结合注解和配置文件(如:XML)来使用,这也是大部分框架(Spring等)支持两种配置方式的原因。如果是注解方式:当服务端启动时,Spring框架会去扫描指定目录下的类,通过反射看类有没有Service注解,如果类上有 Service注解,会提前初始化(new)这个类。初始化好所有类以后,再去查找所有属性,看属性有没有Autowired注解,有的话,会给这个属性注入值(反射赋值),见下图。
如果是XML方式,原理上也差不多,只不过是先解析XML,拿到XML里的配置信息,再去初始化(new)或给属性反射赋值。所以我们写业务代码的时候才不用一个个的去new实现类,所有参数都赋上值,这部分工作Spring已经利用反射技术给完成了。在实际工作中,当写到部分业务逻辑时,经常有新手问我,好麻烦呀,为什么每次写一个类,先要写一个interface,再写一个实现类?工作中大部分接口都只有一个实现类,这也是造成他们困惑的原因。那什么情况下会有多个实现类呢?比如手机APP的用户注册,一般有验证短信这个功能,有时候不能过度依赖一个第三方发短信的平台,假设这家短信平台的服务挂了,会导致我们的APP无法注册,验证短信等。我们之前短信平台用的是云片网的短信平台,并找了阿里短信平台做备用,由于这两个厂商的API完全不一致,写出来的代码也不一样,大部分人写代码会像下面这样写
发送短信(){
if(云片网发短信开关为开启){
调用第三方云片网发短信API发送短信;
}else if(阿里短信平台发短信开关为开启){
调用第三方阿里短信平台发短信API发送短信;
}else if(....){
...........;//如果有新的需求,增加else if逻辑
}else{
}
}
这样写也能完成功能,大部分代码都会这么写,但有几个问题,第一:假设我们打开了阿里短信平台的开关,但忘了去关闭云片网短信平台的开关,会导致不调用阿里短信平台去发送短信,还是会去调用云片网短信平台去发送短信。 第二:假设某天老板说,这两家短信平台费率太高,要使用其它短信平台,这时候开发人员还会来修改这段这段代码,增加新的else if条件,并写下相关的调用短信平台的代码,这样就违背了类设计的六大设计原则:开闭原则(Open-Close Principle),类的设计应该对扩展开放,对修改关闭。这里明显修改了这个方法(类),一旦修改了这段代码(这个类),测试人员就会对这段代码(这个类)里所有的else if条件(整个类)都要进行覆盖测试。那有没有办法一次写好这段代码,以后就算新增加其它短信平台也不用修改这段代码呢?有,我们可以利用反射来完美实现,先定义一个接口,接口里声明了一个方法发送短信()
interface 短信接口(){
void 发送短信();
}
这时候就可以写实现了,写了两个实现类,一个是云片网发送短信的实现,一个是阿里短信平台发送短信的实现
class 云片网短信接口实现 implments 短信接口{
void 发送短信(){
调用第三方云片网发短信API发送短信;
}
}
class 阿里短信接口实现 implments 短信接口{
void 发送短信(){
调用第三方阿里短信平台发短信API发送短信;
}
}
这时候客户端调用发送短信的时候只需要三行代码就解决了String 实现类名 = 从数据库或缓存里读取到的实现类名。
短信接口 接口 = Class.forName(实现类名).newInstance();//反射创建子类实例
接口.发送短信();
以上代码Class.forName()方法中的“实现类名”可以放在数据库或缓存里,到时候修改一下,就能随心切换了。上属例子不但涉及了到了面向对象中的多态,还有六大设计原则中的依赖倒置(依赖于抽象编程,而不是实现),以后就算增加新的短信平台,只是再扩展一个新的实现类,上面这三行发送短信的代码都不用改,把实现类名传入客户端就行了,这就是对扩展开放,对修改关闭。这么做还有一个好处就是解耦,客户端不再和这几家短信平台的发送短信的代码有任何耦合。这也是面向对象的设计技巧之一。具体的发送短信的代码不写在客户端,而是利用接口和几个不同的实现类来完成各自的发送短信的职责,这也是六大设计原则中的单一职责,当阿里短信平台的API需要从1.0升级到2.0的时候,这时候我会去修改阿里云短信平台实现类的发送短信的方法,但其它实现类和客户端不需要做任何修改,没任何影响。呀,好像要跑题了,赶紧拉回来。实际开发中,有时候后台日志需要打印整个对象来看一下所有的属性,大多数人会利用IDE去实现toString方法来打印,所有的JavaBean都要覆盖toString实现,如果觉得麻烦,可以利用反射技术遍历JavaBean的所有参数名称及值即可。也可以利用反射和注解写一个前端参数验证框架来验证前端传过来的参数是否必输,长度等,如下
public class SelectOrderInfoRequestVO extends BaseRequestVO {
@RequestParam(isMust = true, maxLength = 20)//可以通过反射拿到注解,判断订单号是否必输,长度是否大于20等
public String orderId;
}
还有著名的junit测试框架也是利用反射方法名和参数名来进行测试的,要不写一个个的main方法,那得多累呀。