07.spring工厂,IOC,DI,Bean


在这里插入图片描述

一、Spring

官网:https://spring.io

优秀的java开源框架。

作用:项目管理。 管理组件(对象 DAO,Service,Controller)。

设计思路:践行工厂模式,打造一个工厂,通过工厂完成对项目的管理。

学习思路:分析项目中的问题,引入spring解决方案,打造更好的项目形态。

二、spring工厂搭建

1. 导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>
spring 核心jar,其中核心容器 jar:beans、context、context-support、core、expression
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EuhFr6tS-1569679132903)(mdpic/jars.jpg)]

2. 配置文件

配置文件作用:描述哪些组件需要spring生产,管理

文件位置:resources目录

文件名称:随意. 常用名 :applicationContext.xml beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 作用:声明需要spring 生产的组件 -->
    <!-- UserDAOImpl组件  id="组件标识" class="组件类型" -->
    <bean id="userDAO" class="com.zhj.dao.UserDAOImpl"></bean>
    <!-- UserServiceImpl组件 -->
    <bean id="userService" class="com.zhj.service.UserServiceImpl"></bean>
    ....
</beans>

3. 启动工厂

工厂接口:ApplicationContext

实现类:ClassPathXmlApplicationContext

// 启动工厂,注意:需要指定配置文件位置
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 从工厂中获取 标识为"userDAO"的组件
UserDAO userDAO = (UserDAO)context.getBean("userDAO")

三、Spring-Schema

Schema:配置文件的schema(规范)。
作用:统一配置方式,开发者和框架采用同一套配置语法。向框架传达开发意图。
格式:xsd文件 (spring-beans-4.1.xsd,spring-context-4.1.xsd,…)
导入方式:spring的每种schema都有namespace作为标识;

:在配置文件中追加namespace,以及和namespace配套的location即可。如下

<beans xmlns="http://www.springframework.org/schema/beans"  <!-- beans-Schema的ns -->
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   
                           <!-- beans-Schema的location -->
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
	...
</beans>

每种schema中,都在定义:允许出现哪些标签,标签的层级,标签的先后顺序,标签的属性。

每一种配置意图,都由规范中的 标签、属性来描述。

四、项目中的问题

1. 依赖关系

项目由一个个的组件组成,而组件之间都不是孤立的。会彼此依赖。

2. 强耦合

类之间的关系紧密程度,即耦合度。关系松散即弱耦合,关系密切即强耦合。

class A{
    public void eat(){...}
}
class B{
    public void fn2(Dog a){a.eat();...}
}
//如上B类中 直接关联了A类,则B类智能和A类协作,不能和其他类协作 ==> 强耦合
interface Animal{ public void eat();}
class Dog implements Animal{
    public void fn1(){...}
}
class Test{
 	public void fn2(IA a){a.fn1();...}   
}
//如上B类中只是和IA接口关联,则此时B类可以使用A类,也可以在完全不需要改动的情况下兼容所有IA的其他实现类。
//则B类 和 A类 的关系是松散的 ==> 弱耦合

关系处理不当时,可能会导致组件之间强耦合。

一旦强耦合:组件即陷入不稳健的状态,不稳健的组件 将导致整个项目的形态极差。

项目中的具体体现:Controller 依赖 Service 、Service 依赖 DAO 、…

五、解决方案

spring介入后,可以以全新的方式处理依赖关系,既保证依赖健全,又没有强耦合。

<?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.xsd">
    <!-- 作用:声明需要spring 生产的组件 -->
    <!-- UserDAOImpl组件  id="组件标识" class="组件类型" -->
    <bean id="userDAO" class="com.zhj.dao.UserDAOImpl"></bean>
    <!-- UserServiceImpl组件 -->
    <bean id="userService" class="com.zhj.service.UserServiceImpl">
        <!-- =========重点==========重点========重点========== -->
        <!-- 为userDAO属性赋值,值为id=“userDAO”的组件 -->
    	<property name="userDAO" ref="userDAO"></property>
    </bean>
</beans>
public class UserServiceImpl implements UserService{
    //private UserDAO userDAO = new UserDAOImpl(); 不再强耦合UserDAOImpl
    private UserDAO userDAO; //替换为接口
    // set/get
    public UserDAO getUserDAO() {
        return userDAO;
    }
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

思考:此时UserServiceImpl 和 UserDAOImpl的关系是否为弱耦合?是的

1. IOC(重点)

Inverse Of Controll:控制反转

反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)

