IOC相关内容——Bean、单例和原型、注解

1.Bean的配置

String 采用了XML配置文件来指定实例之间的依赖关系,从Spring2.0开始,spring推荐采用了XML Schema 来定义配置文件的语义约束。

在idea中创建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">
    
</beans>

在上述代码中引入了一个beans的定义,它是一个根元素,而XSD文件也被引入了,这样它所定义的元素将可以对应的Sping Bean

2.Bean的标识

在上一个标题中我们创建了完整的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的IOC容器中,并起上名字
    id:表示给对象起名字
    class:类的全类名
    -->
    <bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>

在配置文件中,Spring配置Bean实例通过ID作为其唯一标识,程序通过ID属性值访问该Bean实例。

当然也可以使用name属性来指定Bean的ID,代码如下;

<?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的IOC容器中,并起上名字
    id:表示给对象起名字
    class:类的全类名
    -->
    <bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>

    <bean id="bookService" class="com.zhao.service.impl.BookServicelmpl">
        <property name="name" value="张三" />
        <property name="bookDao" ref="bookDao" />
    </bean>
</beans>

3.BeanFactory的作用?

BeanFactory是Spring中核心的顶层接口,是Bean的工厂,主要职责就是生产Bean,实现了简单工厂的设计模式,根据传入一个唯一的标识来获得Bean对象,它有非常多的实现类。
BeanFactory也是容器,因为它管理Bean的生命周期。

 BeanDefinition的作用:

主要是用来存储Bean的定义信息,决定Bean的生产方式。
比如类名,对象名,作用域,懒加载。
这里是利用了反射的机制,是拿到了类名之后用class.forname拿到类,然后通过newinstance来创建对象。

在这里插入图片描述

 ApplicationContext和BeanFactory的关系?

容器和工厂的关系类似于4S店和汽车店的关系。
Application不生产bean,只是通知BeanFactory来生产bean。ApplicationConext实现了BeanFactory。
但是ApplicationCOntext可以自动地把bean注册进来,加载环境变量,实现事件监听,支持多语言。

SpringIOC容器的加载过程

从new ApplicationContext开始加载。

  1. 概念态
  2. 定义态
  3. 纯净态
  4. 成熟态

实例化一个ApplicationContext的对象;
调用bean工厂后置处理器完成扫描;
循环解析扫描出来的类信息;
实例化一个BeanDefinition对象来存储解析出来的信息;
把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来, 以便后面实例化bean;
再次调用其他bean工厂后置处理器;

然后,会检查是否lazy,是否抽象等等,再通过反射进行实例化得到纯净的bean,然后进行属性注入,创建AOP等初始化,在put到单例池。

3.Bean管理

在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。所以所谓的Bean管理就是对对象的管理。包含创建对象、给对象注入属性。

Spring IoC容器管理Bean有两种方式:基于 xml 配置文件;基于注解方式。

3.1什么是Bean,JavaBean和SpringBean和对象的区别?

由SpringIOC容器管理的对象就是bean,由IOC容器实例化组装和管理的对象

3.2配置bean有几种方式?

  1. xml配置文件直接配置
  2. @Component(Controller,Service,Repostory) 前提得配置扫描,是直接通过反射得到的。
  3. JavaConfig:@Bean是标注在方法上面的,必须返回一个对象作为Bean,一般是加载Configuration注解下面的。
  4. @Import

3.3Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定为Bean指定的作用域。Spring支持如下6种作用域。

  • singleton:单例模式,在整个Spring IOC容器中,singleton作用域的Bean将只生成一个实例。
  • prototype:每次通过容器的getBean()方法获取prototype作用域的Bean域时,都将产生一个新的Bean实例。
  • Request:每次发起请求都创建一个Bean
  • Session:每个对话都创建一个对象
  • application:一个全局应用共享对象
  • websocket:在整个WebSocket的通信过程中,该Bean只生成一个实例。只有在web应用中使用Spring时,该作用域才真正生效。

