Day68.Spring的基本使用、IoC、DI、Bean的生命周期

目录

一、Spring简介

1.Spring Framework

2. IOC容器

3. IoC (Inversion of Control) 控制反转

4. IOC容器的API  (IOC容器)

二、基于XML管理bean

1. Hello Spring (通过id获取bean) ★

2. 通过类名、接口获取Bean  (每个类值只能配置一次Bean) ★

3. 给bean的属性赋值: 注入简单属性  (property) ★

4. 注入复杂属性 引用已声明的外部bean  (ref) ★

5. 属性赋值:内部bean ★

6. 给bean的属性赋值:引入外部属性文件 ★

7. 给bean的属性赋值:级联属性赋值

8. 给bean的属性赋值:构造器注入  (constructor-arg)

9. 给bean的属性赋值:特殊值处理

10. 给bean的属性赋值:使用p名称空间 (了解)

11. 给bean的属性赋值:集合属性

12. 集合类型的bean

13. FactoryBean机制  实现FactoryBean<>

14. bean的作用域  ★

15. 自动装配  (autowire) 

三、bean的生命周期 ★


一、Spring简介

Spring是一家公司,也是一门技术,就像名字一样,Spring出现就像软件开发的春天一样。Spring是一个轻量级控制反转(ioC) 面向切面(AOP)容器框架更利于开发和维护,代码优雅,实现了解耦

优点:Ioc、AOP、容器、声明式、组件化、一站式、非入侵性、轻量级

罗德·约翰逊 (Rod Johnson) Spring框架的创始人,同时也是SpringSource的联合创始人。

2002,首次推出了Spring框架的雏形:interface21框架。Spring框架以interface21框架为基础,经过重新设计,并不断丰富其内涵,与2004年3月24日发布了1.0正式版。

经过一系列的眼花缭乱的商业并购,其中涉及SpringSource、EMC、VMWare、Pivotal等公司,最终花落Pivotal公司。 官网:Spring | Home

Spring技术发展的四个阶段 (Spring全家桶)
第一阶段 spring-core (spring):spring core、spring security、spring data
第二阶段 spring-boot:快速开发,提升程序的开发效率,使得程序从可用变得好用
第三阶段 spring-cloud 微服务。推动微服务架构的落地,让不具备开发微服务的小型互联网公司也能享受到开箱既用的微服务解决方案
第四阶段 spring cloud dataflow 基于微服务的分布式流处理和批处理数据通道,目标是简化大数据应用开发

1.Spring Framework

① Spring Framework关键点

  • Spring 可以说是Java世界最成功的框架。在企业级应用中,大部分的企业架构都基于Spring框架。
  • Spring 是开源的,这一点和MyBatis、SpringMVC、Struts2、Hibernate等一样。
  • Spring 的出现是因为Sun EJB的失败。EJB是重量级技术,功能强大,但配置繁杂,占用资源多,对EJB容器有依赖(侵入性),测试不方便,运行缓慢。而Spring是轻量级技术,保持技术强大的同时,克服了EJB的种种缺点。
  • 2004 年 03 月,Spring 1.0 版发布。2017 年 09 月,Spring 5.0 发布,基于JDK8(流式编程)
  • Spring 的成功来自理念,而不是技术。最核心的理念是IoC(控制反转)和AOP(面向切面编程)。基础是IoC,AOP的最典型应用当属数据库事务的处理。
  • Spring不是为了取代现有技术,不重复的造轮子,而是提供更好的整合模板来整合这些技术,开发更简单。
  • 特别强调 Spring 的优点非侵入性或者低侵入性。使用POJO开发,不需要继承Spring API,通过配置扩展POJO功能,通过依赖注入、AOP、面向接口编程等,降低耦合性。即使Java应用离开了Spring也可以运行。
  • Spring 已经从一个产品发展为一个家族,处理Spring之外,还有SpringMVC、Spring Security、SpringData、SpringBoot和SpringCloud等,但他们的基础都是Spring 的 IOC 和 AOP。

