首页 > 技术文章 > Spring(配置Bean)

xjs1874704478 2019-12-06 19:58 原文

一、IOC和DI概述

1、IOC(Inversion of Control):

  其思想是反转资源获取的方向。传统的资源查找方式要求组件向容器发起请求查找资源,作为回应,容器适时的返回资源。而应用了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。

2、DI(Dependency Injection):

  IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter方法)接受来自如容器的资源注入。相对IOC而言,这种表述更直接。

二、配置Bean

    <!--配置bean
        class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参构造器
        id:标识容器中的bean。id唯一
    -->
    <bean id="helloWorld2" class="com.atguigu.spring.beans.HelloWorld">
        <property name="name" value="spring"/>
    </bean>

Spring容器:

  在SpringIOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用。

  Spring提供了两种类型的IOC容器实现。

    ==BeanFactory:IOC容器实现。

    ==ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口。

    ==BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory

    ==无论使用何种方式,配置文件是相同的。

ApplicationContext:

 

 

 其接口:

 

 

 获取ApplicationContext实例:

        //1、创建Spring的IOC容器
        //ApplicationContext代表IOC容器
        //ClassPathXmlApplicationContext:是ApplicationContext接口的实现类,该实现类从类路径下加载配置文件。
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

得到bean实例:

        //2、从IOC容器中获取Bean实例
        //利用id定位到IOC容器中的bean
        //HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld2");
        //利用类型返回 IOC容器中的bean,但要求IOC容器中必须只能有一个该类型的bean
        HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

依赖注入的方式

  Spring支持3种依赖注入方式

    1)属性注入

    2)构造器注入

    3)工厂方法注入(很少使用,不推荐)

1)属性注入即通过setter方法注入Bean的属性值或依赖的对象。

属性注入使用<property>元素,使用name属性指定Bean的属性名称,value属性或者<value>子节点指定属性值。

属性注入是实际应用中最常用的注入方式

    <bean id="helloWorld2" class="com.atguigu.spring.beans.HelloWorld">
        <property name="name" value="spring"/>
    </bean>

2)构造方法注入

通过构造方法注入Bean的属性值或者依赖的对象,它保证了Bean实例在实例化后就可以使用。

构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性。

    public Car(String brand, String corp, double price) {
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }

    public Car(String brand, String corp, int maxSpeed) {
        this.brand = brand;
        this.corp = corp;
        this.maxSpeed = maxSpeed;
    }

构造方法注入:

    <!--通过构造方法配置bean的属性-->
    <bean id="car" class="com.atguigu.spring.beans.Car">
        <constructor-arg value="Audi" index="0"></constructor-arg>
        <constructor-arg value="ShangHai" index="1"></constructor-arg>
        <constructor-arg value="300000" type="double"></constructor-arg>
    </bean>

    <!--使用构造器注入属性值可以指定参数的位置和参数的类型,以区分重载的构造器-->
    <bean id="car2" class="com.atguigu.spring.beans.Car">
        <constructor-arg index="0" type="java.lang.String" value="Baoma"/>
        <constructor-arg index="1" type="java.lang.String" value="BeiJing"/>
        <constructor-arg index="2" type="int" value="120"/>
    </bean>

注入属性值的细节

字面值:可用字符串表示的值,可以通过<value>元素标签或value属性进行注入。

 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式。