3.4Spring的Bean是线程安全的吗?如何处理单程并发问题?

并不是,单例Bean在类中声明了成员变量并且有读写操作,这个时候就会出现线程不安全的情况。但是,只需要把成员变量放在方法中,就是线程安全的了。
解决方法:

  1. 设置为多例
  2. 将成员变量放在ThreadLocal中,也就是让成员变量与本地的线程做关联,这样就是线程安全的了。
  3. 使用同步锁 比如加入synchronized就行了

3.5实例化Bean有几种方式

  1. 构造器方式(反射机制)

  2. 实例工厂方式(@bean)
  3. FactoryBean

3.6解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?

autowired:

  1. byName 根据bean的名字进行装配,如果bean的名字和属性相同,就会自动相配
  2. ByType:根据参数的数据类型进行自动装配
  3. constructor:利用构造函数进行装配
  4. no:不自动装配,手动通过ref属性或者Autowired手动指定需要自动注入的属性。

3.7Bean的生命周期回调方法

有两个重要的bean 生命周期方法,第一个是init , 它是在容器加载bean时候被调用。第二个方法是 destroy 它是在容器卸载类的时候被调用。

bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。

3.8Bean的生命周期

  1. 实例化:通过反射去推断构造函数进行实例化
  2. 属性赋值:解析自动装配,进行依赖注入(循环依赖)
  3. 初始化:调用Aware回调方法,调用初始化生命周期回调方法(如果实现了它)如果bean出现了aop,那就会创建动态代理。
  4. 销毁:Spring容器关闭的时候进行调用。

3.9如何解决循环依赖

三级缓存。

一级缓存:存储完整的Bean

二级缓存:避免动态代理重复创建

三级缓存:缓存函数接口,可以通过lambda表达式吧方法传进去,但是不会立刻调用,可能会aop创建。

如何在bean创建完之后进行扩展?

beanFactoryPostProcesso

Spring如何避免在并发下获取不完整的Bean?

设置双重检查锁, 在创建bean的过程中加锁,直到放到一级缓存中。但是不是放到一级缓存中,而是把锁加载到二级缓存中。而另一个线程会在能执行后再次寻找一级缓存中的bean。
之所以不放在一级缓存里,是因为性能——因为锁住一级缓存,其他线程就拿不出来其他的bean,会影响其他线程的性能。

JavaConfig是如何代替Spring.xml方式的?
1.以前Xml
a. Spring容器 ClassPathXmlApplicationContext(“xml”)
b. Spring.xml
c. bean的配置:< bean scope lazy>
d. 扫描包: < component-scan>
e. 引入外部属性配置文件 < property-placeHodeler resource=“xxx.properties”>
f. < property name=“password” value=“${mysql.password}”></ property>
g. 指定其他配置文件:<import resource=“”
2.javaconfig
a. Spring容器:AnnotationConfigApplicationContext(javaconfig.class)
b. 配置类 @Configuration
c. @Bean @Scope @Lazy
d. 扫描包: @ComponentScan
e. 引入外部属性配置文件 @PropertySource(“classpath:db.properties”)
f. @Value(“${mysql.password}”)
g. @Import @Import({配置类}) 使用比较灵活
 

如何在没有找到自动注入的bean/找到多个bean的时候不报错

@Autowired(required = false) 不报错
@Primary 多个bean

4.Bean的属性

4.1简单值的注入

将简单的值注入到Bean中是非常容易的,首先定义一个类,通过暴露setter方法来声明它所需设置的属性。

Bean代码如示:

package com.zhao.service.impl;

import com.zhao.dao.BookDao;
import com.zhao.service.BookService;

/**
 * 低配版
 */
public class BookServicelmpl implements BookService {

    private String name;
    private BookDao bookDao;

    public void setName(String name) {
        this.name = name;
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }




