Spring IOC思想和spring配置文件 bean的依赖注入

1、IOC控制反转

控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现Ioc 的一种方法。没有Ioc的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制。控制反转后将对象的创建转移给第三方(交给Spring容器来创建)!!灵活性更高。

也可以这么理解,早期用户调用的功能是程序员指定的,但是用了控制反转去实现之后,控制权交给用户后,程序员写好功能,用户想调用哪个功能就调用哪个!

作用:降低耦合度(也就是降低模块之间的依赖)

下面会通过举例子说明控制反转带来的好处。

1.1 最原始的业务层调用dao层方法

(1)创建UserDao接口

public interface UserDao {
    void save();
}

(2)创建UserDaoImpl实现UserDao

public class userDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存用户数据");
    }
}

(3)创建业务层UserService接口

public interface UserService {
    void save();
}

 (4)创建业务层UserServiceImpl实现UserService(由程序来控制创建对象)

public class UserServiceImpl implements UserService {

    //最原始的方法,由程序来控制创建对象
    private UserDao userDao=new userDaoImpl();

    @Override
    public void save() {
        userDao.save();
    }
}

(5)测试

@Test
public void test11(){
    UserService userService=new UserServiceImpl();//用户实际调用的是业务层
    userService.save();
}

 缺点 :

     当用户需求改变,要保存mysql 的数据,那么就需要改变业务方法,这里改变daoImpl的内容即可

public class userDaoMysqlImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存Mysql");
    }
}

    那么这时候我们就需要去ServiceImpl层里面修改private UserDao userDao=new userDaoImpl();为下面代码,也就是程序员需要去改变源代码,那么当用户的需求不断改变时,也意味着源码需要不断的改变,所以这就是耦合度高的缺点!!

//private UserDao userDao=new userDaoImpl();
private UserDao userDao=new userDaoMysqlImpl();

1.2 解决上面耦合度高缺点的常用方法

1.2.1 通过工厂模式

(1)新建静态工厂负责创建UserDao

public class StaticFactory {
    public static UserDao getUserDao(){
        return new userDaoImpl();
    }
}

(2)ServiceImpl改变成:

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    @Override
    public void save() {
        userDao = StaticFactory.getUserDao();//工厂模式创建对象
        userDao.save();
    }
}

(3)其他的dao和service和上面一样,那么如果用户需求像上面一样改变,那么源代码就不需要变动了,但是!!需要改变工厂类的方法。比如用户需求新增保存mysql数据,那么工厂类应该变为:

public class StaticFactory {
    public static UserDao getUserDao(){
        return new userDaoMysqlImpl();
    }
}

(4)测试 

@Test
public void test11(){
    UserService userService=new UserServiceImpl();//用户实际调用的是业务层
    userService.save();
}

通过工厂模式解耦,实则是把Service层和dao层的耦合变成Service-工厂-dao层的耦合,当需求改变,源代码不变,但是工厂类需要变,所以依然还是存在着比较高的耦合度

1.2.2 通过set方法(这种思想就是比较接近IOC思想的解决方法了) 

思想:利用set方法实现动态值的注入

 (1)UserDaoSeriviceImpl改为:

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    //set方法实现动态注入对象
    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
    }
    @Override
    public void save() {
        userDao.save();
    }
}

(2)测试(只需要用户动态传入对象即可,源代码不需要改变)

下面的代码之所以需要强转类型是因为UserService没有setUserDao这个方法,所以需要强转为实现类调用。而用接口变量指向实现类对象是符合多态思想的。

@Test
public void test11(){
    UserService userService=new UserServiceImpl();//用户实际调用的是业务层
    ((UserServiceImpl) userService).setUserDao(new userDaoImpl());
    userService.save();
}

优点:之前我们都是程序主动创建对象(private UserDao userDao=new userDaoImpl())!控制权在程序员手上。但是现在使用set方法,程序不再具有主动性,而是被动的接受对象!!这也就是控制反转(控制权的反转)。这也是IOC的原型。 

1.3 Spring解决的思路

         Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象。控制反转是一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,Dl)。

     控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。

      反转:程序本身不创建对象,而变成被动的接收对象。