若字面值中包含特殊字符,可以使用<![CDATA[ ]]>把字面值包裹起来。

    <bean id="car2" class="com.atguigu.spring.beans.Car">
        <constructor-arg index="0" type="java.lang.String" value="Baoma"/>
        <!-- 如果字面值包含特殊字符可以使用<![CDATA[]>包裹起来 -->
        <!--属性值也可以使用value子节点进行配置-->
        <constructor-arg index="1" type="java.lang.String">
            <value><![CDATA[<ShangHai^>]]></value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>230</value>
        </constructor-arg>
    </bean>

引用其它 Bean

组成应用程序的Bean经常需要相互协作以完成应用程序的功能,要使Bean能够相互访问,就必须在Bean配置文件中指定对Bean的引用。

Bean的配置文件中,可以通过<ref>元素或ref属性为Bean的属性或者构造器参数指定对Bean的引用。

    <bean id="person" class="com.atguigu.spring.beans.Person">
        <property name="name" value="Tom"></property>
        <property name="age" value="23"></property>
        <!--可以使用property的ref属性建立bean之间的引用关系-->
        <!--<property name="car" ref="car2"></property>-->
        <property name="car">
            <ref bean="car2"></ref>
        </property>
    </bean>

也可以在属性或构造器里包含Bean的声明,这样的Bean称为内部Bean

内部Bean:

    <bean id="person" class="com.atguigu.spring.beans.Person">
        <property name="name" value="Tom"></property>
        <property name="age" value="23"></property>
        <!--内部Bean,不能被外部引用,只能在内部使用-->
        <property name="car">
            <bean class="com.atguigu.spring.beans.Car">
                <constructor-arg value="Ford"></constructor-arg>
                <constructor-arg value="ChangAn"></constructor-arg>
                <constructor-arg value="200000" type="double"></constructor-arg>
            </bean>
        </property>
    </bean>

注入参数详解:null值和级联属性

可以使用专用的<null/>元素标签为Bean的字符串或其它对象属性注入null值

和Struts、Hibernate等框架一样,Spring支持级联属性的配置。

级联属性:当两个bean关联时,从一个bean给另一个bean赋值。例如Person类中有Car类型的属性,从person给car中的属性赋值。

    <bean id="person2" class="com.atguigu.spring.beans.Person">
        <constructor-arg value="XJS"></constructor-arg>
        <constructor-arg value="18"></constructor-arg>
        <constructor-arg ref="car"></constructor-arg>
        <!--为级联属性赋值。注意:属性需要先初始化后才可以为级联属性赋值-->
        <property name="car.maxSpeed" value="222"></property>
    </bean>

集合属性:

    <bean id="car11" class="com.atguigu.spring.beans.collection.Car">
        <constructor-arg index="0" type="java.lang.String" value="宝马"/>
        <constructor-arg index="1" type="java.lang.String" value="北京"/>
        <constructor-arg index="2" type="double" value="500000"/>
    </bean>

    <bean id="car22" class="com.atguigu.spring.beans.collection.Car">
        <constructor-arg index="0" type="java.lang.String" value="奔驰"/>
        <constructor-arg index="1" type="java.lang.String" value="郑州"/>
        <constructor-arg index="2" type="double" value="600000"/>
    </bean>

    <!--测试如何设置集合属性-->
    <bean id="person3" class="com.atguigu.spring.beans.collection.Person">
        <property name="name" value="Mike"></property>
        <property name="age" value="27"></property>
        <property name="cars">
            <!--使用list节点为List类型的属性赋值-->
            <list>
                <ref bean="car11"/>
                <ref bean="car22"/>
                <!--内部bean-->
                <bean class="com.atguigu.spring.beans.collection.Car">
                    <constructor-arg value="Ford"></constructor-arg>
                    <constructor-arg value="ChangAn"></constructor-arg>
                    <constructor-arg value="200000" type="double"></constructor-arg>
                </bean>
            </list>
        </property>
    </bean>

 

 为Map类型的属性赋值:

    private Map<String,Car> cars;
    <!--配置Map属性值-->
    <bean id="newPerson" class="com.atguigu.spring.beans.collection.NewPerson">
        <property name="name" value="杰西卡"></property>
        <property name="age" value="18"></property>
        <property name="cars">
            <!--使用map节点及map的entry子节点配置Map类型的成员变量-->
            <map>
                <entry key="First" value-ref="car11"></entry>
                <entry key="Second">
                    <ref bean="car22"></ref>
                </entry>
            </map>
        </property>
    </bean>

为Properties类型的属性赋值:

    private Properties properties;
    <!--配置Properties属性值-->
    <bean id="dataSource" class="com.atguigu.spring.beans.collection.DataSource">
        <property name="properties">
            <!--使用props 和prop 子节点为Properties属性赋值-->
            <props>
                <prop key="user">root</prop>
                <prop key="password">root</prop>
                <prop key="jdbcUrl">jdbc:mysql://test</prop>
                <prop key="driverClass">com.mysql.jdbc.Driver</prop>
            </props>
        </property>
    </bean>

配置独立的集合bean

 

 

 首先在applicationContext.xml文件开头添加util的定义。

 xmlns:util="http://www.springframework.org/schema/util"
并在xsi:schemaLocation描述中加上以下描述:http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd

xml:

    <!--配置独立的集合bean,以供多个bean进行引用,需要导入util命名空间-->
    <util:list id="cars">
        <ref bean="car22"/>
        <ref bean="car11"/>
    </util:list>

    <bean id="person4" class="com.atguigu.spring.beans.collection.Person">
        <property name="name" value="Jessica"></property>
        <property name="age" value="18"></property>
        <property name="cars" ref="cars"></property>
    </bean>

使用p命名空间

 

 

 首先在applicationContext.xml文件开头添加p的命名空间。

p:没有xsd文件,直接加上下面这句就好了。

xmlns:p="http://www.springframework.org/schema/p"

 xml:

    <!--通过p命名空间为bean的属性赋值,需要先导入p命名空间,相对于传统的配置方式更简洁-->
    <bean id="person5" class="com.atguigu.spring.beans.collection.Person"
        p:name="TaeYeon" p:age="31" p:cars-ref="cars"></bean>

三、配置Bean(自动装配)

 之前的都是手动装配

 

    <bean id="address" class="com.atguigu.spring.beans.autowire.Address"
            p:city="BeiJing" p:street="huilongguan"></bean>

    <bean id="address2" class="com.atguigu.spring.beans.autowire.Address"
          p:city="BeiJing" p:street="huilongguan"></bean>

    <bean id="car" class="com.atguigu.spring.beans.autowire.Car"
        p:brand="BaoMa" p:price="500000"></bean>

    <!--可以使用autowire属性指定自动装配的方式,
        byName 根据bean的名字和当前bean的setter风格的属性名进行自动装配,若有匹配的,则自动装配,若,没有匹配的,则不装配。
        使用byName:上边的bean的名字和Person类中的属性相同
        byType 根据bean的类型和当前bean的属性的类型进行自动装配。若IOC容器中有一个以上的类型匹配的bean,则抛异常。
    -->
    <bean id="person_xjs" class="com.atguigu.spring.beans.autowire.Person"
        p:name="谢军帅" autowire="byName"></bean>

自动装配的缺点:

bean之间的关系:继承;依赖

 继承:和java中的不一样

抽象bean:bean的abstract属性为true,这样的bean不能被IOC容器实例化,只用来被继承配置

若某一个bean的class属性没有指定,则该bean必须是一个抽象bean
    <bean id="address" class="com.atguigu.spring.beans.autowire.Address"
        p:city="上海" p:street="陈家胡同"></bean>

    <!--bean配置的继承:使用bean的parent 属性指定继承哪个bean的配置-->
    <bean id="address2" p:street="谢家胡同" parent="address"></bean>

 

 

依赖:只有先初始化car之后,才能执行之后的代码。

    <bean id="car" class="com.atguigu.spring.beans.autowire.Car"
        p:brand="宝马" p:price="500000"></bean>

    <!--要求在配置Person时,必须有一个关联的car!-->
    <bean id="person" class="com.atguigu.spring.beans.autowire.Person"
        p:name="张多慧" p:address-ref="address2" depends-on="car"></bean>

bean的作用域

singleton;prototype;WEB环境作用域

xml:

    <!--scope:bean的作用域,
        singleton:默认值。IOC容器初始化创建bean的实例,在整个容器的生命周期内只创建这一个bean。单例的。
        prototype:原型的。IOC容器初始化时不创建bean的实例,而在每次请求时都创建一个新的bean实例,并返回。
     -->
    <bean id="car" class="com.atguigu.spring.beans.autowire.Car"
        p:brand="跑车" p:price="500000" scope="prototype"></bean>

测试:

        //singleton:在初始化IOC容器的时候就创建好bean的实例了
        //prototype:在初始化IOC容器的时候没有创建bean的实例

        Car car = (Car) context.getBean("car");

        Car car1 = (Car) context.getBean("car");

        //也就是同一个bean得到的实例是单例的,,默认
        System.out.println(car==car1);

使用外部属性文件

这里测试连接数据的配置文件,使用的是C3P0连接池

jar包:c3p0-0.9.1.2.jar,以及mysql-connector-java-5.1.37-bin.jar

Spring2.5之后的使用:

 

 先导入context命名空间

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd

db.properties:

user=root
password=root
driverclass=com.mysql.jdbc.Driver
jdbcurl=jdbc:mysql:///mybatis_sgg

xml文件:

    <!--到入属性文件-->
    <context:property-placeholder location="db.properties"/>


    <!--C3P0中的类-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--使用外部化属性文件 的属性-->
        <property name="jdbcUrl" value="${jdbcurl}"></property>
        <property name="driverClass" value="${driverclass}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>

        <!--直接配置-->
        <!--<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis_sgg"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>-->
    </bean>

测试:

        ApplicationContext context = new ClassPathXmlApplicationContext("beans-properties.xml");

        DataSource dataSource = (DataSource) context.getBean("dataSource");

        System.out.println(dataSource.getConnection());

SpEL

 

 

 

    <bean id="address" class="com.atguigu.spring.beans.spel.Address">
        <!--使用spel为属性赋一个字面值-->
        <property name="city" value="#{'安阳'}"/>
        <property name="street" value="宋村"/>
    </bean>

    <bean id="car" class="com.atguigu.spring.beans.spel.Car">
        <property name="brand" value="奥迪"/>
        <property name="price" value="500000"></property>
        <!-- zc:车轮的周长       使用SpEL  引用类的静态属性-->
        <property name="zc" value="#{T(java.lang.Math).PI * 2}"></property>
    </bean>

    <bean id="person" class="com.atguigu.spring.beans.spel.Person">
        <!--使用SpEl 来应用其他的bean-->
        <property name="car" value="#{car}"></property>

        <property name="name" value="#{'谢军帅'}"></property>
        <!--city:person的city属性    使用SpEl 来应用其他的bean 的属性-->
        <property name="city" value="#{address.city}"></property>
        <!--在SpEL 中使用运算符-->
        <property name="info" value="#{car.price>300000 ? '金领_有钱人':'白领_穷人'}"></property>
     </bean>

IOC容器中bean的生命周期

 

 

 Bean的后置处理器:

 

 添加后置处理器的生命周期:

 

 Car.java:

package com.atguigu.spring.beans.cycle;

/**
 * @Author 谢军帅
 * @Date2019/12/5 20:39
 * @Description
 */
public class Car {

    public Car() {
        System.out.println("car`s constructor...");
    }

    private String brand;

    public void setBrand(String brand) {
        System.out.println("setBrand...");
        this.brand = brand;
    }

    public void init(){
        System.out.println("init...");
    }

    public void destroy(){
        System.out.println("destroy...");
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                '}';
    }
}

自己写的bean后置处理器实现BeanPostProcessor接口:

package com.atguigu.spring.beans.cycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @Author 谢军帅
 * @Date2019/12/5 20:54
 * @Description
 */
/*是要处理所有的bean的*/
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization:"+bean+","+beanName);

        if ("car".equals(beanName)){/*可以对符合条件的bean进行过滤*/
            //...
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization:"+bean+","+beanName);
        return bean;
    }
}

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"
       xmlns:p="http://www.springframework.org/schema/p"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!---->
    <bean id="car" class="com.atguigu.spring.beans.cycle.Car"
            init-method="init"
            destroy-method="destroy">
        <property name="brand" value="宝马"></property>
    </bean>

    <!--实现接口BeanPostProcessor,
        并提供两个方法的具体实现
       分别会在init-method之前和之后调用

       bean:bean 实例本身
       beanName:IOC容器配置的bean的名字
       返回值:是实际上返回给用户的那个bean,注意:可以在以上两个方法中修改返回的bean,甚至返回一个新的bean
    -->
    <!--配置bean的后置处理器;不需要配置id,IOC容器自动识别是一个BeanPostProcessor-->
    <bean class="com.atguigu.spring.beans.cycle.MyBeanPostProcessor"></bean>