    @Override
    public void save() {

        System.out.println("BookServicelmpl...save"+name);
        bookDao.insert();

    }
}

配置文件中:

<?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的IOC容器中,并起上名字
    id:表示给对象起名字
    class:类的全类名
    -->
    <bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>

    <bean id="bookService" class="com.zhao.service.impl.BookServicelmpl">
        <property name="name" value="张三" />
        <property name="bookDao" ref="bookDao" />
    </bean>
</beans>

测试类:

package com.zhao.servlet;

import com.zhao.service.BookService;
import com.zhao.service.impl.BookServiceVip;
import com.zhao.service.impl.BookServicelmpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class BookServlet {

    BookService bookService;

    /**
     * 更换功能需要修改源代码
     */
    @Test
    public void add(){
        System.out.println("BookServlet...add");
        //1.获得IOC容器
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
        //2.根据名字从容器中获取对象
        bookService= (BookService) context.getBean("bookService");
        bookService.save();
    }

}

集合注入:

Bean组件的属性可能不仅仅是单一的值,而是一个对象集合。接下来的示例显示了如何最常用的List、Map和Properties类型的集合属性。首先定义一个类,代码如下:

接口类:

package com.zhao.service;

public interface BookService {
    void save();
}

业务处理:

package com.zhao.service.impl;

import com.zhao.service.BookService;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class BookSeviceVip implements BookService {
    List<String> list;
    Set<String> set;
    Map<String,String> map;

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    @Override
    public String toString() {
        return "BookSeviceVip{" +
                "list=" + list +
                ", set=" + set +
                ", map=" + map +
                '}';
    }

    @Override
    public void save() {

        System.out.println("BookSeviceVip...save");
    }
}

配置文件中:

<?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">
    <bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <bean id="bookService1" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

    <bean id="bookService2" class="com.zhao.service.impl.BookSeviceVip" scope="singleton">
        <property name="list">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王二</value>
                <value>麻子</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>南阳</value>
                <value>郑州</value>
                <value>周口</value>
                <value>信阳</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="name" value="张三"/>
                <entry key="age" value="20"/>
                <entry key="sex" value="男"/>
            </map>
        </property>
    </bean>
    <!--    配置工厂,但是获得的工厂的产品-->
    <bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>

测试类:

package com.zhao.test;

import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test03 {

    @Test
    public void test01(){
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
        BookService bookService1= (BookService) context.getBean("bookService");
        System.out.println(bookService1);

        BookService bookService= (BookService) context.getBean("bookService2");
        System.out.println(bookService);

    }
}

测试结果:

5.单例和原型

单例(singleton)和原型(prototype)是Bean的两种常用作用域。如果不指定Bean的作用域,Spring默认使用singleton作用域。

singleton作用域的Bean实例是同一个共享实例,可以重复使用。而prototype作用域的Bean实例都是全新产生的实例,相当于new操作。而实例的创建和销毁等工作都会增加系统的开销,因此,应该尽量避免将Bean设置成prototype作用域。

<?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">
    <bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <bean id="bookService1" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
 </bean>
    <!--    配置工厂,但是获得的工厂的产品-->
    <bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>

测试代码如下:

package com.zhao.test;

import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {

    @Test
    public void test01(){
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
        BookService b1= (BookService) context.getBean("bookService");
        BookService b2= (BookService) context.getBean("bookService");

        System.out.println(b1);
        System.out.println(b2);

        //单例,框架会唯一创建对象,后续获取的都是同一个,在配置文件加载的时候就创建了
        //多例,框架在每次获取的时候创建一个新的,在获得时候创建  多线程
    }
}

6.注解

6.1@Autowired 注解有什么作用

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。

6.2atuowired和resource有什么区别?

autowired是spring提供的,默认类型。
resource是jdk提供的,默认名字。

6.3使用@Autowired注解自动装配的过程是怎样的?

6.3.1@Autowired 通过Bean的后置处理器进行解析的