解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健

2. DI

Dependency Injection:依赖注入

全新的依赖满足方式,体现在编码中就是全新的赋值方式 ==> 在工厂中为属性推送值

如:<property name="userDAO" ref="userDAO"></property>

3. IOC 和 DI

在spring中关于IOC和DI的描述是这样的: IOC(DI),即,是一码事

IOC 是思想:指导我们在满足依赖时,应该有反转的设计。

DI 是手段:实际操作时,就是在一次次的 注入

spring官方文档中,关于DI的解释:

This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern

六、DI的配置使用

1. DI的方式

  • set注入:
    • 借助set方法完成注入
  • 构造注入 (了解)
    • 借助构造方法完成注入
  • 自动注入
    • spring自动识别属性,并注入

2. set注入

<bean id="setDI" class="x.xx.XXX">
    <!-- jdk 8种基本类型+String -->
    <property name="age" value="18"></property>
    <property name="name" value="zhj"></property>
    <property name="gender" value="true"></property>
    <!-- 引用类型  -->
    <property name="userDAO" ref="ud"></property>
    <!-- List或数组 -->
    <property name="list">
        <list>
            <value>18</value>
            <ref bean="ud"/>
        </list>
    </property>
    <!-- set -->
    <property name="xxx">
        <set>
            <value>xx</value>
        </set>
    </property>
    <!-- map -->
    <property name="map">
        <map>
            <entry key="name" value="zhj"></entry>
            <entry key="userDAO" value-ref="ud"></entry>
        </map>
    </property>
    <!-- properties -->
    <property name="prop">
        <props>
            <prop key="url">jdbc:oracle:xxxx</prop>
        </props>
    </property>
</bean>
<!-- 
	注意:在注入过程中,其实就是一个复制的过程, a=b, a是组件的属性,b是如上配置的value中的值。
         则b是String,而a可能是 Integer,Boolean,Double,Date,....;即左值和右值类型不匹配。
		 spring有自动的类型转换机制,可以保证我们绝大多数的类型转换。
		 细节:“org.springframework.beans.propertyeidtors”包下定义了大量的Editor。
-->

3. 构造注入 (了解)

<bean id="consDI" class="com.test.TestConstrutorDIComponent">
	<!-- index=构造参数索引    type:构造参数类型   value=构造参数值 -->
 	<constructor-arg index="0" type="java.lang.Integer" value="18"></constructor-arg>
 	<constructor-arg index="1" type="java.lang.String" value="zhj"></constructor-arg>
 	<constructor-arg index="2" type="java.lang.Boolean" value="true"></constructor-arg>
</bean>

<bean id="consDI04" class="com.qianfeng.di.ConsComponent">
    <!-- 构造和set注入混用 -->
    <constructor-arg index="0" type="java.lang.Integer" value="4"></constructor-arg>
    <property name="name" value="yueqi"/>
    <property name="gender" value="false"/>
</bean>

4. 自动注入

不用在配置中 指定为哪个属性赋值,及赋什么值.

由spring自动根据某个 “原则” ,在工厂中查找一个bean,为属性注入属性值

<!-- 基于属性名自动注入=将和属性"同名"的bean 赋值给属性 -->
<bean id="xx" class="xxx" autowire="byName"></bean>
<!-- 基于属性类型自动注入=将和属性"同类型"的bean 赋值给属性 -->
<bean id="xx" class="xxx" autowire="bytype"></bean>

此处,掌握 byNamebyType 的概念即可。

基于类型 自动注入时,如果存在多个此类型的bean,会报错。

七、Bean细节

1. Bean创建原理

通过反射加载类对象,默认调用无参构造

<bean class="xx.xx.xxx.XXX" id="xxx">
// 反射
String classpath="com.zhj.domain.User";
Class user = Class.forName(classpath);
Constructor constructor = user.getConstructor();
User o = (User)constructor.newInstance();//调用空参构造

2. Bean创建模式

