Spring 学习(二)-IOC容器的依赖注入DI

Spring IOC容器的依赖注入

依赖注入其实是spring的IOC容器控制用户类中对象的创建过程,首先需要知道如何在xml文件中配置对象创建

一、 创建对象的四种方法

1. 无参数构造器

<bean id="springUser" class="com.cityu.b_createObj.User">bean>

2. 带参数构造器

<bean id="user2" class="com.cityu.b_createObj.User">
        <constructor-arg value="17">constructor-arg>
        <constructor-arg value="BJT">constructor-arg>
bean>

3. 工厂创建(分为实例方法和静态方法实现)

//实例方法创建
    <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");
        }
    }

二、 理解DOI和DI:

对于初学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中去的,依赖注入的名字就这么来的。

下面可以通过设置依赖对象来进一步体会

三、 依赖注入实现方式

如何给对象属性赋值?

  • 构造函数
  • 通过set方法给属性注入
  • p名称空间
  • 自动装配
  • 注解

1. 通过构造函数以及set方法注入依赖对象


    <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配置文件并使用反射(可以理解为读取类名,就可以获取类的所有信息从而完成对象创建)来完成,不需要用户类去创建。

2. p名称空间(spring2.0以上)

目的是为了简化书写,其实也是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]

3. 自动装配

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>
  • autowire=”byName”:根据引用对象的id值;
  • autowire=”byType”:根据引用对象的class值,需要确保容器中只有一个这种类型的引用对象!不推荐。

4. 注解

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

你可能感兴趣的:(Spring)