共计 10132 个字符,预计需要花费 26 分钟才能阅读完成。
前面几个章节介绍了 bean 的定义,在一个完整的应用中,各个 bean 并不是相互独立的,而是一起工作的,在 spring 中,通过注入来把这些独立的 bean 串起来。
基于构造方法的注入
XML
在 xml 配置文件中,是通过 constructor-arg
标签来实现注入。
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="byType" class="com.learn.di.Person">
<constructor-arg type="java.lang.String" value="张三"/>
<constructor-arg type="int" value="18"/>
</bean>
<bean id="byName" class="com.learn.di.Person">
<constructor-arg name="name" value="李四"/>
<constructor-arg name="age" value="19"/>
</bean>
<bean id="byIndex" class="com.learn.di.Person">
<constructor-arg index="0" value="王五"/>
<constructor-arg index="1" value="20"/>
</bean>
</beans>
person 代码:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试代码
@Test
public void test() {ApplicationContext app = new ClassPathXmlApplicationContext("di.xml");
Person person1 = app.getBean("byType",Person.class);
Person person2 = app.getBean("byName",Person.class);
Person person3 = app.getBean("byIndex",Person.class);
System.out.println(person1);
System.out.println(person2);
System.out.println(person3);
}
运行结果如下:
第一个是通过类型注入,第二个是通过名称注入,第三个是通过索引位置注入。
注解
在注解中,常用的注解是 @Autowired。
MyService,MyController
@Service
public class MyService {
}
@Controller
public class MyController {
MyService myService;
public MyController(@Autowired MyService myService) {this.myService = myService;}
}
MyConfig
@Configuration
@ComponentScan(value="com.learn.annotation2")
public class MyConfig {}
测试代码:
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
MyController myController=app.getBean("myController",MyController.class);
System.out.println(myController.myService);
System.out.println(app.getBean("myService"));
}
运行结果如下:
两次打印的地址是一样的,说明已经成功注入到 myController 中。如果想实现上面 XML 那种基本类型的注入,可以用 @Bean 注解,通过 return new 一个对象来实现。
基于 setter 方法的注入
XML
di.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person2" class="com.learn.di.Person2">
<property name="name" value="赵六"/>
<property name="age" value="21"/>
</bean>
</beans>
person2 代码如下:
public class Person2 {
private String name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试代码:
@Test
public void test2() {ApplicationContext app = new ClassPathXmlApplicationContext("di.xml");
Person2 person2 = app.getBean("person2",Person2.class);
System.out.println(person2);
}
运行结果如下:
不同于构造器注入,setter 只能通过名称注入。
ref
上面注解可以直接注入容器的对象,在 XML 配置中,也可以使用 ref 来注入。
di2.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="di.xml"/>
<bean id="persons" class="com.learn.di.Persons">
<constructor-arg name="person1" ref="byName"/>
<property name="person2" ref="byType"/>
</bean>
</beans>
Persons 代码如下:
public class Persons {
Person person1;
Person person2;
public Persons(Person person1) {this.person1 = person1;}
public Person getPerson2() {return person2;}
public void setPerson2(Person person2) {this.person2 = person2;}
@Override
public String toString() {
return "Persons{" +
"person1=" + person1 +
", person2=" + person2 +
'}';
}
}
运行结果如下:
ref 后面的值,是 bean 的标识符。
p-namespace 和 c -namespace
dim5.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person3" class="com.learn.di.Person3" c:tel="13800138000" p:name="张三" p:age="18"/>
</beans>
Person3
public class Person3 {
private String name;
private int age;
private String tel;
public Person3(String tel) {this.tel = tel;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {
return "Person3{" +
"name='" + name + '\'' +
", age=" + age +
", tel='" + tel + '\'' +
'}';
}
}
测试代码:
@Test
public void test6() {ApplicationContext app = new ClassPathXmlApplicationContext("di5.xml");
Person3 person3 = app.getBean("person3", Person3.class);
System.out.println(person3);
}
运行结果
这种写法,要引入 xmlns:p 和 xmlns:c。p 相对于 property,c 相对于 constructor-arg。
注解
在注解中,方法名也不一定要 setXXX,而且 @Autowired 支持直接在成员上面使用。
MyController
@Controller
public class MyController {
@Autowired
MyService myService;
MyDao myDao;
@Autowired
public void myDao(MyDao myDao) {this.myDao = myDao;}
}
测试代码:
@Test
public void test(){ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
MyController myController=app.getBean("myController",MyController.class);
System.out.println(myController.myService);
System.out.println(myController.myDao);
System.out.println(app.getBean("myService"));
System.out.println(app.getBean("myDao"));
}
运行结果如下:
两次打印的地址是一样的,说明已经成功注入到 myController 中。
构造器注入和 setter 注入
在系统中,我们既可以用构造器注入,也可以用 setter 注入,甚至可以两种一起用,都可以达到我们想要的效果。
构造器注入:强依赖,大量的构造函数参数不是很优雅,无法解决循环依赖的问题,会抛出 BeanCurrentlyInCreationException 异常。
setter 注入:可选依赖,可以重新注入。
内部类注入
di3.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="outer" class="com.learn.di.Outer">
<property name="inner">
<bean class="com.learn.di.Outer.Inner">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</property>
</bean>
</beans>
Outer 类
public class Outer {
private Inner inner;
public Inner getInner() {return inner;}
public void setInner(Inner inner) {this.inner = inner;}
static class Inner{
private String name;
private int age;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {
return "Inner{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Override
public String toString() {
return "Outer{" +
"inner=" + inner +
'}';
}
}
测试代码
@Test
public void test4() {ApplicationContext app = new ClassPathXmlApplicationContext("di3.xml");
Outer outer = app.getBean("outer", Outer.class);
System.out.println(outer);
}
运行结果:
内部类的 bean 定义,不需要 id 和 name。因为是内部类,所以即便设置了,容器也会忽略。
集合
di4.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="abstractCollect" abstract="true" class="com.learn.di.AbstractCollect">
<property name="list">
<list merge="true">
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="map">
<map merge="true">
<entry value="name" key="张三"/>
<entry value="age" key="18"/>
</map>
</property>
<property name="set">
<set merge="true">
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<property name="properties">
<props merge="true">
<prop key="name"> 李四 </prop>
<prop key="age">20</prop>
</props>
</property>
</bean>
<bean id="collect" class="com.learn.di.Collect" parent="abstractCollect">
<property name="list">
<list merge="true">
<value>ddd</value>
<value>eee</value>
<value>fff</value>
</list>
</property>
<property name="map">
<map merge="true">
<entry value="name" key="王五"/>
<entry value="age" key="19"/>
</map>
</property>
<property name="set">
<set merge="true">
<value>ddd</value>
<value>eee</value>
<value>fff</value>
</set>
</property>
<property name="properties">
<props merge="true">
<prop key="sex"> 男 </prop>
<prop key="tel">13800138000</prop>
</props>
</property>
</bean>
</beans>
AbstractCollect 和 Collect
public abstract class AbstractCollect {
List<String> list;
Set<String> set;
Map<String,String> map;
Properties properties;
public List<String> getList() {return list;}
public void setList(List<String> list) {this.list = list;}
public Set<String> getSet() {return set;}
public void setSet(Set<String> set) {this.set = set;}
public Map<String, String> getMap() {return map;}
public void setMap(Map<String, String> map) {this.map = map;}
public Properties getProperties() {return properties;}
public void setProperties(Properties properties) {this.properties = properties;}
@Override
public String toString() {
return "AbstractCollect{" +
"list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
public class Collect extends AbstractCollect{}
测试类:
@Test
public void test5() {ApplicationContext app = new ClassPathXmlApplicationContext("di4.xml");
Collect collect = app.getBean("collect", Collect.class);
System.out.println(collect);
}
运行结果如下:
merge 为 true 且设置了 parent 的时候,可以继承父类的信息。merge 的时候,必须是同一类型的对象。
其他
空值的处理
如果是空字符串,以下是等同的。
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
exampleBean.setEmail("");
如果是 null,以下是等同的。
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
exampleBean.setEmail(null);
depends-on 和 @DependsOn
当 A 对象包含 B 对象的时候,A 对象初始化时,B 对象必须初始完成。如果 A 对象没有包含 B 对象,也需要 B 对象初始化完成后,才初始化 A,那可以用 depends-on。
di6.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="one" class="com.learn.di.One" depends-on="two"/>
<bean id="two" class="com.learn.di.Two"/>
</beans>
One 和 Two
public class One {public One(){System.out.println("one init");
}
}
public class Two {public Two(){System.out.println("two init");
}
}
测试代码
@Test
public void test7() {new ClassPathXmlApplicationContext("di6.xml");
}
当有 depends-on=”two” 的时候,运行结果如下:
否则运行结果如下
说明 depends-on 生效了。@DependsOn 这边就不做例子了。