J2EE学习笔记(六)之Spring原理

Spring框架在项目中可以说是非常常见了,一直以来都只停留在会用的程度上,对原理、概念都了解的很模糊,因此很想找个机会好好梳理一下。

在Spring中有几个比较常见的概念,先理清楚,将会对我们理解框架有非常大的帮助。

一、依赖注入和控制反转


如果稍微有接触过spring,应该知道依赖注入(Inversion of Control,IOC)是Spring的核心机制,控制反转(Dependency Injection)往往会被同时提及,这两者是什么含义,有什么区别呢?
首先,理解一下什么是依赖
我们知道Java是一门面向对象的语言,在那些年我们又枯燥又彷徨、有时却又有点小骄傲的编程过程中,应该会经常遇到A对象中调用B对象的情形,这种情形就被Spring称之为依赖关系,即A对象依赖B对象。这种互相调用的关系,就是Spring中所说的依赖关系。
这种时候本不必举个栗子,但是还是……举一个吧。
现在有一个Food类

package com.yolanda.fun.entity;

public class Food {

    public String print() {
        return "It is delicious.";
    }

}

有一个Person类,调用了Food类

package com.yolanda.fun.entity;

public class Person {

    private Food food;

    public Food getFood() {
        System.out.println("I want to eat something.");
        System.out.println(food.print());
        return food;
    }

    public void setFood(Food food) {
        this.food = food;
    }

}

好,这两段代码中并没有创建一个实例。如果没有用Spring框架,可能会怎样做呢?
如果答案是new一个的话,下面将引用书中的一段原话
“如果读者发现自己还在经常采用这种方式创建Java对象,那表明你对Java掌握的相当不够”。
嘿嘿(邪魅一笑)
这里写图片描述
这样调用者直接使用new关键字创建被调用对象(被依赖对象)的Java实例,程序会高度耦合。
那如何才能解除这种大量存在又无法避免的高度耦合现象呢?噔噔噔噔~~~这就是Spring的价值所在啦。
上述的这种原始的方式,是调用者主动获取被依赖的对象,而使用Spring框架,调用者只要被动的接受Spring容器为调用者的成员变量赋值。调用者获取被依赖对象的方式从原来的主动获取,变成了被动接受,这个概念就称为控制反转
用一种更容易理解的说法就是,主动创建实例的原始方式,就好像我们人类饿了就去寻找食物,打猎呀、摘果子呀、点外卖呀,用Spring框架就是一种理想的共产主义,饿了,Spring容器就把食物发给你。
而从Spring容器的角度来看,Spring容器把被依赖对象赋值给调用者的成员变量,简单来说,就是为调用者注入它依赖的对象的实例,这个概念就称为依赖注入
说到这里就豁然开朗了,有木有!有木有!其实依赖注入和控制反转的含义是完全相同的,只是两个人取了不一样的名字而已。
(还有一种工厂模式比直接new的方式好点,我先懒得写,后面补,明天就补)

二、Bean


Bean就是Spring中的一个基本单元了,在Spring当中,所有的Java对象都是Bean,注意不是Java Bean。
Spring容器就是管理这些Bean之间的关系。

三、注入


前面说了那么多概念,Spring容器是怎么来管理Bean的呢?
自问自答~~~
Spring用XML配置文件来管理Bean.

下面介绍一种一秒懒人快速创建Spring项目的方法,连需要什么jar包都不用管
方法就是!
——直接在eclipse里新建一个Spring Project.哈哈哈,让我叉会腰先~~~
好,继续~
项目里自动生成了一个spring配置文件,application-config.xml,那我们来注入这两个bean。

<?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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="person" class="com.yolanda.fun.entity.Person">
        <property name="food" ref="food"></property>
    </bean>
    <bean id="food" class="com.yolanda.fun.entity.Food"></bean>        

</beans>

下面就来解释一下,上面的配置文件做了些啥~
在配置文件中,Spring配置Bean实例通常会指定两个属性
一个是id,id是Bean的唯一标识,Spring根据此id值来管理Bean,程序通过id值来访问Bean实例。
另一个就是class,class指定了Bean的实现类,而且这个实现类不能是接口,必须到接口的实现类,Spring会使用XML解析器读取这个值,然后通过反射来创建该实现类的实例。

 <bean id="person"
 class="com.yolanda.fun.entity.Person">

这一句代码,声明了一个名为person的Bean,它的实现类是

com.yolanda.fun.entity.Person

这个class属性一定要带上完整的包名。

Spring的底层解析代码

String idStr = "Person";
String classStr = "com.yolanda.fun.entity.Person";
Class clazz = Class.forName(classStr);
Object obj = clazz.newInstance();
// Spring容器container
container.put(idStr, obj);

就是说Spring通过反射根据class指定的类名创建了一个Java对象,以id值作为key,放到了容器里。这个Java对象就是Spring容器里的一个Bean。

每个<Bean.../>元素默认驱动Spring容器调用这个类的无参数构造器来创建实例,并将该实例作为Spring容器中的Bean.

同理下面这句话就是把名为food的Java对象放在容器里啦。

 <bean id="food" class="com.yolanda.fun.entity.Food"></bean>     

那再来解释这个啦,

<bean id="person" class="com.yolanda.fun.entity.Person">
        <property name="food" ref="food"></property>
    </bean>

这个的Spring底层就是酱紫滴~

String nameStr = "food";
String refStr = "food";
String setterName = "set" + nameStr.substring(0,1).toUpperCase() + name.substring(1);// setFood
// 这一步是把刚才放进去的Bean取出来
Object paramBean = container.get(refStr);
// 这个clazz上一段解释过啦
Method setter = clazz.getMethod(setterName, paramBean.getClass());
// 这个obj就是前面那段的obj
setter.invoke(obj, paramBean);

每个<property.../>元素默认驱动Spring调用一次setter方法。

四、测试


最后再写个test类测试一下

public class TestSpring {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/spring/application-config.xml");
        Person person = ctx.getBean("person", Person.class);
        person.getFood();
        }
    }

输出:
I want to eat something.
It is delicious.

这里写图片描述

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值