Spring框架之IOC

什么是Spring框架?

    Spring框架是一种开发框架,它的使用有助于提升开发人员的开发效率以及系统的可维护性。它由很多模块组成,框架示意图如下:

Spring AOP:提供面向切面的编程实现。

Spring ORM:用于支持Hibernate等ORM(关系规则映射)工具。

Spring DAO:提供事务支持,JDBC支持等与数据库相关。

Spring Web:提供面向切面的编程

Spring Context与Spring Web MVC:为Web应用提供支持

Spring Core:基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。

Spring的核心功能

   Spring的核心功能主要是IOC(Inverse of Control 控制反转)与AOP(Aspect-Oriented Programming 面向切面编程),本篇博文也是主要介绍这两个功能。

IOC介绍

      IOC是一种设计思想,就是将对象的创建与管理交给Spring框架完成。IOC在其他语言中也存在,并非Spring特有的。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。

      提出IOC的目的:如同手表中的齿轮,当其中一个齿轮出现问题就会影响其他齿轮的工作,进而影响整个手表的工作,对象与对象之间也存在这种关系,称为耦合关系,这是无法避免的,也是必需的,开发者的目标就是尽量减少对象之间的耦合。基于此,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”。

     现在,我们来看看IOC提出的过程,举例进行说明:需求:User类中有add方法,现要在其他类中调用其add方法,则有:

//User类
public class User {
    public void add() {
        System.out.println("User的add方法");
    }

//在Test类中调用User的add方法
public class Test {
    public void test() {
        User user = new User();
        user.add();
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.test();
    }
}

         我们会发现,这种方法虽然可以实现我们的需求,但对象之间耦合太高,一旦User类中出现问题,那么Test类中相关的功能也会受到限制,如何解决呢,我们又提出了利用工厂类实例化对象来实现。

//User类
public class User {
    public void add() {
        System.out.println("User的add方法");
    }
//利用工厂类实例化对象
public class Factory {
    public static User getBean() {
        return new User();
    }
}
//在Test类中调用User的add方法
public class Test {
    public void test() {
        User user = Factory.getBean();
        user.add();
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.test();
    }
}

   如此一来,解决了User类的对象与Test对象间的耦合关系,但是,工厂类对象与User类对象的耦合性又太高了。此时,我们为了解耦,利用IOC来进行,后面我们会介绍Spring IOC的两种方式(XML方式和注解方式)。

什么是IOC?

    IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:

      IOC容器就是“第三方”,它的引进使对象A,B,C,D之间没有了耦合,将控制权交给了第三方(IOC容器),所以IOC容器成为了整个系统的关键核心,那么为什么称这种过程为控制反转呢?

      软件系统在没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
      软件系统在引入IOC容器之后,这种情形就完全改变了,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
     通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

IOC的实现

    Spring IOC主要有两种实现方式:xml配置方式和注解方式。

1、使用XML配置方式实现IOC:

实现步骤:

1、通过maven管理依赖核心jar包

 <!--Spring 基本核心jar包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>
    <!--log4j日志-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

2、创建类、在类中创建方法

public class User {
     public void add(){
         System.out.println("User.add...");
     }
}

3、创建springXML文件来管理user,使用的约束spring-beans-3.0.xsd

<?xml version="1.0" encoding="UTF-8"?>
<!--根标签-->
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
     将User对象的实例化交给Spring管理
     <bean id="user" class="com.tulun.bean.User"/>
</beans>

 4、测试代码创建对象

public static void main(String[] args) {
         //加载Spring的配置文件,创建对象
         ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
         //得到实例对象user
         User user = (User) context.getBean("user");
         System.out.println(user);
         user.add();
     }

      可以看到,Spring IOC的上述实现过程中创建对象是通过XML配置文件进行的,通过XML配置创建对象也分为三种方法(通过无参构造,通过静态工厂,通过一般工厂),上述过程就是通过无参构造创建对象的,下面我们介绍另外两种方法。

使用静态工厂创建对象(在配置文件中添加)

<!--通过静态工厂创建对象-->
     <bean id="user1" class="com.tulun.foctory.StaticFactory" factory-method="getBean"/>

    class属性值为工厂类的全路径名

    factory-method属性值对应的是工厂类提供的实例化对象方法

工厂类:

public class StaticFactory {
        //通过工厂方法创建对象
        public static User getBean() {
            return new User();
        }
    }

使用一般工厂创建对象

<!--通过普通实例工厂创建对象-->
      <!--实例化普通工厂-->
      <bean id="factory" class="com.tulun.foctory.Factory2"/>
      <bean id="user2" factory-bean="factory" factory-method="getBean"/>

    工厂类:

public class Factory2 {
       public User getBean() {
           return new User();
       }
   }

bean标签常用属性

