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)首先我们先了解标签的属性:

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

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

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

(2)标签:属性注入

    name属性:属性名称

    value:注入的普通属性值

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

(3)标签:有参构造注入


  2.1.1构造方法(不推荐)

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

(1)构造方法下标赋值


    

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


    

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




    

 2.1.2  set方法(推荐)

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

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




    

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

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

其次,需要修改注入方式


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 普通数据类型



    


     


3.2 数组类型


    
        
            水浒传
            红楼梦
        
    

3.3 集合数据类型

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

(1)list

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

public void setStrList(List strList) {
    this.strList = strList;
}

   
       
           aaa
           bbb
           ccc
       
   

(2)map

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

public void setUsermap(Map usermap) {
    this.usermap = usermap;
}

    
        
            
            
            
        
        



    
    


    
    

(3)properties

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

public void setProperties(Properties properties) {
    this.properties = properties;
}

    
        
            ppp1
            ppp2
            ppp3
        
    

3.4 Bean的生命周期

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

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

 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等于其他配置文件的名称)


.

你可能感兴趣的:(spring,spring)