依赖注入
属性注入
构造方法注入
给Bean添加对应的构造方法
public class Book {
private Integer id;
private String name;
private Double price;
public Book() {
System.out.println("-------book init----------");
}
public Book(Integer id, String name, Double price) { this.id = id;
this.name = name;
this.price = price;
}
}
XML注入Bean
<bean class="org.javaboy.Book" id="book">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="三国演义"/>
<constructor-arg index="2" value="30"/>
</bean>
constructor-arg中的index和Book中的构造方法参数一一对应,写的顺序可以颠倒,但是index值和value要一一对应
另一种构造方法中的属性注入,则是通过直接指定参数名来注入:
<bean class="org.javaboy.Book" id="book2">
<constructor-arg name="id" value="2"/>
<constructor-arg name="name" value="红楼梦"/>
<constructor-arg name="price" value="40"/>
</bean>
如果有多个构造方法,则会根据给出参数个数以及参数类型,自动匹配到对应
的构造方法上,进而初始化一个对象。
set方法注入
<bean class="org.javaboy.Book" id="book3">
<property name="id" value="3"/>
<property name="name" value="水浒传"/>
<property name="price" value="30"/>
</bean>
set 方法注入,有一个很重要的问题,就是属性名。很多人会有一种错觉,觉得属性名就是你定义的属性名,这个是不对的。在所有的框架中,凡是涉及到反射注入值的,属性名统统都不是 Bean 中定义的属性名,而是通过 Java 中的内省机制分析出来的属性名,简单说,就是根据 get/set 方法分析出来的属性名。
p名字空间注入
p 名称空间注入,使用的比较少,它本质上也是调用了 set 方法。
<bean class="org.javaboy.Book" id="book4" p:id="4" p:bookName="西游记" p:price="33"></bean>
外部Bean的注入
有时候,我们使用一些外部 Bean,这些 Bean 可能没有构造方法,而是通过 Builder
来构造的,这个时候,就无法使用上面的方式来给它注入值了。
例如在 OkHttp 的网络请求中,原生的写法如下:
public class OkHttpMain {
public static void main(String[] args) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.build();
Request request = new Request.Builder()
.get()
.url("http://b.hiphotos.baidu.com/image/h%3D300/sign=ad628627aacc7cd9e52d32d909032104/32fa828ba61ea8d3fcd2e9ce9e0a304e241f5803.jpg")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
System.out.println(e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
FileOutputStream out = new FileOutputStream(new File("E:\\123.jpg"));
int len;
byte[] buf = new byte[1024];
InputStream is = response.body().byteStream();
while ((len = is.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.close();
is.close();
}
});
}
}
这个 Bean 有一个特点,OkHttpClient 和 Request 两个实例都不是直接 new
出来的,在调用 Builder 方法的过程中,都会给它配置一些默认的参数。这种情况,我们可以使用 静态工厂注入或者实例工厂注入来给 OkHttpClient
提供一个实例。
1.静态工厂注入
首先提供一个 OkHttpClient 的静态工厂:
public class OkHttpUtils {
private static OkHttpClient OkHttpClient;
public static OkHttpClient getInstance() {
if (OkHttpClient == null) {
OkHttpClient = new OkHttpClient.Builder().build();
}
return OkHttpClient;
}
}
在 xml 文件中,配置该静态工厂:
<bean class="org.javaboy.OkHttpUtils" factory-method="getInstance" id="okHttpClient"></bean>
这个配置表示 OkHttpUtils 类中的 getInstance 是我们需要的实例,实例的名字就叫 okHttpClient。然后,在 Java 代码中,获取到这个实例,就可以直接使用了。
public class OkHttpMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OkHttpClient okHttpClient = ctx.getBean("okHttpClient", OkHttpClient.class);
Request request = new Request.Builder()
.get()
.url("http://b.hiphotos.baidu.com/image/h%3D300/sign=ad628627aacc7cd9e52d32d909032104/32fa828ba61ea8d3fcd2e9ce9e0a304e241f5803.jpg")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
System.out.println(e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
FileOutputStream out = new FileOutputStream(new File("E:\\123.jpg"));
int len;
byte[] buf = new byte[1024];
InputStream is = response.body().byteStream();
while ((len = is.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.close();
is.close();
}
});
}
}
2.实例工厂注入
实例工厂就是工厂方法是一个实例方法,这样,工厂类必须实例化之后才可以调用工厂方法。
这次的工厂类如下
public class OkHttpUtils {
private OkHttpClient OkHttpClient;
public OkHttpClient getInstance() {
if (OkHttpClient == null) {
OkHttpClient = new OkHttpClient.Builder().build();
}
return OkHttpClient;
}
}
此时,在 xml 文件中,需要首先提供工厂方法的实例,然后才可以调用工厂方法:
<bean class="org.javaboy.OkHttpUtils" id="okHttpUtils"/>
<bean class="okhttp3.OkHttpClient" factory-bean="okHttpUtils" factory-method="getInstance" id="okHttpClient"></bean>
自己写的 Bean 一般不会使用这两种方式注入,但是,如果需要引入外部 jar,外部 jar 的类的初始化,有可能需要使用这两种方式。
复杂属性注入
对象注入
<bean class="org.javaboy.User" id="user">
<property name="cat" ref="cat"/>
</bean>
<bean class="org.javaboy.Cat" id="cat">
<property name="name" value="小白"/>
<property name="color" value="白色"/>
</bean>
可以通过 xml 注入对象,通过 ref 来引用一个对象。
数组注入
数组注入和集合注入在xml中的配置时一样的:
<bean class="org.javaboy.User" id="user">
<property name="cat" ref="cat"/>
<property name="favorites">
<array>
<value>足球</value>
<value>篮球</value>
<value>乒乓球</value>
</array>
</property>
</bean>
<bean class="org.javaboy.Cat" id="cat">
<property name="name" value="小白"/>
<property name="color" value="白色"/>
</bean>
注意,array 节点,也可以被 list 节点代替。
当然,array 或者 list 节点中也可以是对象。
<bean class="org.javaboy.User" id="user">
<property name="cat" ref="cat"/>
<property name="favorites">
<list>
<value>足球</value>
<value>篮球</value>
<value>乒乓球</value>
</list>
</property>
<property name="cats">
<list>
<ref bean="cat"/>
<ref bean="cat2"/>
<bean class="org.javaboy.Cat" id="cat3">
<property name="name" value="小花"/>
<property name="color" value="花色"/>
</bean>
</list>
</property>
</bean>
<bean class="org.javaboy.Cat" id="cat">
<property name="name" value="小白"/>
<property name="color" value="白色"/>
</bean>
<bean class="org.javaboy.Cat" id="cat2">
<property name="name" value="小黑"/>
<property name="color" value="黑色"/>
</bean>
注意,即可以通过 ref 使用外部定义好的 Bean,也可以直接在 list 或者 array 节点中定义 bean。
Map注入
<property name="map">
<map>
<entry key="age" value="99"/>
<entry key="name" value="javaboy"/>
</map>
</property>
Properties注入
<property name="info">
<props>
<prop key="age">99</prop>
<prop key="name">javaboy</prop>
</props>
</property>