</beans>

测试:

public class Test_cycle {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context  = new ClassPathXmlApplicationContext("beans-cycle.xml");

        Car car = (Car) context.getBean("car");
        System.out.println(car);

        //关闭容器
        context.close();
    }
}

 通过工厂创建bean

 

 静态工厂方法:

 静态工厂方法:直接调用某一个类的静态方法就可以返回Bean的实例
public class StaticCarFactory {

    private static Map<String,Car> cars = new HashMap<String, Car>();

    static {
        cars.put("宝马ss",new Car("宝马ss"));
        cars.put("奔驰xx",new Car("奔驰xx"));
    }

    //静态工厂方法
    public static Car getCar(String name){

        Car car = cars.get(name);
        return car;
    }
}

xml配置:

    <!--通过静态工厂方法来配置bean-->
    <!--
        class属性:指向静态工厂方法的全类名
        factory-method:指向静态工厂方法的名字
        constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
    -->
    <bean id="car1" class="com.atguigu.spring.beans.factory.StaticCarFactory"
        factory-method="getCar">
        <constructor-arg value="宝马ss"></constructor-arg>
    </bean>

 

 实例工厂方法:

/*实例工厂方法:实例工厂的方法,即需要创建工厂实例,然后调用方法得到bean实例*/
public class InstanceCarFactory {
    private Map<String,Car> cars = null;