注意,这里的有无状态是指该对象的状态是否会发生改变,即他的属性是否会发生变化,如service中的各项属性都不会发生变化,所有的对dao,service,servlet的操作都没有将他们的属性进行改变,改变的只是传入的参数,但他们本身的属性并没有变化。(反例)对于User可能存在对他属性的更改

  • singleton:单例 ==> 默认(在一个范围中,只有一个对象)
    • 同一个spring工厂中,一个bean只会创建一次对象。
    • 多次getBean(),或多次注入使用的是同一个对象
    • 随工厂创建 而创建,随工厂关闭而销毁
  • prototype:多例 (原型)
    • <bean id=xxx class=xxx scope="prototype">
    • 每次getBean(),或注入都会重新创建对象
    • 不随工厂创建 而创建,不随工厂关闭而销毁
    • 被用到时才会创建对象

对象的状态:对象的成员变量值即对象的状态
无状态:不同的用户,不同的请求,对象的属性值不会发生改变
有状态:不同的用户,不同的请求,对象的属性值会发生改变

有状态对象:多例模式
无状态对象:单例模式

3. 生命周期

单例bean:构造(工厂启动)–>set–>init–>User–>destroy(工厂关闭)
多例bean:获取时才创建–>set–>init–>User–>不会随工厂关闭而销毁

八. 工厂Bean(重点)

FactoryBean:生产某一类对象

在工厂中有些bean,无法直接通过 简单的<bean></bean>生产。

比如:Connection,SqlSessionFactory

FactoryBean:spring工厂中一种特殊的bean,可以生产对象。Spring工厂中的小作坊。

Spring支持如下方式:

8.1 FactoryBean(根)

// 1.实现FactoryBean
public class MySqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>{
    public SqlSessionFactory getObject(){
        //完成SqlSessionFactory的生产,并返回生产的对象
    }
    ...
}

<!-- 通过该Id获取bean时,返回的不是工厂bean本身的对象,而是其生产的对象 -->
<bean id="sqlSessionFactory" class="com.zhj.factory.MySqlSessionFactoroyBean"></bean>

8.2 静态工厂方法

// 2.静态工厂方法
public class MyFactoryBean {
    public static User createUser(){
        return new User();
    }
}

<bean id="user" factory-method="createUser" class="com.zhj.factory.MyFactoryBean" scope="xx"></bean>

8.3 工厂方法

// 3.工厂方法
public class MyFactoryBean {
    public User createUser(){
        return new User();
    }
}

<bean id="userFactory" class="com.zhj.factory.bean.MyFactoryBean"></bean>
<bean id="user" factory-bean="userFactory" factory-method="createUser" scope="xx"></bean>

定义了如上任何一种后,测试:

//获取bean,此时获取的并不是FactoryBean,而是其生产的对象。
SqlSessionFactory sqlSessionFactory = (sqlSessionFactory)context.getBean("sqlSessionFactory");
//获取bean,此时获取的并不是FactoryBean,而是其生产的对象。
User user = (User)context.getBean("user");

九、工厂模式

工厂模式是编程中经常用到的一种模式。

它的主要优点有:
1> 可以使代码结构清晰,有效地封装变化。在编程中,产品类的实例化有时候是比较复杂和多变的,通过工厂模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产品。对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。
2> 降低耦合度。产品类的实例化通常来说是很复杂的,它需要依赖很多的类,而这些类对于调用者来说根本无需知道,如果使用了工厂方法,我们需要做的仅仅是实例化好产品类,然后交给调用者使用。对调用者来说,产品所依赖的类都是透明的。

//简单工厂模式:
class SimpleFactory{
    public Computer createOperate(string sig){
        Computer com = null;
        switch (sig){
            case "1":
                {
                    com = new Lenovo();
                    break;
                }
            case "2":
                {
                    com = new Asus();
                    break;
                }
        }
        return com;
    }
}
// 如上方法如果是静态的,则为静态工厂模式
//上述简单工厂,存在问题,即如果有新的生产需要时,需要改动工厂类,则不符合设计模式中的开闭原则。改进如下:

//工厂方法模式:
// 1.定义接口
interface Computer{}
class ASUS implements Computer{}
class Lenovo implements Computer{}
// 2.定义工厂接口
interface ComputerFactory{Computer createComputer();}
// 3.如此在有新的生产需要时,每个工厂类依然稳定,只要添加新的工厂类即可
LenovoFactory implements ComputerFactory{
    public Computer createComputer(){
        return new Lenovo();
    }
}