  • id属性  名称,不能包含特殊字符,根据id属性来获取对象
  • name属性:功能和id相同,但是可以包含特殊字符
  • class属性:创建对象所在类的全路径
  • scope属性:
  1. -singleton:默认值,单例

  2.    -prototype:多例

  3.    -request:创建对象将对象放到request域里面

  4.    -session:创建的对象放到session域里面

  5.    -globalSession:创建的对象放到globalSession域里面

IOC的别名:依赖注入(DI)

      2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

      依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦

依赖注入方式 

    依赖注入的方式有三种:set方法  有参构造  接口注入,下面将进行一一介绍:首先创建接口UserDao以及对象的实现类UserDaoImpl:

public interface userDao{
   public void add(String name)
}

public class UserDaoImpl implements userDao {
   private String name;
   public void add(String name){
      this.name = name;
   }
}

Spring提供属性注入方式

(1)通过set方法注入属性

<!--通过set方法注入对象属性-->
    <bean id="user3" class="com.tulun.bean.User">
        <property name="name" value="tulun"/>
        <property name="sex" value="nan"/>
    </bean>

(2)有参构造

<!--通过有参构造函数注入对象属性-->
   <bean id="user4" class="com.tulun.bean.User">
       <constructor-arg name="name" value="lisi"/>
       <constructor-arg name="sex" value="niu"/>
   </bean>

2、通过注解方式实现Spring IOC

创建对象(注解形式)注解的写法: @注解名称(属性名称=属性值),注解可以使用在类、方法、属性上面

1、引入依赖jar(核心的四个jar包外,还要包含以下spring-aop的注解jar包)

 <!--注解相关jar包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.1.7.RELEASE</version>
    </dependency>

2、创建bean类,添加注解Component

@Component(value = "student")
public class Student1 {
    public void show() {
        System.out.println("Student1.show...");
    }
}

3、xml配置信息:配置包的扫描信息

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!--<bean id="" class=" "/>-->
    <!--开启注解扫描
    (1)到包里面扫描类方法、属性上是否有注解
    -->
    <context:component-scan base-package="com.tulun"></context:component-scan>
    <!--
    扫描属性上面的注解(不建议使用)
    -->
    <!--<context:annotation-config></context:annotation-config>-->
</beans>

4、使用student1对象    

 ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
 Student1  student = (Student1)context.getBean("student");
 student.show();

注:
 Spring创建对象的注解:4个注解
 1、@Component 
 2、@Controller (web层)
 3、@Service (业务层)
 4、Register (持久层)
 4个注解的功能是一样的,都是来创建对象

 
 属性注入(注解方式)

 @Autowired
  //通过注解的形式注入属性,set方法不用在写了
  private Student1 student;
  
   @Resource(name = "student")(使用比较多)
   name属性值写的是通过注解创建的dao对象的value值

3、XML与注解配合使用(xml创建对象,注解属性注入)

首先:创建StudentDao\StudentService\Student,提供show方法

1、创建三个对象StudentDao\StudentService\Student对象

2、在controller层使用service层,即在controller层添加属性studentService

3、在StudentService层使用dao层,即在StudentService层添加属性student

public class Student {
    public void show() {
        System.out.println("student.show");
    }
}



public class StudentService {
//    //通过配置文件的set方法注入属性
    @Autowired
    public Student student;
    public void show() {
        student.show();
    }
}



public class StudentDao {
    @Autowired
    private StudentService studentService;
    public void show() {
        studentService.show();
    }
}

在注解的xml文件中创建三个对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <!--开启注解扫描
    (1)到包里面扫描类方法、属性上是否有注解
    -->
    <context:component-scan base-package="MavenLG"></context:component-scan>
    <!--
    扫描属性上面的注解(不建议使用)
    -->
    <!--<context:annotation-config></context:annotation-config>-->
    <!--通过无参构造创建对象-->
    <bean id="studentService" class="MavenLG.service.StudentService"/>
    <bean id="studentDao" class="MavenLG.dao.StudentDao"/>
    <bean id="student" class="MavenLG.bean.Student"/>
</beans>

测试类进行测试:

public class TestStudent {
    @Test
    public void testAdd(){
        //利用xml创建对象,注解进行属性注入
        //读取配置文件(加载Spring上下文   )
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-zhujie.xml");
        //获取 bean
        StudentDao studentDao = (StudentDao) context.getBean("studentDao");
        System.out.println(studentDao);
        studentDao.show();//使用
    }
}

总结

 IOC容器的技术剖析
IOC中最基本的技术就是“反射(Reflection)”编程,有关反射的概念和用法,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

使用IOC框架应该注意什么
使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点:
第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。
第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。
第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。
参考自:

https://www.cnblogs.com/wang-meng/p/5597490.html

https://www.zhihu.com/question/23277575/answer/169698662

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值