② Spring Framework优良特性

  • 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
  • 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
  • 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。
  • 容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
  • 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。

③ Spring Framework五大功能模块

Ioc、AOP、MyBatis、ScriptMVC、Test

功能模块功能介绍
Core Container (IoC)核心容器,在 Spring 环境下使用任何功能都必须基于 IOC 容器
AOP&Aspects (AOP)面向切面编程
Testing (Test)提供了对 junit 或 TestNG 测试框架的整合。
Data Access/Integration (MyBatis)提供了对数据访问/集成的功能。
Spring MVC提供了面向Web应用程序的集成功能

2. IOC容器

生活中的容器:杯子。    程序中的容器:数组、List、Set

①生活中的复杂容器

政府管理我们的一生,生老病死都和政府有关。

②程序中的复杂容器

Servlet 容器能够管理 Servlet、Filter、Listener 这样的组件的一生,所以它是一个复杂容器。
IOC 容器也是一个复杂容器,负责管理Bean。它们不仅要负责创建组件的对象、存储组件的对象,还要负责调用组件的方法让它们工作,最终在特定情况下销毁组件。

[1]Servlet生命周期

名称时机次数
创建对象默认情况:接收到第一次请求
修改启动顺序后:Web应用启动过程中
一次
初始化操作创建对象之后一次
处理请求接收到请求多次
销毁操作Web应用卸载之前

[2]Filter生命周期

生命周期阶段执行时机执行次数
创建对象Web应用启动时一次
初始化创建对象后一次
拦截请求接收到匹配的请求多次
销毁Web应用卸载前一次

3. IoC (Inversion of Control) 控制反转

  • IoC:反转控制方式获取资源

IOC (Inversion of Control) 控制反转设计思想,由容器将设计好的对象交给容器控制,而非对象内部直接new。将new对象的控制权从应用的代码本身转移到了外部XML文件

作用:对象之间解耦。利于后期维护和修改。(修改xml文件不同于修改.java源代码,不涉及重新编译、打包、部署等操作)

  •  DI:依赖注入

IoC是一种控制反转思想,DI是它的一种具体实现方式

DI(Dependency Injection)依赖注入,Spring框架的核心之一。实现IOC的依赖注入策略,依赖对象通过注入进行创建,由IOC容器负责创建和注入。
注入:将依赖关系的类放在IOC容器中,IOC就会根据依赖的关系NEW对象的实例。比如MVC模式

结论:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。

4. IOC容器的API  (IOC容器)

Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。

Spring认为一切类都是Bean,比如实体类、DAO类、业务层、控制类、通知类等,容纳这些Bean的是Spring提供的IoC容器,所以Spring是一种基于Bean的编程。

BeanFactory

IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

② ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

以后在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关

③ApplicationContext的主要实现类

类型名简介
ClassPathXmlApplicationContext
Java项目
通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext
Web项目
专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

二、基于XML管理bean

1. Hello Spring (通过id获取bean) ★

由 Spring 的 IOC 容器创建类的对象。

①创建接口实现类

public interface HappyComponent {
    public void doWork();
}
public class HappyComponent1 implements HappyComponent {
    @Override
    public void doWork() {
        System.out.println("doWork01");
    }
}
public class HappyComponent2 implements HappyComponent {
    @Override
    public void doWork() {
        System.out.println("doWork02");
    }
}

②导入依赖 spring-context


<dependencies>
    <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

 

③创建 Spring 配置文件、定义bean

  • bean标签:通过配置bean标签告诉IOC容器需要创建对象的组件是什么
  • id属性:bean的唯一标识
  • class属性:组件类的全类名
<?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 -->
    <!--配置Bean
        class是Bean的完整路径(全类名),名字叫做id
        使用反射
        1.解析XML: DOM4j等解析xml技术
        2.获取类对象 Class clazz = Class.forName(className)
        3.使用反射创建对象
            Object happyComponent = clazz.newInstance();
            Object happyComponent = clazz.getContrucotor().newInstance();-->

    <!-- 实验一 [重要]创建bean -->
    <bean id="happyComponent1" class="com.atguigu.pojo.HappyComponent1"></bean>
    <bean id="happyComponent2" class="com.atguigu.pojo.HappyComponent1"></bean>