AsusFactory implements ComputerFactory{
    public Computer createComputer(){
        return new Asus();
    }
}

//抽象工厂模式:
interface  水果{}
interface  蔬菜{}
class 本地水果 implements 水果{}
class 外地水果 implements 水果{}
class 本地蔬菜 implements 蔬菜{}
class 外地蔬菜 implements 蔬菜{}

interface factory{
    //相对于工厂方法模式,抽象工厂模式有多个抽象产品,也就有多个抽象生产方法
    //抽象工厂更像是工厂,而抽象方法更像是一个生产线
    水果  createFruit();
    蔬菜  createVeg();
}
//如下一样,当有新的蔬菜和水果需要生产时,现有的类不用改动,新加工厂类即可
class 本地Factory implements Factory{
    public 水果 createFruit(String name){
        new 本地水果(name);
    }
    public 蔬菜 createFruit(String name){
        new 本地蔬菜(name);
    }
}
class 外地Factory implements Factory{
    public 水果 createFruit(String name){
        new 外地水果(name);
    }
    public 蔬菜 createFruit(String name){
        new 外地蔬菜(name);
    }
}

十、 运行原理

Bean创建过程:运行过程,对应核心API,直到反射层面
new ClassPathXmlApplicationContext(String[] configLocations,boolean refresh,ApplicationContext   parent){
    //...;
    refresh();//创建主流程,Spring创建Bean的入口
    //...;
}

AbstractApplicationContext#refresh(){
    //...;
    // 创建BeanFactory,(DefaultListableBeanFactory)
    // 并解析spring配置文件,进而加载BeanDefinitions
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    //...;
    finishBeanFactoryInitialization(beanFactory);//创建所有单例的bean
    //...;
}

AbstractApplicationContext#finishBeanFactoryInitialization(){
	//...;
	beanFactory.preInstantiateSingletons();//创建所有单例的bean
	//...;
}

DefaultListableBeanFactory#preInstantiateSingletons(){
    for (String beanName : beanNames) {//遍历所有的beanName,挨个创建对象
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {//非抽象+单例+非延迟,则创建
            if (isFactoryBean(beanName)) {
                FactoryBean  fb = getBean("&"+beanName);//获得工厂Bean对象本身								
                //....
                getBean(beanName);//创建实际对象,此中会通过工厂Bean获得
            }else{
                getBean(beanName);//普通bean,直接创建对象
            }
        }
    }
}

AbstractBeanFactory#getBean(){
	return doGetBean(...);
}

AbstractBeanFactory#doGetBean(){
    //....
    if (mbd.isSingleton()) {
        //注意:此处有回调 ObjectFactory#getObject()
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {//获得或创建单例的bean
            @Override
            public Object getObject() throws BeansException {
                try {
                    return createBean(beanName, mbd, args);//回调方法,创建bean
                }
                //...
            }
        }
    }

DefaultSingletonBeanRegistry#getSingleton(beanName,singletonFactory){
    synchronized (this.singletonObjects) {//枷锁,防止重复创建
        Object singletonObject = this.singletonObjects.get(beanName);//尝试获取,如果有则不再创建
        //....
        singletonObject = singletonFactory.getObject();//回调doGetBean中内部类的方法,创建单例bean
        //....
        addSingleton(beanName, singletonObject);//记录已创建的单例bean,下次不再创建
    }
}

//接:回调doGetBean中内部类“ObjectFactory”的getObject方法,创建单例bean
AbstractAutowireCapableBeanFactory#createBean(){
    //.....
    doCreateBean(beanName, mbdToUse, args);//创建单例bean
}

AbstractAutowireCapableBeanFactory#doCreateBean(){
    //.....
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
}

AbstractAutowireCapableBeanFactory#createBeanInstance(){
    //.....
    return instantiateBean(beanName, mbd);//使用无参构造创建bean
}
AbstractAutowireCapableBeanFactory#instantiateBean(){
    //.....
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}

SimpleInstantiationStrategy#instantiate(){
    //....
    constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);//获得构造方法对象
    //....
    return BeanUtils.instantiateClass(constructorToUse);//反射构建bean
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值