属性注入:通过setXxx()方法注入Bean的属性值或依赖对象,它的灵活性较高,使用率高。
看例子:
public class Car {
private String brand;
private String color;
private String maxSpeed;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(String maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
再看Bean.xml的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.baobaobao.Car" >
<property name="brand" value="红旗CA"></property>
<property name="color" value="黑色"></property>
<property name="maxSpeed" value="200"></property>
</bean>
</beans>
属性注入要求提供一个默认的构造函数,并为需要注入的属性提供对应的setter()方法,Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射调用setter方法注入属性值。
并且Spring只检查Bean中是否有 对应的setter方法,对于该Bean中是否有对应的属性变量则不要求,例如:
package com.baobaobao;
public class Car {
private String brand;
private String color;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void setMaxSpeed(String maxSpeed) {
System.out.println("speed的setter......");
}
}
配置文件跟上面一样,没有speed属性,依旧可以,但一般情况我们预定俗称提供同名的成员变量。
构造函数注入:
构造函数注入是除了属性注入以外另一种常用注入方式,它可以保证一些必要的属性在Bean实例化时就得到设置,保证了Bean在实例化后就可以使用。
1)按类型匹配入参:
如果要任何可用的Car对象都必须提供brand和price的值,如果使用属性注入的方式只能在人为配置时提供保证而无法在语法级别提供保证,这时候构造函数注入就派上用场了。使用构造函数注入的前提是必须提供带参的构造函数。如下:
package com.baobaobao;
public class Car {
private double price;
private String color;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Car(double price,String color){
this.price = price;
this.color = color;
}
}
加入一个带参的构造器,再看配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.baobaobao.Car" >
<constructor-arg type="double">
<value>200000</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>红色</value>
</constructor-arg>
</bean>
</beans>
在<constructor-arg>元素中有一个type属性,它为Spring提供了判断配置项和构造函数入参对应关系的信息。而且Spring标签属性跟元素无关的策略,保障了配置文件的正确性。例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.baobaobao.Car" >
<constructor-arg type="java.lang.String">
<value>红色</value>
</constructor-arg>
<constructor-arg type="double">
<value>200000</value>
</constructor-arg>
</bean>
</beans>
顺序改变,依旧不会影响。。。。
2)按索引匹配入参:
如果通过type属性来区分属性显然不现实,如果俩属性类型相同就完蛋了,这是我们可以通过入参索引进行确定。
因为Java反射机制无法记住构造函数的入参名,因此我们无法指定构造函数的入参名来进行构造函数注入的配置,只能通过入参类型和入参索引间接地确定构造函数配置项和入参的对应关系。如下:
package com.baobaobao;
public class Car {
private double price;
private String color;
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Car(double price,String color,String brand){
this.price = price;
this.color = color;
this.brand = brand;
}
}
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.baobaobao.Car" >
<constructor-arg index="0">
<value>200000</value>
</constructor-arg>
<constructor-arg index="1">
<value>红色</value>
</constructor-arg>
<constructor-arg index="2">
<value>奥迪</value>
</constructor-arg>
</bean>
</beans>
索引从0开始,表示着在构造函数入参的顺序索引。
3)联合使用类型和索引匹配入参:
有时候类型跟索引要联合出马才能确定配置项跟构造函数入参的对应关系,如下:
public class Car {
private double price;
private String color;
private String brand;
private int maxSpeed;
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Car(double price,String color,String brand){
this.price = price;
this.color = color;
this.brand = brand;
}
public Car(String color,int maxSpeed,String brand){
this.color = color;
this.maxSpeed = maxSpeed;
this.brand = brand;
}
}
该Bean有两个重载的构造函数,他们都有3个入参,如果只按索引肯定没法确定对应关系,可以type跟index结合用:如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.baobaobao.Car" >
<constructor-arg index="0" type="java.lang.String">
<value>红色</value>
</constructor-arg>
<constructor-arg index="1" type="int">
<value>200</value>
</constructor-arg>
<constructor-arg index="2" type="java.lang.String">
<value>奥迪</value>
</constructor-arg>
</bean>
</beans>
这个配置即对应上面Car的第二个构造函数。。。。
4)通过自身类型反射匹配入参:
如果Bean构造函数入参的类型是可辨别的(非基础数据类型且入参类型各异),由于java反射机制可以获取构造函数入参的类型,即使构造函数注入的配置不提供类型和索引,Spring依旧可以完成构造函数注入工作。
如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="boss" class="com.baobaobao.Boss">
<constructor-arg>
<value>James</value>
</constructor-arg>
<constructor-arg>
<ref bean="car"/>
</constructor-arg>
</bean>
<bean id="car" class="com.baobaobao.Car" >
<constructor-arg index="0" type="java.lang.String">
<value>红色</value>
</constructor-arg>
<constructor-arg index="1" type="int">
<value>200</value>
</constructor-arg>
<constructor-arg index="2" type="java.lang.String">
<value>奥迪</value>
</constructor-arg>
</bean>
</beans>
Car类跟上面的例子一样,这样既可实现注入,但如果Bean存在多个构造函数,显示的指定type和index依然是一种良好的习惯。
5)循环依赖问题:
Spring容器能够顺利实例化以构造器注入配置的Bean有一个前提:Bean构造函数的入参引用的对象必须已经准备就绪,由于这个机制限制,如果两个Bean都采用构造函数注入,而且通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。
工厂方法注入:
1)非静态工厂方法
先提供一个工厂类,如下:
package cn.com.demo.po;
public class CarFactory {
public Car createAodiCar(){
Car car = new Car();
car.setBrand("奥迪A4");
return car;
}
}
然后applicationContext.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="carFactory" class="cn.com.demo.po.CarFactory"></bean>
<bean id="car5" factory-bean="carFactory" factory-method="createAodiCar"></bean>
</beans>
然后使用如下的测试类跑一下:
public class SpringRun {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Car car = ac.getBean("car5", Car.class);
System.out.println(car.getBrand());
}
}
完美运行!!!~
由于CarFactory不是静态的,所以首先要定义一个工厂类的Bean,然后通过factory-bean引入工厂类实例,再通过factory-method指定对应的工厂方法。
2)静态工厂方法:
很多工厂方法都是静态的,不需要用户创建工厂实例就可以调用他们,修改一下CarFactory,如下:
public class CarFactory {
public static Car createAodiCar(){
Car car = new Car();
car.setBrand("奥迪A4");
return car;
}
}
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="car6" class="cn.com.demo.po.CarFactory" factory-method="createAodiCar" />
</beans>
当使用了静态工厂类型方法后,用户无需再配置文件中定义工厂类的Bean了,只需要按如上配置即可,直接在bean的class里指定工厂类,然后通过factory-method指定工厂方法。
关于选择注入方式:
仁者见仁,智者见智,例如构造函数注入的优点:
1)可以保证Bean需要的属性在实例化后就已经设置好,避免一些重要的属性没有提供,导致一个无用的Bean实例产生。
2)不需要为每个属性提供setter方法,减少了类中方法的个数。
3)更好的封装变量,没有提供setter方法,可以避免外部错误的引用。
而它的缺点也有:
1)如果一个类属性众多,那么构造函数签名会变得庞大,可读性差。
2)灵活性差,如果有些属性时可选的情况下,如果构造器注入,需要为这些属性提供一个null值。
3)如果构造函数多的话,配置文件为了区别开来这些构造函数,配置变得复杂。
4)构造函数有时候会造成循环依赖问题。
用户完全可以根据这两种注入方式的优缺点在特点的情况下选择,没有强制规定,不过工厂方法因为引入了额外的类跟方法,不推荐,Spring已经实现了工厂模式的所有功能,不需要画蛇添足了。。。。