2. Spring的依赖注入

Spring的依赖注入概念

  • 依赖注入(Dependency Injection):它是Spring框架接心IOC的具体实现。组件之间依赖关系由容器在运行期决定,形象的说,既由容器动态的(编译前不会建立,运行时建立)将某个依赖关系注入到组件(在Spring中称为Bean)之中。
    • 依赖:bean对象的创建依赖于容器!
    • 注入:bean对象中的所有属性,由容器注入
  • 作用:提升组件重用的频率,为系统搭建一个灵活、可扩展的平台。
  • 在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法,那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

怎么将UserDao怎样注入到UserService内部呢?

2.1 XML配置方法实现

XML的重点配置

(1)首先我们先了解<bean>标签的属性:

    id属性:在容器中Bean实例的唯一标识,不允许重复

    class属性:要实例化的Bean的全限定名

    scope属性:Bean 的作用范围,常用是Singleton(默认)和prototype

(2)<property>标签:属性注入

    name属性:属性名称

    value:注入的普通属性值

    ref属性:注入的对象引用值

(3)<constructor-arg>标签:有参构造注入


  2.1.1构造方法(不推荐)

Beans类必须提供有参构造方法

(1)构造方法下标赋值

<bean id="dog" class="domain.Dog">
    <constructor-arg index="0" value="zhangSan"/>
</bean>

(2)构造方法参数类型赋值(不建议使用)

<bean id="dog" class="domain.Dog">
    <constructor-arg type="java.lang.String" value="zhangSan"/>
</bean>

(3)直接通过构造方法参数名设置(推荐) 

<bean id="userDao1" class="dao.impl.userDaoImpl" ></bean>
<!--1、构造方法的参数名 2、bean ID-->
<bean id="userService" class="service.impl.UserServiceImpl" >
    <constructor-arg name="userDao" ref="userDao1"></constructor-arg>
</bean>

 2.1.2  set方法(推荐)

Bean类必须有一个无参构造方法,Beans类必须为属性提供setter方法

(1)普通版本,举个栗子:为service注入dao

<bean id="userDao" class="dao.impl.userDaoImpl" ></bean>
<bean id="userService" class="service.impl.UserServiceImpl">
<!--告诉spring依赖注入(name指的是set方法去掉set之后把第一个字母小写.对象的引用用ref是bean id)-->
    <property name="userDao" ref="userDao" />
</bean>

(2)P命名空间注入,本质也是set方法注入,但比起上述的se方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入P命名空间:

xmlna:p="http://www.apxingframework.org/achema/p"

其次,需要修改注入方式

<bean id="userDao1" class="dao.impl.userDaoImpl" ></bean>
<bean id="userService" class="service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

OK,使用xml配置法实现Bean的依赖注入之后,一旦有需求变化,我们彻底不需要去程序中更改代码,只需要在xml配置文件中进行修改。 

2.1.3 测试

@Test
public void test11(){
    ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");//只要一读完配置文件马上就创建对象
    Dog dog = (Dog) app.getBean("dog");
    Dog dog1 = (Dog) app.getBean("dog");
    System.out.println(dog==dog1);//运行结果是true,也就是说Spring容器同一个对象只存放一个实例
}

这里的ApplicationContext是Spring的一个容器,这里顺带讲一下Spring容器的使用

