最近在研读Hive社区版本的源码,发现其中多处用到了Java依赖注入,这里简单总结一下依赖注入的几种实现方法。在谈java依赖注入之前,有必要先回顾一下设计模式中的Proxy模式。
其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。先来看看关系图:
image.png根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:
public interface Sourceable {
public void method();
}
@Override
public void method() {
System.out.println("the original method!");
}
public class Proxy implements Sourceable {
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
测试类
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
输出:
before proxy!
the original method!
after proxy!
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
下面我们要说的Java依赖注入可以说就是proxy模式的一种应用,这里总结一下常用的三种依赖注入方法
1. 接口注入
我们常常借助接口来将调用者与实现者分离。如:
public class ClassA {
private InterfaceB clzB;
public init() {
Ojbect obj =
Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
}
……
}
上面的代码中,ClassA依赖于InterfaceB 的实现,如何获得InterfaceB 实现类的实例?传统的方法是在代码中创建InterfaceB 实现类的实例,并将起赋予clzB。而这样一来,ClassA在编译期即依赖于InterfaceB 的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码,我们根据预先在配置文件中设定的实现类的类名,动态加载实现类,并通过InterfaceB 强制转型后为ClassA所用。
这就是接口注入的一个最原始的雏形。而对于一个接口注入型IOC容器而言,加载接口实现并创建其实例的工作由容器完成,如J2EE开发中常用的Context.lookup(ServletContext.getXXX),都是接口注入型IOC的表现形式。
Apache Avalon是一个典型的Type1型IOC容器。
2. 构造子注入
构造子注入,即通过构造函数完成依赖关系的设定,如:
public class DIByConstructor {
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg) {
this.dataSource = ds;
this.message = msg;
}
……
}
可以看到,在Type2类型的依赖注入机制中,依赖关系是通过类构造函数建立,容器通过调用类的构
造方法,将其所需的依赖关系注入其中。
PicoContainer(另一种实现了依赖注入模式的轻量级容器)首先实现了Type2类型的依赖注入模式。
3. 设置注入
这种方式广泛应用在Spring框架的参数配置中,客户代码仅仅面向接口编程,而无需知道实现类的具体名称。同时,我们可以很简单的通过修改配置文件来切换具体的底层实现类。下面我们抛开Spring框架本身,使用一个简单的例子来实现类似Spring的依赖注入
public class ActionFactory {
public static Action getAction(String actionName) {
Properties pro = new Properties();
try {
pro.load(new FileInputStream("config.properties"));
String actionImplName = (String) pro.get(actionName);
String actionMessage = (String) pro.get(actionName + "_msg");
Object obj = Class.forName(actionImplName).newInstance();
// BeanUtils是Apache Commons BeanUtils提供的辅助类
BeanUtils.setProperty(obj, "message", actionMessage);
return (Action) obj;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
配置文件则采用最简单的properties文件形式:
TheAction=net.xiaxin.spring.qs.UpperAction
TheAction_msg=HeLLo
测试类:
public void testFactory(){
Action action = ActionFactory.getAction("TheAction");
System.out.println(action.execute("Rod Johnson"));
}
上面的例子中,我们通过设置注入,在运行期动态将字符串“HeLLo” 注入到Action实现类的Message属性中。用同样的方法,我们可以通过修改配置文件让程序调用不同的实现方法,从而实现灵活的依赖注入。在Hive源码中,HiveDriverRunHook类也是使用这种方式实现注入的
网友评论