第四章、Spring 依赖注入
一、构造器注入
构造器注入bean子节点constructor-arg节点
可以使用constructor-arg节点属性index,name,type
基本类型注入:使用value
引用类型注入:使用ref
1、index 构造方法参数的索引
public StudentService(String name, StudentDao studentDao) {
System.out.println("StudentService(name,studentDao)构造方法...name="+name+",studentDao="+studentDao);
this.name = name;
this.studentDao = studentDao;
}
<bean id="studentDao" class="com.tjetc.dao.StudentDao"></bean>
<bean id="studentService" class="com.tjetc.service.StudentService">
<constructor-arg index="0" value="张三"></constructor-arg>
<constructor-arg index="1" ref="studentDao"></constructor-arg>
</bean>
2、name 构造方法参数的名称
<bean id="studentDao" class="com.tjetc.dao.StudentDao"></bean>
<bean id="studentService" class="com.tjetc.service.StudentService">
<constructor-arg name="name" value="张三2"></constructor-arg>
<constructor-arg name="studentDao" ref="studentDao"></constructor-arg>
</bean>
3、type 构造方法参数的类型
<bean id="studentDao" class="com.tjetc.dao.StudentDao"></bean>
<bean id="studentService" class="com.tjetc.service.StudentService">
<constructor-arg name="name" type="java.lang.String" value="张三2"></constructor-arg>
<constructor-arg name="studentDao" ref="studentDao"></constructor-arg>
</bean>
二、属性set方法注入
使用bean类的属性的set方法注入属性值:
1.bean类添加属性的setter方法
2.在bean的子节点property
name:是setXxx()方法后单词的首字母变小写后的单词,不是属性名
value:代表的是值
ref:代表的是引用类型的bean的id的值
<bean id="studentDao" class="com.tjetc.dao.StudentDao"></bean>
<bean id="studentService" class="com.tjetc.service.StudentService">
<property name="xxx" value="李四"></property>
<property name="studentDao" ref="studentDao"></property>
</bean>
Set注入与构造器注入对比:
- Set注入模式代码更加简洁
- 构造器注入对依赖关系的表达更加清楚
- Set注入可以避免循环依赖问题
三、集合属性注入
1、list
(1)基本数据类型
<property name="hobbies">
<list>
<value>吃</value>
<value>喝</value>
<value>玩</value>
<value>乐</value>
<value>读书</value>
</list>
</property>
(2)引用类型
<property name="books">
<list>
<ref bean="book1"/>
<ref bean="book2"/>
<ref bean="book3"/>
</list>
</property>
2、数组(与list相同)
3、set(把list标签改为set标签,其他与list一样)
4、map
(1)基本数据类型
<property name="hobbies">
<map>
<entry key="chi" value="吃"></entry>
<entry key="he" value="喝"></entry>
<entry key="wan" value="完"></entry>
</map>
</property>
(2)引用类型
<property name="books">
<map>
<entry key="xi" value-ref="book1"></entry>
<entry key="dong" value-ref="book2"></entry>
<entry key="nan" value-ref="book3"></entry>
</map>
</property>
5、properties
<property name="properties">
<props>
<prop key="pk1">pv1</prop>
<prop key="pk2">pv2</prop>
<prop key="pk3">pv3</prop>
<prop key="pk4">pv4</prop>
</props>
</property>
Properties properties = student.getProperties();
Set<Object> keySet2 = properties.keySet();
for (Object k : keySet2) {
// String k=(String) object;
System.out.println(properties.get(k));
四、p命名空间
简化property节点
1、引入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
2、使用p命名空间
(1)基本数据类型
p:属性=值
(2)引用类型
p:属性-ref=另一个bean的id的值
<bean id="book" class="com.tjetc.domain.Book" p:name="西游记" p:price="30"></bean>
<bean id="student" class="com.tjetc.domain.Student" p:name="张三" p:book-ref="book"></bean>
五、c命名空间
简化constructor-arg节点
1、引入c命名空间
xmlns:c="http://www.springframework.org/schema/c"
2、使用c命名空间
(1)基本数据类型
c:构造方法参数名=值
(2)引用类型
c:构造方法参数名-ref=另一个bean的id的值
<bean id="book" class="com.tjetc.domain.Book" p:name="西游记" p:price="30"></bean>
<bean id="student" class="com.tjetc.domain.Student" c:name="李四" c:book-ref="book"></bean>
六、depends-on属性
B中没有A,在applicationContext.xml中配置depends-on=“a”,则先实例化A,再实例化B,销毁是先销毁B,再销毁A
结论:依赖对象先创建后销毁.
public class A {
public A() {
System.out.println("A()...");
}
public void init() {
System.out.println("A.init()...");
}
public void destroy() {
System.out.println("A.destroy()...");
}
}
public class B {
public B() {
System.out.println("B()...");
}
public void init() {
System.out.println("B.init()...");
}
public void destroy() {
System.out.println("B.destroy()...");
}
public void say() {
System.out.println("say():hello...");
}
}
<bean id="b" class="com.tjetc.domain.B" depends-on="a" init-method="init" destroy-method="destroy"></bean>
<bean id="a" class="com.tjetc.domain.A" init-method="init" destroy-method="destroy"></bean>
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
B b = context.getBean(B.class);
b.say();
context.close();
A()...
A.init()...
B()...
B.init()...
say():hello...
B.destroy()...
A.destroy()...
七、延迟加载lazy-init
ApplicationContext的默认行为就是在创建IOC容器时将所有singleton 的bean提前进行实例化。
系统默认配置是lazy-init=“false”
当配置lazy-init="true"后,当第一次调用bean对象时,才进行实例
1、lazy-init="true"创建IOC容器时没有实例化bean,当第一次调用context.getBean(C.class)才进行实例化bean
2、lazy-init="true"只对当前的bean有效,对其他的bean不起作用
3、让所有的bean都设置成懒加载
beans节点设置 default-lazy-init=“true” ,让所有的bean都设置成懒加载.
4、default-lazy-init=“true” 所有bean懒加载的前提下,设置某个bean不懒加载 bean设置lazy-init=“false”
八、Spring注解配置Bean
Spring自动扫描bean,把类上有@Componet,@Controller,@Service,@Repository注解的bean纳入Spring容器管理
- @Component 不好分层的时候用该注解
- @Controller 控制层使用的注解
- @Service 业务层使用该注解
- @Repository dao层使用的注解
扫描包内注解的类**<context:component-scan base-package=”com.tjetc”>**
让spring扫描com.tjetc包及其子孙包下的在类上有@Component,@Controller,@Service,@Repository之一注解的类,Spring会把含有这些注解的类纳入spring管理,就相当于在applicationContext.xml配置了<bean id="" class="xxx.yyy.类">
九、AutoWire注入
1.自动注入,默认按类型注入
2.如果同一接口有多个实现类就相当于同一个类型在spring容器中有多个相同类型的对象,Spring不知道使用哪一个对象,需要我们告诉他:
- 把成员变量的名字改为spring容器中bean的名字
- 把要用的实现类中@Repository(“成员变量的名字”)
- 把不用的类上的注解去掉(不推荐使用)
- 在每个实现类的注解写不同的名字,在引用的地方在@AutoWired 后面写@Qualified(“bean的名字”) --推荐使用
@Service//<bean id="userServiceImpl" class="com.tjetc.service.impl.UserServiceImpl">
public class UserServiceImpl implements UserService {
@Autowired//让容器把它创建好的对象注入进来,依赖注入DI:Dependency Injection
private UserDao userDao;
@Override
public void login() {
userDao.login();
}
}
@Repository//<bean id="userDao" class="com.tjetc.dao.impl.UserDaoImpl">
public class UserDaoImpl implements UserDao {
@Override
public void login() {
System.out.println("UserDaoImpl.login()...");
}
}
@Qualifier按照名称注入
@Service//<bean id="userServiceImpl" class="com.tjetc.service.impl.UserServiceImpl">
public class UserServiceImpl implements UserService {
//@Autowired:自动注入,默认按类型注入
@Autowired//让容器把它创建好的对象注入进来,依赖注入DI:Dependency Injection
//@Qualifier("userDaoOracle")//按照名称注入
private UserDao userDao;//userDaoMysqlImpl:bean的名称
@Override
public void login() {
userDao.login();
}
/**
* <bean id="userDaoMysqlImpl" class="com.tjetc.dao.impl.UserDaoImpl">
* <bean id="userServiceImpl" class="com.tjetc.service.impl.UserServiceImpl">
* <property name="userDao" ref="userDaoMysqlImpl">
* </bean>
*/
}
Autowired模式的缺陷
- 对于java基本类型和String等简单类型,无法使用Autowire方式注入
- 业务变化,注入的配置项必须改变时,没有xml配置修改容易。例如:
@Component(“userDaoMysql”)
public class UserDaoMySqlImpl implements UserDao{ - 同一接口的多个实现类同时使用时,容易引发冲突
十、Spring中,支持 5种 自动装配模式
Spring支持5种自动装配模式:
- no——默认情况下,不自动装配,通过“ref”attribute手动设定。
- byName——根据Property的Name自动装配,如果一个bean的name,和另一个bean中的Property的name相同,则自动装配这个bean到Property中。
- byType——根据Property的数据类型(Type)自动装配,如果一个bean的数据类型,兼容另一个bean中Property的数据类型,则自动装配。
- constructor——根据构造函数参数的数据类型,进行byType模式的自动装配。
- autodetect——如果发现默认的构造函数,用constructor模式,否则,用byType模式。
十一、Bean的scope属性
(一)单例singletone
当定义一个bean定义并且它的作用域是一个singleton时,Spring IoC容器创建由该bean定义的对象的一个实例。 这个单实例存储在这个单例bean的缓存中,该bean的所有后续请求和引用都返回从缓存中获得对象。与golf的单例模式不一样的.
spring容器默认scope的是singleton:
注意:scope=singleton情况下,bean的构造方法什么时候执行的?
答:实例化容器的时候执行.
(二)多例prototype
当scope="prototype"时,每次调用getBean(“bean的名称”),都会生成一个新的对象。
注意:scope=prototype情况下,bean的构造方法什么时候执行的?
答:实例化容器的时候不执行.每次调用getBean(“bean的名称”)的时候执行.
十二、Bean的生命周期回调处理(bean初始化和销毁)
- 使用JSR-250 @PostConstruct 和 @PreDestroy 是bean对象生命周期callback的最好方式。
- 使用Spring 的InitializingBean 和 DisposableBean 接口也可以。使用接口的弊端是callback管理与Spring的代码产生了耦合,带来了不必要的麻烦。