</beans>

④创建测试类 使用Ioc操作测试

注意:Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要无参构造器时,没有无参构造器,则会抛出异常:BeanCreationException

    //一、使用IoC创建bean
    //1.从Ioc容器中获取已经创建好的对象,对象有IoC容器根据配置文件来创建。
    //2.解耦: 如果要修改实现类,只需要该配置文件,不需要改动源码
    @Test
    public void test1_2(){
        //1.创建IoC容器
        //ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("classpath:spring.xml");
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

        //2.从IoC容器通过id获取指定Bean对象
        //Object happyComponent = context.getBean("happyComponent");
        HappyComponent hc = context.getBean("happyComponent1", HappyComponent.class);

        //3.执行方法
        hc.doWork();
    }

⑤用IOC容器创建对象和自己建区别

2. 通过类名、接口获取Bean  (每个类值只能配置一次Bean) ★

    <!-- 实验一 使用IoC,通过id获取bean-->
    <bean id="happyComponent" class="com.atguigu.pojo.HappyComponent1"></bean>

    <!--实验二 通过类型、接口获取bean-->
    <!--<bean id="happyComponent2" class="com.atguigu.pojo.HappyComponent2"></bean>-->
//实验二 通过类型、接口获取bean
    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        //方式1.从IoC容器通过id获取指定Bean对象
        HappyComponent bean = context.getBean("happyComponent", HappyComponent.class);

        //方式2:通过类型获取bean 前提: 同一个类值只配置一次Bean
        HappyComponent bean = context.getBean(HappyComponent1.class);

        //方式3:按照接口名获取Bean 前提:同一个接口的实现类只配置一次Bean
        HappyComponent  bean = context.getBean(HappyComponent.class);
        System.out.println(bean);
    }

如果相同类型的 bean 在IOC容器中配置了多个:

根据类型获取时会抛出异常:org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.ioc.component.HappyComponent' available: expected single matching bean but found 2: happyComponent,happyComponent2

3. 给bean的属性赋值: 注入简单属性  (property) ★

简单类型 (基本类型、包装类、String) 的成员变量赋值,使用 <property>

name:属性名称     value:赋具体值

    <bean id="happyComponent" class="com.atguigu.pojo.HappyComponent1">
        <!--实验三: 给bean的属性赋值: 注入简单属性--> 
        <property name="componentName" value="BMW"></property>
    </bean>

<!--如果给简单类型的成员变量赋值,可以通过property标签实现  
    底层通过反射调用set方法实现,所以set 方法必须提供-->
    @Test
    public void test3(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        HappyComponent bean = context.getBean("happyComponent",HappyComponent.class);
        bean.doWork();    //doWork01--componentName:BMW
    }

4. 注入复杂属性 引用已声明的外部bean  (ref) ★

name:属性名称     ref:指向某一个已经存在的Bean 

public class HappyMachine {
    private String machineName;//简单属性
    private HappyComponent happyComponent;//复杂属性
    set()..toString()...
}
     <bean id="happyComponent" class="com.atguigu.pojo.HappyComponent1">
        <property name="componentName" value="BMW"></property>
    </bean>
    <!--实验四: 注入复杂属性 引用已声明的外部bean-->
    <bean id="happyMachine" class="com.atguigu.pojo.HappyMachine">
        <property name="machineName" value="Benz"></property>
        <property name="happyComponent" ref="happyComponent"></property>
    </bean>
    @Test
    public void test4(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        HappyMachine happyMachine = context.getBean("happyMachine", HappyMachine.class);
        System.out.println(happyMachine);//HappyMachine{machineName='Benz', happyComponent=HappyComponent1{componentName='BMW'}}
    }

