依赖注入其实是spring的IOC容器控制用户类中对象的创建过程,首先需要知道如何在xml文件中配置对象创建
<bean id="springUser" class="com.cityu.b_createObj.User">bean>
<bean id="user2" class="com.cityu.b_createObj.User">
<constructor-arg value="17">constructor-arg>
<constructor-arg value="BJT">constructor-arg>
bean>
//实例方法创建
<bean id="factory" class="com.cityu.b_createObj.InstanceFactory">bean>
<bean id="user" factory-bean="factory" factory-method="getInstance">bean>
//静态方法创建
<bean id="user1" class="com.cityu.b_createObj.InstanceFactory" factory-method="getStaticInstance" lazy-init="true">bean>
对应工厂类:
public class InstanceFactory {
public User getInstance(){
return new User(1,"instance");
}
public static User getStaticInstance(){
return new User(2,"Static instance");
}
}
对于初学Spring的人来说,总觉得IOC 、DI这两个概念是模糊不清的,是很难理解的。
其实,java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用他的合作对象(也叫依赖对象)时,自己均要使用像new关键字或者工厂方法来完成合作对象的申请工作,同时还要自己来管理合作对象的生命周期,例如关闭资源对象(如数据库连接Connection对象)。你会发现:对象间的耦合度高了。
而IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关注于业务逻辑本身就可以了。从这方面来说,对象如何得到他的合作对象的责任被反转了。
更通俗地讲,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转(IOC)。
IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如userService需要操作Dao,以前我们总是要在userService中自己new一个Dao对象然后使用Dao对数据库的增删改查方法,有了spring我们就只需要告诉spring,userService这个对象需要在执行业务时需要一个Dao对象,至于这个Dao对象怎么构造,何时构造,userService是不需要知道。在系统运行时,spring会根据配置文件在适当的时候制造一个Dao,然后像打针一样,注射到userService当中,这样就完成了对各个对象之间关系的控制。userService需要依赖Dao对象才能运行(不然会报空指针异常)而Dao对象则是被spring容器注入到userService中去的,依赖注入的名字就这么来的。
下面可以通过设置依赖对象来进一步体会
如何给对象属性赋值?
<bean id="user1" class="com.cityu.b_createObj.User" lazy-init="true">
<constructor-arg name="id" value="1">constructor-arg>
<constructor-arg name="name" value="BJT1">constructor-arg>
bean>
<bean id="user" class="com.cityu.b_createObj.User" lazy-init="true">
<property name="id" value="2">property>
<property name="name" value="BJT2">property>
bean>
通常在service中需要调用dao对象,而在action中需要调用service对象,将创建对象的控制权转移给配置文件(需要在Bean中提供public的setter方法),这叫IOC(Inversion of Control),控制反转。
而其中对象之间的依赖关系(例如,service中的service()方法需要使用dao对象,因此在创建service对象时,必须同时创建dao对象作为它的成员属性)也是通过配置文件完成,这叫做DI(Dependency Injection),依赖注入。想完成对依赖对象的注入,必须提供public的set方法,提供给容器来创建对象:
id="userDao" class="com.cityu.app.UserDao">
id="userService" class="com.cityu.app.UserService">
<property name="userDao" ref="userDao">property>
id="userAction" class="com.cityu.app.UserAction">
<property name="userService" ref="userService">property>
这样,就可以从spring的IOC容器中去取Bean对象:
public class App {
private ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("com/cityu/app/applicationContext.xml");
@Test
public void testname() throws Exception {
UserAction action = (UserAction) ac.getBean("userAction");
action.execute();
}
}
对于每一个userAction对象来说,只可能使用一个userService和一个userDao对象,对于这种依赖的对象有限且固定的情况,可以通过使用内部Bean,但是内部Bean相当于局部成员,别的类不能引用,也不方便维护,因此,这种方式不是很推荐。
<bean id="userAction" class="com.cityu.app.UserAction">
<property name="userService">
<bean class="com.cityu.app.UserService">
<property name="userDao">
<bean class="com.cityu.app.UserDao">bean>
property>
bean>
property>
bean>
对象创建由IOC容器通过读取XML配置文件并使用反射(可以理解为读取类名,就可以获取类的所有信息从而完成对象创建)来完成,不需要用户类去创建。
目的是为了简化书写,其实也是set方法
id="userDao" class="com.cityu.app.UserDao">
id="userService" class="com.cityu.app.UserService" p:userDao-ref="userDao">
id="userAction" class="com.cityu.app.UserAction" p:userService-ref="userService">
注意,所有的-ref后缀引用后面都需要时容器中存在的对象!
不带-ref后缀的p名称空间指定的是具体值:
id="user" class="com.cityu.pNameSpace.User" p:id="0017" p:name="BJTShang">
测试类:
public class Demo {
@Test
public void testIOC() throws Exception {
ApplicationContext ac =
new ClassPathXmlApplicationContext(
"com/cityu/pNameSpace/applicationContext.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
}
输出:User [id=17, name=BJTShang]
autowire=”byName”,创建该类对象时,如果需要使用某个对象属性,就去IOC容器中寻找,找到符合命名标准的自动注入。本质和set方法一样。
<bean id="userDao" class="com.cityu.d_assembly.UserDao">bean>
<bean id="userService" class="com.cityu.d_assembly.UserService" autowire="byName">bean>
<bean id="userAction" class="com.cityu.d_assembly.UserAction" autowire="byName">bean>
或者直接在全局配置
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName">
<bean id="userDao" class="com.cityu.d_assembly.UserDao">bean>
<bean id="userService" class="com.cityu.d_assembly.UserService">bean>
<bean id="userAction" class="com.cityu.d_assembly.UserAction">bean>
beans>
JDK1.5以后出现了注解,目的是为了简化配置。(缺点是不便于维护)spring中可以使用注解简化依赖注入的配置。
引入context名称空间;
xmlns:context="http://www.springframework.org/schema/context"
开启注解扫描;
<context:component-scan base-package="com.cityu.e_anno">context:component-scan>
使用注解;
Dao:
@Component("userDao")//将userDao对象加入IOC容器:
public class UserDao {
public void save(){
System.out.println("save into DataBase!");
}
}
Service:
@Component("userService")
public class UserService {
@Resource(name="userDao")//从IOC容器中取出名字为userDao的对象,并注入到当前属性
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void service(){
userDao.save();
}
}
Action:
@Component("userAction")
public class UserAction{
@Resource(name="userService")
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public String execute(){
userService.service();
return null;
}
}
Test客户类:
public class App {
private ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("com/cityu/e_anno/applicationContext.xml");
@Test
public void testApp() throws Exception {
UserAction action = (UserAction) ac.getBean("userAction");
action.execute();
}
}
注解可以简化,set方法也可以省略,使用的都是默认值userService,userDao,但是有局限性,因此最好指定!
@Component//类名,首字母小写的对象加入IOC容器
public class UserService {
@Resource//默认在IOC容器中根据类名注入对象,需要保证IOC容器中只有一个该类对象
private UserDao userDao;
public void service(){
userDao.save();
}
}
为了具有区分度,可以对不同功能的类使用不同的注解,加入IOC
@Repository 持久层注解Dao
@Service 业务层注解Service
@Controller 控制层注解Action