    public InstanceCarFactory(){
        cars = new HashMap<String, Car>();
        cars.put("奥迪",new Car("奥迪"));
        cars.put("宝马",new Car("宝马"));
    }

    public Car getCar(String brand){

        return cars.get(brand);
    }
}

xml配置:

    <!--配置工厂的实例-->
    <bean id="carFactory" class="com.atguigu.spring.beans.factory.InstanceCarFactory">

    </bean>

    <!--通过工厂实例配置bean的实例-->
    <!--
        factory-bean属性:指向实例工厂方法的bean
        factory-method:指向静态工厂方法的名字
        constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数
    -->
    <bean id="car2"
        factory-bean="carFactory"
        factory-method="getCar">
        <constructor-arg value="奥迪"/>
    </bean>

FactoryBean来配置bean:

是Spring提供的FactoryBean。

有的时候在配置一个bean的时候,需要用到IOC容器中的其他bean,这时候通过FactoryBean最合适。

//自定义Factory 需要实现FactoryBean接口
public class CarFactoryBean implements FactoryBean<Car> {

    private String brand;



    //返回bean对象实例
    @Override
    public Car getObject() throws Exception {
        return new Car("BMW");
    }


    //返回bean的类型
    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    //是不是单实例的
    @Override
    public boolean isSingleton() {
        return true;
    }