5. 属性赋值:内部bean ★

    <!--实验五、给bean赋值: 引用内部bean-->
    <bean id="happyMachine2" class="com.atguigu.pojo.HappyMachine">
        <property name="machineName" value="BMW"></property>
        <property name="happyComponent">
            <!--内部Bean,作用范围就是当前property -->
            <bean class="com.atguigu.pojo.HappyComponent1">
                <property name="componentName" value="door"></property>
            </bean>
        </property>
    </bean>
    @Test
    public void test5(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        HappyMachine happyMachine = context.getBean("happyMachine2", HappyMachine.class);
        System.out.println(happyMachine);
    }

6. 给bean的属性赋值:引入外部属性文件 ★

①加入依赖

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.3</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
    <!--驱动器jar包下driver的全类名-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"></property>
        <property name="username" value="root"></property>
        <property name="password" value="*******"></property>
    </bean>

 ②创建外部属性文件

//属性文件jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis-example?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=*******

③ 引入数据源、赋值

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--实验六 引入外部属性文件 给bean的属性赋值-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </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
       https://www.springframework.org/schema/context/spring-context.xsd">

④测试 

    //实验六: 给bean的属性赋值:引用外部属性文件 (连接池)
    @Test
    public void test6() throws SQLException {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        DataSource dataSource = context.getBean("dataSource", DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }

7. 给bean的属性赋值:级联属性赋值

注意:底层方法的调用:getHappyComponent().setComponentName(),必须提供这些方法

    <!--实验七 级联属性赋值-->
    <bean id="happyMachine3" class="com.atguigu.pojo.HappyMachine">
        <property name="machineName" value="Benz"></property>
        <!--注意: 必须先引用已声明的外部bean,才能给它赋值-->
        <property name="happyComponent" ref="happyComponent"></property>
        <property name="happyComponent.componentName" value="engine"></property>
    </bean>
    //实验七: 级联属性赋值
    @Test
    public void test7(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        HappyMachine happyMachine = context.getBean("happyMachine3", HappyMachine.class);
        System.out.println(happyMachine);
    }

8. 给bean的属性赋值:构造器注入  (constructor-arg)

  1. 必须提供对应有参数构造方法
  2. 一旦提供了有参数构造方法,默认的无参数构造方法将不再提供,可能导致某些使用无参数构造方法创建的Bean无法创建,此时必须同时提供无参数构造方法
  3. 对应一个类,一般都要提供无参数构造方法(因为这里Spring创建Bean,也因为子类默认调用父类的无参数构造方法)

一般推荐使用setter方法注入

    <!--实验八 构造器注入-->
    <bean id="happyComponent3" class="com.atguigu.pojo.HappyComponent1">
        <constructor-arg name="componentName" value="BYD"></constructor-arg>
    </bean>
    <!--注意顺序 可以用index标记 第几个参数-->
    <bean id="happyMachine4" class="com.atguigu.pojo.HappyMachine">
        <constructor-arg name="machineName" value="battery"></constructor-arg>
        <constructor-arg name="happyComponent" ref="happyComponent3" index="1"></constructor-arg>
    </bean>
    //实验八:给bean的属性赋值:构造器注入
    @Test
    public void test8(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
        HappyMachine happyMachine = context.getBean("happyMachine4", HappyMachine.class);
        System.out.println(happyMachine);
    }

9. 给bean的属性赋值:特殊值处理

    <!--实验九 特殊值处理-->
    <bean id="propValue" class="com.atguigu.pojo.PropValue">
        <property name="commonValue">
            <!-- null标签:将一个属性值明确设置为null -->
            <null></null>
        </property>
        <property name="expression1" value="5 &lt; 6"></property>
        <property name="expression2">
            <value><![CDATA[5<6]]></value>
        </property>
    </bean>
    //实验九 特殊值处理
    @Test
    public void test9(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring2.xml");
        PropValue propValue = context.getBean("propValue", PropValue.class);
        System.out.println(propValue);  //PropValue{commonValue='null', expression1='5 < 6', expression2='5<6'}
    }
public class PropValue {
    private String commonValue;
    private String expression1;
    private String expression2;
}

10. 给bean的属性赋值:使用p名称空间 (了解)

<?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:p="http://www.springframework.org/schema/p"   //这里
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        ">

    <!--实验十 使用p名称空间赋值-->
    <bean id="happyComponent" class="com.atguigu.pojo.HappyComponent1" p:componentName="123456">

    </bean>

11. 给bean的属性赋值:集合属性

集合标签:<array> <list>  <set> <map> <props>

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private Integer age;
    private double [] scoreArr; //不需要分配空间
    private List<Course> courseList = new ArrayList<Course>();
    private Set<String> friendSet; //不需要创建对象
    private Map countriesMap = new HashMap<String,String>();
    private Properties properties;
}
 <value>98</value>
            </array>
        </property>
        <property name="courseList">
            <list>
                <bean class="com.atguigu.pojo.Course">
                    <property name="cno" value="1"></property>
                    <property name="cname" value="2"></property>
                </bean>
            </list>
        </property>

        <property name="friendSet">
            <set>
                <value>马云</value>
                <value>马化腾</value>
                <value>马斯特</value>
            </set>
        </property>

        <property name="countriesMap">
            <map>
                <entry key="cn" value="China"></entry>
                <entry key="us" value="the United States"></entry>
                <entry key="jp" value="Japan"></entry>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">******</prop>
                <prop key="url">jdbc:mysql://....</prop>
                <prop key="driverClassName">com.mysql.cj.jdbc.Driver</prop>
            </props>
        </property>
    </bean>