在创建一个spring容器的时候,在构造函数中进行注册。

  1. 在实例化后预解析(解析@Autowired标注的属性、方法 比如:把属性的类型、名称、属性所在的类… 元数据缓存起)
  2. 在属性注入真正的解析(拿到上一步缓存的元数据 去ioc容器帮进行查找,并且返回注入)
  3. 首先根据预解析的元数据拿到 类型去容器中进行查找

如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false

6.4使用@Component注解装配Bean

  • Component代表Spring IOC会把这个类扫描生成Bean实例,而其中的value属性代表这个类在Spring中的Id,相当于XML方式定义的Bean的ID,也可以写成@Component(BookDaoImpl ),或者直接写成@Component,那么其ID就是类名首字母小写。
  • @Value:代表值的注入,这里只是注入一些简单的值,注入的时候Spring会成为其转化类型。

接口:

package com.zhao.dao;

public interface BookDao{
    void insert();
}

在dao包中的impl包里定义一个BookDaoImpl类:

package com.zhao.dao.impl;

import com.zhao.dao.BookDao;
import org.springframework.stereotype.Component;

@Component
public class BookDaoImpl implements BookDao {
    @Override
    public void insert() {

        System.out.println("BookDaoImpl...insert");
    }
}

配置文件:

<?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">

    <!--让系统去扫描注解-->
    <context:component-scan base-package="com.zhao"/>
</beans>

接口:

package com.zhao.service;

public interface BookService {
    void save();
}

在service中创建一个impl包然后定义一个BookServiceImpl类:

package com.zhao.service.impl;

import com.zhao.dao.BookDao;
import com.zhao.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("bookService")
public class BookServiceImpl implements BookService {

    //自动注入优先按照类型注入
    @Autowired
    BookDao bookDao;

//    public void setBookDao(BookDao bookDao) {
//
//        this.bookDao = bookDao;
//    }

    @Override
    public void save() {
        System.out.println("BookServiceImpl...save");
        bookDao.insert();
    }
}

测试类:

package com.zhao.servlet;

import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
    BookService bookService;

    @Test
    public void test01(){
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
        bookService= (BookService) context.getBean(BookService.class);//按照类型获得对象,前提类型有唯一的对象
        bookService.save();
    }
}

测试结果:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring IOC(Inversion of Control,控制反转)是Spring框架的核心之一,是一种设计模式,它将对象的创建、依赖关系的管理以及对象的生命周期等管理交给Spring容器来完成,从而降低了组件之间的耦合性,提高了系统的可维护性和可扩展性。 Spring IOC的知识体系主要包括以下几个方面: 1. 容器:Spring容器是Spring IOC的核心,它负责管理对象的创建、依赖关系的维护和对象的生命周期等,Spring容器主要有两种:BeanFactory和ApplicationContext。 2. BeanBean是Spring容器中的对象,它们由Spring容器创建、管理和维护,Spring中的Bean原型、会话和请求等不同的作用域。 3. 依赖注入:依赖注入是Spring IOC的一个重要特性,它通过将对象的依赖关系注入到对象中,实现对象之间的解耦。 4. 生命周期:Spring容器负责管理Bean的生命周期,包括Bean的创建、初始化、使用和销毁等,Spring提供了BeanPostProcessor和BeanFactoryPostProcessor等机制,允许在Bean的生命周期中进行自定义操作。 5. AOP:AOP是Spring框架的另一个核心模块,它通过动态代理机制实现了代码的横切关注点的统一管理,与IOC紧密关联。 6. 事件机制:Spring容器提供了事件机制,允许Bean之间的通信,使得Bean之间的解耦更加彻底。 7. 注解:Spring提供了一系列注解,简化了开发人员对Spring容器的配置,使得开发变得更加方便和快捷。如,@Autowired注解可以自动注入Bean依赖关系,@Component注解可以将一个普通的Java类声明为Bean等。 以上是Spring IOC知识体系的主要内容

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值