    public void setBrand(String brand) {
    }
}
    <!--
        通过FactoryBean 来配置Bean的实例
        class:指向FactoryBean的全类名
        property:配置FactoryBean的属性

        但实际返回的实例却是 FactoryBean的getObject()方法返回的实例!
    -->
    <bean id="car" class="com.atguigu.spring.beans.factorybean.CarFactoryBean">
        <property name="brand" value="BMW"></property>
    </bean>

通过注解配置Bean(一)

之前的配置bean的方式都是通过基于xml文件的方式。

基于注解配置Bean;基于注解配置来装配Bean的属性。

需要:引入jar包==spring-aop-4.0.0.RELEASE.jar;还要在xml配置文件中引入context的dtd约束。

 

 

!--指定Spring  IOC 容器 扫描的包-->
    <!--可以通过resource-pattern指定扫描的资源-->
    <context:component-scan
            base-package="com.atguigu.spring.beans.annotation"
            resource-pattern="repository/*.class"></context:component-scan>
<!--context:exclude-filter 子节点指定排除哪些指定表达式的组件-->
    <!--context:include-filter 子节点指定包含哪些表达式的组件,该子节点需要use-default-filters 配合使用 -->
    <context:component-scan base-package="com.atguigu.spring.beans.annotation" use-default-filters="false">
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
        <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
        <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring.beans.annotation.repository.UserRepository"/>-->
        <context:include-filter type="assignable" expression="com.atguigu.spring.beans.annotation.repository.UserRepository"/>
    </context:component-scan>

 

通过注解配置Bean(二)

 

 后置处理器===处理属性

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

推荐阅读