12. 集合类型的bean

    <!--实验十二 集合类型的bean-->
    <util:list id="courseList">
        <bean class="com.atguigu.pojo.Course">
            <property name="cno" value="1"></property>
            <property name="cname" value="Java"></property>
        </bean>
        <bean class="com.atguigu.pojo.Course">
            <property name="cno" value="2"></property>
            <property name="cname" value="HTML"></property>
        </bean>
        <bean class="com.atguigu.pojo.Course">
            <property name="cno" value="1"></property>
            <property name="cname" value="Web"></property>
        </bean>
    </util:list>
    //实验十二 集合类型的bean
    @Test
    public void test13(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring2.xml");

        HappyMachine happyMachine = context.getBean("happyMachine", HappyMachine.class);
        System.out.println(happyMachine);
    }

13. FactoryBean机制  实现FactoryBean<>

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

举例

  1. Spring整合MyBatis的时候会有一个SqlSessionFactoryBean,返回值不是SqlSessionFactoryBean,也不是SqlSessionFactory,而是SqlSession
  2. Spring整和定时器Quartz组件,会用到多个FactoryBean
    //getObject方法的返回值
    //适用于Bean比较复杂的情况
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HappyMachineFactoryBean implements FactoryBean<HappyMachine> {
    private String machineName;
    private String compoName;
    //得到的是getObject方法的返回值
    @Override
    public HappyMachine getObject() throws Exception {
        HappyMachine happyMachine = new HappyMachine();
        happyMachine.setMachineName(machineName);
        happyMachine.setHappyComponent(new HappyComponent1(compoName));
        return happyMachine;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}
    <!--十三 FactoryBean机制-->
    <bean id="happyMachine" class="com.atguigu.pojo.HappyMachineFactoryBean">
        <property name="machineName" value="特斯拉"></property>
        <property name="compoName" value="刹车"></property>
    </bean>
    //实验十二 集合类型的bean
    @Test
    public void test13(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring2.xml");
        HappyMachine happyMachine = context.getBean("happyMachine", HappyMachine.class);
        System.out.println(happyMachine);//HappyMachine{machineName='特斯拉', happyComponent=HappyComponent1{componentName='刹车'}}
    }

14. bean的作用域 <scope> ★

取值含义创建对象的时机
singleton在IOC容器中,这个bean的对象始终为单实例IOC容器初始化时
prototype这个bean在IOC容器中有多个实例获取bean时
    <!--十四 bean的作用域-->
    <bean id="happyComponent2"
          class="com.atguigu.pojo.HappyComponent1"
          scope="prototype">
        <property name="componentName" value="12345"></property>
    </bean>
    //实验十四 作用域 默认是单例的(singleton),可以通过scope=prototype 修改
    @Test
    public void testIoC14Singleton(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring2.xml");

        HappyComponent happyComponent1 = context.getBean("happyComponent2", HappyComponent1.class);
        HappyComponent happyComponent2 = context.getBean("happyComponent2", HappyComponent1.class);
        System.out.println(happyComponent1 == happyComponent2);//测试是否单例
    }
HappyComponent1构造器
    public HappyComponent1() {
        System.out.println("HappyComponent1 无参执行了");
    }

singleton:
prototype:

15. 自动装配  (autowire) 

autowire 自动装配可以减少配置量,选项:

  1. byName:Bean的property属性名和另外一个Bean的id 进行匹配
  2. byType:根据类型进行装配,必须保证装配的Bean只配置了一个
  3. no:不进行自动转配
  4. constructor:和有参构造方法的参数一致。使用不多
    <!--实验十五 自动装配-->
    <bean id="happyComponent" class="com.atguigu.pojo.HappyComponent1">
        <property name="componentName" value="engine"></property>
    </bean>
    <bean id="happyMachine" class="com.atguigu.pojo.HappyMachine" autowire="byName">
        <!-- 简单类型只有手动装配-->
        <property name="machineName" value="红旗"></property>
        <!-- 引用类型可以手动装配,也可以自动装配 -->
        <!--<property name="happyComponent" ref="happyComponent"></property>-->
    </bean>
    //实验十五 自动装配
    @Test
    public void test15(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring3.xml");
        HappyMachine happyMachine = context.getBean("happyMachine", HappyMachine.class);
        System.out.println(happyMachine);//HappyMachine{machineName='红旗', happyComponent=HappyComponent1{componentName='engine'}}
    }

三、bean的生命周期 ★

Bean中配置指定的初始化 和销毁方法 <init-method>  <destroy-method>

面试题!! 注意,Bean是在ioc容器初始化时创建的

其中红框标记四个阶段的是基本阶段

MyBeanPostProcessor是针对所有Bean的扩展阶段需定义BeanPostProcessor实现类并配置

未进行标记的是只针对当前Bean的阶段,需要当前Bean实现相应的接口

    <!--实验十五 生命周期  init-method自定义初始化方法  destroy-method自定义销毁方法-->
    <bean id="course" class="com.atguigu.pojo.Course" init-method="init" destroy-method="destory">
        <property name="cno" value="1"></property>
        <property name="cname" value="Java"></property>
    </bean>
    <!--BeanPostProcessor 对所有Bean有效,自定义初始化前后执行-->
    <bean class="com.atguigu.pojo.MyBeanPostProcessor"></bean>
//BeanPostProcessor 对所有Bean有效,自定义初始化前后执行
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before Init");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before After");
        return null;
    }
}
public class Course implements InitializingBean {
    private Integer cno;
    private String cname;
    //初始化方法
    public void init(){
        System.out.println("Course init初始化方法");
    }
    //销毁方法
    public void destory(){
        System.out.println("Course destory销毁方法");
    }
    //只针对当前Bean有效
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean — afterPropertiesSet");
    }
    public Course() {
        System.out.println("Course 构造方法");
    }
} 
    //实验十六 生命周期
    @Test
    public void test16() throws InterruptedException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring3.xml");
        Course course = context.getBean("course", Course.class);
        System.out.println(course);

    }


 

  • bean对象创建(调用无参构造器)
  • 给bean对象设置属性
  • bean对象初始化之前操作(由bean的后置处理器负责)
  • bean对象初始化(需在配置bean时指定初始化方法)
  • bean对象初始化之后操作(由bean的后置处理器负责)
  • bean对象就绪可以使用
  • bean对象销毁(需在配置bean时指定销毁方法)
  • IOC容器关闭
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值