2.2 Spring容器

  • Spring容器是Spring的核心,它让JavaEE应用的各种组件不需要以硬编码方式耦合在一起,甚至无须使用工厂模式,因为Spring核心容器就是一个超级大工厂,所有的组件(包括数据源)都被当成Spring核心容器的管理对象,这些组件在Spring中被称为bean。一切bean的实例化,获取,销毁以及bean之间相互关系的处理等都是由Spring容器管理的。Spring容器通过配置文件很好地将bean组织在一起,而不再是由程序代码直接控制,实现了非常优秀的解耦。
  • Spring为我们提供了两种核心容器,分别为BeanFactory和ApplicationContext。 
  • 除非一个内存非常关键的应用,对于大部分JavaEE应用而言,使用ApplicationContext作为Spring容器更为方便。因为ApplicationContext除了提供BeanFactory全部功能外,还提供了以下功能
    • 默认预初始化所有singleton Bean
      • ApplicationContext适用于单例对象内存中只有一份实例!!),它在构造核心容器时,创建对象采取的策略是立即加载方式,也就是说,只要一读完配置文件马上就创建对象;只要容器在对象就一直存活着,容器销毁时对象消亡。
      • BeanFactory适用于多例对象,它在构造核心容器时,创建对象采取的策略是延迟加载方式,也就是说,什么时候根据id获取对象了,什么时候才真正创建对象。当对象长期不使用并且也没有其他对象引用时,将由java垃圾回收器回收。
    • 继承MessageSource接口,因此提供国际化支持
    • 资源访问,如URL和文件
    • 事件机制
    • 可同时加载多个配置文件
    • 以声明方式启动并创建Spring容器

3. Bean的依赖注入的数据类型


上面的操作,都是注入的引用Bean,处了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。
注入数据的三种数据类型


 3.1 普通数据类型

<!--普通属性值用value-->
<bean id="userDao1" class="dao.impl.userDaoImpl">
    <property name="username" value="zyk" />
</bean>
<bean id="userService" class="service.impl.UserServiceImpl" >
     <constructor-arg name="userDao" ref="userDao1"></constructor-arg>
</bean>

3.2 数组类型

<bean id="userDao1" class="dao.impl.userDaoImpl">
    <property name="books">
        <array>
            <value>水浒传</value>
            <value>红楼梦</value>
        </array>
    </property>
</bean>

3.3 集合数据类型

<property name=""> 这里的name指的是set方法去掉set之后,第一个字母小写的属性

(1)list

这里的list在userdao里面是这样定义的:

public void setStrList(List<String> strList) {
    this.strList = strList;
}
<bean id="userDao1" class="dao.impl.userDaoImpl">
   <property name="strList">
       <list>
           <value>aaa</value>
           <value>bbb</value>
           <value>ccc</value>
       </list>
   </property>
</bean>

(2)map

这里的map在userdao里面是这样定义的:

public void setUsermap(Map<String, User> usermap) {
    this.usermap = usermap;
}
<bean id="userDao1" class="dao.impl.userDaoImpl">
    <property name="usermap">
        <map>
            <!--1、key取什么都行2、被引用对象必须存在容器当中,所以下面bean id=“user”-->
            <entry key="user1" value-ref="user"></entry>
            <entry key="user2" value-ref="user1"></entry>
        </map>
    </property>    
</bean>

<bean id="user" class="domain.User">
    <property name="name" value="zyk" />
    <property name="address" value="22"/>
</bean>
<bean id="user1" class="domain.User">
    <property name="name" value="yk" />
    <property name="address" value="66"/>
</bean>

(3)properties

这里的properties在userdao里面是这样定义的:

public void setProperties(Properties properties) {
    this.properties = properties;
}
<bean id="userDao1" class="dao.impl.userDaoImpl">
    <property name="properties">
        <props>
            <prop key="p1">ppp1</prop>
            <prop key="p2">ppp2</prop>
            <prop key="p3">ppp3</prop>
        </props>
    </property>
</bean>

3.4 Bean的生命周期

1、单例模式(Spring默认机制),内存中只有一份实例!!

<bean id="userDao1" class="dao.impl.userDaoImpl" scope="singleton"/>

2、原型模式:每次从容器中get的时候,都会产生一个新对象!!

<bean id="userDao1" class="dao.impl.userDaoImpl" scope="prototype"/>

 3、默认为单例模式的时候测试

@Test
public void test11(){
    ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");//只要一读完配置文件马上就创建对象
    Dog dog = (Dog) app.getBean("dog");
    Dog dog1 = (Dog) app.getBean("dog");
    System.out.println(dog==dog1);//运行结果是true,也就是说Spring容器同一个对象只存放一个实例
}

4、引入其他配置文件(分模块开发)


实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。(resource等于其他配置文件的名称)

<import resource="applicationContext-xxx.xml"/>


.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值