spring4-1-基础bean的配置

一.概念

1.轻量级开源框架;
2.简化企业级应用而生;
3.是一个IOC(DI)依赖注入和AOP(面向切面编程)容器框架;
4.在IOC和AOP基础上可以整合各种企业应用的开源框架和优秀的第三方类库

二.配置eclipse

以下是不同的eclipse对应的sts版本,请查看好自己的eclipse下载对应的版本

eclipse-kepler.4.3.1–>springsource-tool-suite-RELEASE-e4.3.1-updatesite.zip
eclipse-Mars.4.5.1–>springsource-tool-suite-3.7.2RELEASE-e4.5.1-updatesite.zip
eclipse-Mars.4.5.2–>springsource-tool-suite-3.7.3RELEASE-e4.5.2-updatesite.zip
eclipse-neno.4.6–>springsource-tool-suite-3.7.3RELEASE-e4.6-updatesite.zip

我的版本是eclipse-Mars.4.5.2安装完毕后,welcome页面显示Spring tool,但是在window->properties中却没有spring选项,而且安装完毕后在properties中连maven选项也不见了,这个问题整整折腾了我半天时间,卸了装,装了卸还是不行,最后问同事用的是eclipse-neno.4.6.3发行版,下载eclipse-neno.4.6.3

eclipse-1.jpg

下载后打开,提示JDK环境必须是JDK1.8以上,于是从JDK1.7升级到JDK1.8,

安装步骤如下:我用的是(springsource-tool-suite-3.8.4.RELEASE-e4.6.3-updatesite.zip)下载地址:https://spring.io/tools/sts/all

Paste_Image.png

spring-2.jpg
spring-3.jpg

安装完毕后在window->Preperences中就可以看到spring选项了;


spring-5.jpg

到此为止eclipse中spring插件配置结束,这个汗....

三.第一个helloWorld

  • 下载spring最新稳定版本,下载方式请查看:
    http://www.cnblogs.com/yy3b2007com/p/6783699.html

  • 新建java下过目,spring-1

  • 加载jar包(以下橙色背景的四个核心jar包必须加载)


    spring-6.jpg
  • 自己下载commons-logging.jar包并加载进去

  • 目录结构:


    spring-7.jpg
  • HelloWorld内容:

package com.lxf.spring.bean;

public class HelloWorld {
    private String name;
    public void setName(String name)
    {
        System.out.println("setName:"+name);
        this.name = name;
    }
    public void hello()
    {
        System.out.println("hello: "+this.name);
    }
    public  HelloWorld()
    {
        System.out.println("init HelloWorld...");
    }
}
  • Spring Bean Configuration File内容:
<?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-3.2.xsd">

     <!-- 配置bean -->
    <bean id="helloWorld" class="com.lxf.spring.bean.HelloWorld">
        <property name="name" value="zhangsan"></property>
    </bean>
</beans>
  • Main.java内容
package com.lxf.spring.bean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args)
    {
        /*
        HelloWorld helloworld = new HelloWorld();
        helloworld.setName("liangxifeng");
        */
        //1.创建spring的IOC容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从IOC容器中获取Bean实例
        //HelloWorld helloworld = (HelloWorld)ctx.getBean("helloWorld");
        //helloworld.setName("aaa");
        //3.调用hello方法
       // helloworld.hello();
    }
}

四.IOC(inversion of Control)和 DI(Dependency Injection)

  • IOC概念:
  • 思想是反转资源获取的方向
  • 传统的资源查找方式要求组件向容器发起请求查找资源.作为回应,容器实时的返回资源;
  • 应用了IOC之后,则是容器主动地就将资源推送给它所管理的组件
  • 组件所要做的只是选择一种合适的方式来接收资源;(也称查找的被动形式)
  • DI概念
  • IOC的另一种表达方式:即组件以一些预定义好的方式(例如:setter方法)接收来自容器的资源注入,相对IOC而言,这种表述更直接;
Paste_Image.png

五.IOC的前生

ioc-1.jpg

ReprotService要想生成报表需要依赖三个类,耦合度很高

ioc-2.jpg

使用工厂模式ReprotService要想生成报表需要依赖两个类,耦合度降低

ioc-3.jpg

使用反转控制ReprotService要想生成报表需要依赖一个类,耦合度最低

六.spring容器

  • 在Spring IOC容器读取Bean配置之前,自身要先实例化;

  • Spring提供了两种类型的IOC容器实现;
    (1). BeanFactory:IOC容器的基本实现(BeanFactory是spring框架的基础设施,面向Spring本身)
    (2). ApplicationContext 是BeanFactory的子接口,提供了更丰富的功能;(ApplicationContext 面向Spring框架的开发者,几乎所有应用场合都直接使用ApplicationContext而非底层的BeanFactory)

  • ApplicationContext有两个实现类


    Paste_Image.png
  • 属性的注入方式一(setter方法)

     <!-- 
        配置bean
        class:bean全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参构造器
        id:标识容器中的bean 必须唯一 
     -->
    <bean id="helloWorld" class="com.lxf.spring.bean.HelloWorld">
        <property name="name" value="zhangsan"></property>
    </bean>
    private String name;
    public void setName(String name)
    {
        System.out.println("setName:"+name);
        this.name = name;
    }
  • 属性的注入方式二(构造方法注入)
  • 通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用
  • 构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性;
  • 使用构造器注入属性值可以指定参数的位置和参数类型,以区分重载构造器
    <!-- 通过构造器的方式实现属性注入,可以指定参数的位置和参数类型,以区分重载构造器-->
    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        <!-- 通过构造器的方式实现属性注入,可以指定参数的位置和参数类型,以区分重载构造器 -->
    <bean id="car2" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>

以上两个bean,根据参数类型区分实例化的时候调用哪个构造器
Bean类Car

package com.lxf.spring.bean;
public class Car {
    private String cName;
    private double price;
    private int speed;  
    /**
     * 构造器,为汽车名字和汽车价格属性初始化值
     * @param cName
     * @param price
     */
    public Car(String cName, double price) {
        super();
        this.cName = cName;
        this.price = price;
    }

    /**
     * 重载构造器,为汽车名字和汽车速度初始化值
     * @param cName
     * @param speed
     */
    public Car(String cName, int speed) {
        super();
        this.cName = cName;
        this.speed = speed;
    }
    @Override
    public String toString() {
        return "Car [cName=" + cName + ", price=" + price + ", speed=" + speed + "]";
    }
}

测试:

        Car car = (Car) ctx.getBean("car1");
        System.out.println(car);
        Car car = (Car) ctx.getBean("car2");
        System.out.println(car);

输出:

Car [cName=汽车1, price=150000.0, speed=0]
Car [cName=汽车2, price=0.0, speed=120]
  • 属性值也可以使用value子结点进行那个配置(字面值)
    <!-- 使用value子结点进行配置value值,如果value值包含特殊字符,则使用<![CDATA[]]包裹起来  -->
     <bean id="car1-1" class="com.lxf.spring.bean.Car">
        <constructor-arg  index="0" type="java.lang.String">
            <value><![CDATA[<汽车1-1*>]]></value>
        </constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        Car car3 = (Car) ctx.getBean("car1-1");
        System.out.println(car3);

输出:

Car [cName=<汽车1-1*>, price=150000.0, speed=0]
  • bean之间的相关互引用

person类

package com.lxf.spring.bean;

public class Person {
    //姓名
    private String name;
    //年龄
    private int age;
    //该人所拥有的汽车
    private Car car;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
}

配置bean文件:

    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
    <bean id="persion" class="com.lxf.spring.bean.Person">
        <property name="name" value="lisi"></property>
        <property name="age" value="28"></property>
        <!-- 使用property的ref属性建立bean之间的引用关系 -->
        <!--  <property name="car" ref="car1"></property>-->
        <!-- 和上面等效 -->
        <!--  
        <property name="car">
            <ref bean="car1"/>
        </property>-->
    
        <!-- 使用内部bean,等效与以上,内部bean不能被外部访问 -->
        <property name="car">
            <bean  class="com.lxf.spring.bean.Car">
                <constructor-arg value="汽车1" index="0" type="java.lang.String"></constructor-arg>
                <constructor-arg value="150000" index="1" type="double"></constructor-arg>
            </bean>
        </property>

七.级联属性赋值

    <bean id="car" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>
    
    <bean id="persion2" class="com.lxf.spring.bean.Person">
        <constructor-arg value="wangwu"></constructor-arg>
        <constructor-arg value="30"></constructor-arg>
        <!-- 测试赋值null 
        <constructor-arg><null/></constructor-arg>-->
        <constructor-arg ref="car"></constructor-arg>
        <!-- 级联属性赋值 -->
        <property name="car.price"  value="12"></property>     
    </bean>

八.集合属性赋值

(1)List类型配置
Person类中有属性

    //该人所拥有的汽车集合
    private List<Car> cars;
    public List<Car> getCars() {
        return cars;
    }
    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

spring配置bean

    <!-- 通过构造器的方式实现属性注入,可以指定参数的位置和参数类型,以区分重载构造器-->
    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        <!-- 通过构造器的方式实现属性注入,可以指定参数的位置和参数类型,以区分重载构造器 -->
    <bean id="car2" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽车2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>
    <!--集合属性赋值  -->
    <bean id="person3" class="com.lxf.spring.bean.Person">
                <constructor-arg value="wangwu--1"></constructor-arg>
                <constructor-arg value="30"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="cars">
                    <list>
                        <ref bean="car1"/>
                        <ref bean="car2"/>
                    </list>
                </property>
    </bean>

(2)Map类型属性赋值
Person类中的属性

    //map类型的汽车
    private Map<String,Car> mapCars;
  public Map<String, Car> getMapCars() {
        return mapCars;
    }
    public void setMapCars(Map<String, Car> mapCars) {
        this.mapCars = mapCars;
    }

Spring配置

    <!-- 集合map属性赋值 -->
    <bean id="person4" class="com.lxf.spring.bean.Person" >
                <constructor-arg value="赵六"></constructor-arg>
                <constructor-arg value="33"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="mapCars">
                    <!-- 使用map节点以及map节点子节entry配置 map类型的属性 -->
                    <map>
                        <entry key="AA"  value-ref="car1"></entry>
                        <entry key="BB" value-ref="car2"></entry>
                    </map>
                </property>
    </bean>

(3)Properites属性赋值
DataSource实体类

package com.lxf.spring.bean;

import java.util.Properties;

public class DataSource {
    private Properties properties;

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "DataSource [properties=" + properties + "]";
    }
}

(4)使用Utility scheme定义集合
使用Utility schema里的集合标签定义独立的集合Bean,必须在Bean根元素里面定义;
这样定义的集合就可以被其他Bean共享
注意:需要导入util命名空间

col-5.jpg

Person实体类属性

    //该人所拥有的汽车集合
    private List<Car> cars;
    public List<Car> getCars() {
        return cars;
    }
    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

Spring配置

    <!-- 配置单独的集合bean,以供多个bean进行引用,注意:需要导入util命名空间 -->
    <util:list id="cars">
        <ref bean="car1"/>
        <ref bean="car2"/>
    </util:list>
    <bean id="person5" class="com.lxf.spring.bean.Person">
               <constructor-arg value="小青"></constructor-arg>
                <constructor-arg value="33"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="cars" ref="cars"></property>
    </bean>

从Spring2.5起可以使用P命名空间为bean属性赋值

Spring配置(注意:需导入 p 命名空间,导入方式类似util命名空间

  <!-- 通过 p 命名空间为bean属性赋值,需要先导入 p 命名空间,相对于传统方式更加简洁  -->
    <bean id="person6" class="com.lxf.spring.bean.Person"  p:age="29"  p:name="小网" p:cars-ref="cars"></bean>

九.bean的自动装配

使用autowire属性指定自动装配的方式,
(1) byName根据其他bean的id值和当前bean的setter风格属性名进行自动装配,如果没有匹配的bean则不装配
(2)byType根据其他bean的类型和当前bean属性的类型进行自动装配,若IOC容器中有一个以上的类型匹配的bean,则抛异常

    <bean id="address" class="com.lxf.spring.autowire.Address" p:city="Beijing" p:street="huilongguan">
    </bean>
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000"></bean>
    <!-- 使用autowire属性指定自动装配的方式,
           byName根据其他bean的id值和当前bean的setter风格属性名进行自动装配,如果没有匹配的bean则不装配
           byType根据其他bean的类型和当前bean属性的类型进行自动装配,若IOC容器中有一个以上的类型匹配的bean,则抛异常 -->
    <bean id="person" class="com.lxf.spring.autowire.Person"  p:name="zs" autowire="byType"></bean>

自动装配缺点:
(1)如果使用autowire属性自动装配,那么只要使用autowire的bean中引用其他bean属性的值都自动装载,不能指定某一个或几个属性使用自动装载;
(2)自动装载byType方式,如果一个Spring配置文件中有多个引用类型,则该方式会不知道装载哪个,会抛出异常;
所以在平时开发中尽量使用手动配置,整合其他框架的时候才使用自动装载

十.Bean之间的关系继承

  • Spring允许继承Bean的配置,称为父Bean和子Bean;
  • 子Bean从父Bean中继承配置,包括Bean的属性配置;
  • 子Bean也可以覆盖父Bean继承来的配置;
  • 父Bean可以作为配置模板,也可以作为Bean实例,可以设置<bean>的abstract属性为true,这样Spring将不会实例化这个Bean;
  • 并不是Bean元素里面的所有属性都会被继承,比如:autowire abstract等不会被继承
  • 也可以忽略父Bean的class属性,让子Bean指定自己的类,而共享相同属性配置,但此时abstract必须设为true
<?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">

    <!-- 属性abstract=true代表设置该bean为模板(不能被容器实例化),如果为模板则不需要配置 -->
    <bean id="address1" class="com.lxf.spring.autowire.Address"  p:city="Beijing" p:street="wudaokou" abstract="true"></bean>
    <!-- 使用parent属性继承其他bean配置 -->
    <bean id="address2"  p:street="dazhongsi"  parent="address1"></bean>
</beans>

十一.Bean之间的关系依赖

  • 使用depends-on属性设定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好;
  • 如果前置依赖多个Bean,则可以通过逗号,空格或的方式配置Bean的名称;
    <bean id="car" class="com.lxf.spring.autowire.Car"></bean>
    <!-- 使用depends-on属性设定Bean前置依赖的Bean,必须提前配置car的bean -->
    <bean id="person" class="com.lxf.spring.autowire.Person"  p:name="zs"  depends-on="car" p:car-ref="car"></bean>

十二.Bean的作用域

  • 默认单例: bean属性scope=singleton(默认值),IOC容器初始化的时创建bean实例,在整个容器的生命周期内只创建一个bean,单例的; 比如:
    Car类有无参构造器:
public class Car {
    private String brand;
    private double price;
    public Car()
    {
        System.out.println("Car is Constract...");
    }
}

Spring配置

    <!-- 
        使用bean的scope属性配置bean的作用域,scope值如下:
        singleton:默认值,IOC容器初始化的时创建bean实例,在整个容器的生命周期内只创建一个bean,单例的;
        -->
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000" scope="singleton"></bean>

main方法测试:

        //创建spring的IOC容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");   

输出:

Car is Constract...

证明容器在初始化的时候创建单例bean;
main方法测试:

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");   
        Car car1 = (Car)ctx.getBean("car");
        Car car2 = (Car)ctx.getBean("car");
        System.out.println(car1==car2);//输出true

以上car1=car2,证明每次调用bean的使用都是

  • 多例: bean属性scope=prototype(原型的),IOC容器初始化时不创建bean实例,而在每次请求时都创建一个新的Bean实例,并返回,比如:
    修改上面的Spring配置
    <!-- 
        使用bean的scope属性配置bean的作用域,scope值如下:
        singleton:默认值,IOC容器初始化的时创建bean实例,在整个容器的生命周期内只创建一个bean,单例的;
        prototype:原型的,IOC容器初始化时不创建bean实例,而在每次请求时都创建一个新的Bean实例,并返回;
        -->
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000" scope="prototype"></bean>

main方法测试:

        //创建spring的IOC容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml"); //不创建bean实例  
        Car car1 = (Car)ctx.getBean("car"); //创建一个bean实例
        Car car2 = (Car)ctx.getBean("car");//再次创建一个bean实例
        System.out.println(car1==car2);//返回false

十三.配置bean中使用外部属性文件

Paste_Image.png
  • 首先在Spring配置文件中打开context命名空间;
  • src下新建db.properties属性配置文件
userName=root
pass=123456
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
  • 在Spring配置文件(beans-properties.xml)中引用该配置文件中的属性变量
    <!-- 导入属性文件 classpath代表类路径 -->
    <context:property-placeholder location="classpath:db.properties"/> 
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 使用外部属性文件的属性 -->
        <property name="user" value="${userName}"></property>
        <property name="password" value="${pass}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
    </bean>
  • main方法测试:
        //1.创建spring的IOC容器对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-properties.xml");   
        //获取数据源
        DataSource dataSource = (DataSource)ctx.getBean("dataSource");
        //打印数据库连接
        System.out.println(dataSource.getConnection());

输出结果:

com.mchange.v2.c3p0.impl.NewProxyConnection@ee3d0a5
  • 以上测试需要加入:c3p0-0.9.1.2.jar 和 jdbc jar包,其中C3P0是一个开源的JDBC连接池

十四.Spring的spel表达式

<bean id="address" class="com.lxf.spring.spel.Address">
    <property name="city" value="#{'北京'}"></property>
    <property name="street" value="五道口"></property>
</bean>

<bean id="car" class="com.lxf.spring.spel.Car">
    <property name="brand" value="Audi"></property>
    <property name="price" value="500000"></property>
    <!-- 使用SPEL引用类的静态属性,并在SPEL表达式中进行计算 -->
    <property name="tyrePerimeter" value="#{T(java.lang.Math).PI*80}"></property>
</bean>

<bean id="person" class="com.lxf.spring.spel.Person">
    <property name="name" value="张三"></property>
    <!-- 使用spel来引用其他bean -->
    <property name="car" value="#{car}"></property>
    <!-- 使用spel来引用其他bean属性 -->
    <property name="city" value="#{address.city}"></property>
    <!-- 在spel中使用运算符 -->
    <property name="work" value="#{car.price>300000 ? '金领' : '白领'}"></property>
</bean>

十五.IOC容器中Bean的生命周期

Paste_Image.png

举例如下:
(1)Car实体类:

package com.lxf.spring.cycle;
/**
 * 汽车实体类
 * @author lxf
 */
public class Car {
    private String brand;
    private double price;
    //轮胎周长
    private double tyrePerimeter;
    /**
     * 无参数构造器
     */
    public Car()
    {
        System.out.println("Car is Constract...");
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        System.out.println("setBrand为品牌属性赋值");
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public double getTyrePerimeter() {
        return tyrePerimeter;
    }
    public void setTyrePerimeter(double tyrePerimeter) {
        this.tyrePerimeter = tyrePerimeter;
    }
    @Override
    public String toString() {
        return "Car [brand=" + brand + ", price=" + price + ", tyrePerimeter=" + tyrePerimeter + "]";
    }
    
    /**
     * Bean初始化方法,在IOC容器启动的时候调用
     */
    public void myInit()
    {
        System.out.println("创建了IOC容器:调用Bean的初始化方法");
    }
    /**
     * Bean销毁方法,在IOC容器关闭的时候调用
     */
    public void myDestroy()
    {
        System.out.println("调用Bean的销毁方法,IOC容器关闭了");
    }
}

(2)Spring配置文件(beans-cycle.xml)

    <!-- 
        bean的init-method属性调用对应类中的自定义方法myInit方法,在IOC容器创建的时候执行
        bean的destroy-method属性调用对应类中的自定义方法myDestroy,在IOC容器关闭的时候执行
     -->
    <bean id="car" class="com.lxf.spring.cycle.Car" 
            init-method="myInit" 
            destroy-method="myDestroy">
        <property name="brand" value="Audi"></property>
    </bean>

(3)main方法测试

        //创建spring的IOC容器对象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);
        //关闭IOC容器
        ctx.close();

输出结果如下:

Car is Constract...
setBrand为品牌属性赋值
创建了IOC容器:调用Bean的初始化方法
使用Bean: Car [brand=Audi, price=0.0, tyrePerimeter=0.0]
...
调用Bean的销毁方法,IOC容器关闭了

Paste_Image.png

Paste_Image.png

修改以上代码加入后置处理器bean的生命周期:
(1)新建后置处理器MyBeanPostProcessor

package com.lxf.spring.cycle;

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

public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     *  bean的init-method方法之后调用
     *  参数:
     *  bean: IOC传递进来的bean实例
     *  beanName:bean的id
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if("car".equals(beanName))
        {
            System.out.println("bean的init-method方法之后调用:"+bean);
        }      
        return bean;
    }
    /*
     * bean的init-method方法之前调用
     *  参数:
     *  bean: IOC传递进来的bean实例
     *  beanName:bean的id
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if("car".equals(beanName))
        {
            System.out.println("bean的init-method方法之前调用:"+bean);
            Car car = new Car();
            car.setBrand("大众");
            //对bean重新赋值
            bean = car;
        }       
        return bean;
    }
}

(2)Spring配置文件beans-cycle.xml修改

    <!-- 
        bean的init-method属性调用对应类中的自定义方法myInit方法,在IOC容器创建的时候执行
        bean的destroy-method属性调用对应类中的自定义方法myDestroy,在IOC容器关闭的时候执行
     -->
    <bean id="car" class="com.lxf.spring.cycle.Car" 
            init-method="myInit" 
            destroy-method="myDestroy">
        <property name="brand" value="Audi"></property>
    </bean>
    
    <!-- 配置bean的后置处理器: 不需要配置id, IOC容器自动识别 -->
    <!-- 
        实现BeanPostProcessor接口,并具体实现
        postProcessBeforeInitialization(Object bean, String beanName):init-method之前被调用
        postProcessAfterInitialization(Object bean, String beanName) :init-method之后被调用
        
        以上两个方法参数:
        bean: bean实例本身
        beanName: IOC容器配置的bean的id
        
        以上两个方法返回值:
        实际上返回给用户的那个Bean,注意:可以在以上两个方法中修改返回的Bean,甚至返回一个新Bean
     -->   
    <bean class="com.lxf.spring.cycle.MyBeanPostProcessor"></bean>

(3)main方法测试:

        //创建spring的IOC容器对象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);
        //关闭IOC容器
        ctx.close();

输出结果:

Car is Constract...
setBrand为品牌属性赋值
bean的init-method方法之前调用:Car [brand=Audi, price=0.0, tyrePerimeter=0.0]
Car is Constract...
setBrand为品牌属性赋值
创建了IOC容器:调用Bean的初始化方法
bean的init-method方法之后调用:Car [brand=大众, price=0.0, tyrePerimeter=0.0]
使用Bean: Car [brand=大众, price=0.0, tyrePerimeter=0.0]
调用Bean的销毁方法,IOC容器关闭了

十六.使用工厂方法创建Bean

Paste_Image.png

举例:
(1)新建静态工厂方法类:StaticFactory

package com.lxf.spring.factory;

import java.util.HashMap;
import java.util.Map;

/**
 * 静态工厂方法:直接调用某一个类的静态方法可以返回Bean实例
 * @author lxf
 *
 */
public class StaticFactory {
    private static Map<String,Car> cars = new HashMap<String,Car>();
    static{
        cars.put("audi", new Car("audi"));
        cars.put("ford", new Car("ford"));
    }
    /**
     * 静态工厂方法返回对应Bean实例
     * @param name
     * @return
     */
    public static Car getCar(String name)
    {
        return cars.get(name);
    }
}

(2)Spring配置文件beans-factory.xml

    <!-- 通过静态方法工厂配置bean, 注意不是配置静态方法实例,而是bean实例 -->
    <!-- 
        class属性:指向静态工厂方法的全类名
        factory-method属性:指向静态工厂方法名
        constructor-arg标签:如果工厂方法需要传入参数,则使用constructor-arg标签来配置
     -->
    <bean id="car1" 
        class="com.lxf.spring.factory.StaticFactory"
        factory-method="getCar">
        <constructor-arg value="audi" /></bean>

(3)man方法测试:

        //创建spring的IOC容器对象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");   
        Car car1 = (Car)ctx.getBean("car1");
        System.out.println("使用Bean: "+car1);

输出:

使用Bean: Car [brand=audi, price=0.0, tyrePerimeter=0.0]
Paste_Image.png

举例:
(1)新建实例工厂方法类:InstanceFactory

package com.lxf.spring.factory;
import java.util.HashMap;
import java.util.Map;
/**
 * 实例工厂方法:实例工厂的方法,即先需要创建工厂本身实例,
 *                             再调用工厂的实例方法来返回bean的实例.
 * @author lxf
 */
public class InstanceFactory {
    private Map<String,Car> cars = null;
    public InstanceFactory()
    {
        cars = new HashMap<String,Car>();
        cars.put("audi", new Car("audi"));
        cars.put("ford", new Car("ford"));
    }
    public Car getCar(String name)
    {
        return cars.get(name);
    }
}

(2)Spring配置文件(beans-factory.xml)

     <!-- 先创建工厂实例本身 -->
     <bean id="factory"  class="com.lxf.spring.factory.InstanceFactory"></bean>
     
     <!-- 在通过实例工厂方法配置bean实例 -->
         <!-- 
        factory-bean属性:指向实例工厂方法的bean
        factory-method属性:指向实例厂方法名
        constructor-arg标签:如果工厂方法需要传入参数,则使用constructor-arg标签来配置
     -->
     <bean id="car2"  factory-bean="factory" factory-method="getCar">
        <constructor-arg value="ford"></constructor-arg>
     </bean>

(3)mani方法测试:

        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");           
        Car car2 = (Car)ctx.getBean("car2");
        System.out.println("使用Bean: "+car2);

输出:

使用Bean: Car [brand=ford, price=0.0, tyrePerimeter=0.0]

使用FactoryBean的方式配置Bean

  • 其实就是自己定义一个类,实现Spring的FactoryBean接口,实现该接口的三个方法即可;(其中有一个方法返回Bean实例)
package com.lxf.spring.factorybean;
import org.springframework.beans.factory.FactoryBean;
/**
 * 通过FactoryBean的方式配置Bean,需要实现FactoryBean接口
 * @author lxf
 * @param <T>
 */
public class CarFactoryBean<T> implements FactoryBean<T> {
    private String brand;
    public void setBrand(String brand) {
        this.brand = brand;
    }
    @Override
    /**
     * 返回bean对象
     */
    public T getObject() throws Exception {
        // TODO Auto-generated method stub
        return (T) new Car(brand,5000000);
    }
    @Override
    /**
     * 返回Bean类型
     */
    public Class<?> getObjectType() {
        // TODO Auto-generated method stub
        return Car.class;
    }
    @Override
    /**
     * 是否是单例
     */
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return true;
    }
}
  • 在Spring配置文件中beans-facotrybean.xml中配置自己定义的类
    <!-- 
        通过FactoryBean类配置Bean的实例
        class:指向FactoryBean的全类名
        property:配置FactoryBean的属性
        但实际返回的实例确实FacboryBean的getObject()方法返回的实例
     -->
    <bean id="car" class="com.lxf.spring.factorybean.CarFactoryBean">
        <property name="brand" value="BMW"></property>
    </bean>
  • main方法测试:
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factorybean.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);

输出:

使用Bean: Car [brand=BMW, price=5000000.0, tyrePerimeter=0.0]

使用注解的方式配置bean

注意:需要引入spring-aop-4.3.9.RELEASE.jar

Paste_Image.png

Paste_Image.png
Paste_Image.png

Spring配置文件beans-annotation.xml配置

        <!-- 
          指定Spring IOC容器的扫描包,IOC容器在启动的时候,
          会扫表base-package指定的当前包以及子包下所有包含指定注解的bean,并将其实例
          可通过resource-pattern="repository/*.class"指定扫描资源
        
        <context:component-scan 
                base-package="com.spring.beans.annotation"
                resource-pattern="repository/*.class">
         </context:component-scan> -->
        
       <!-- 排除指定注解下的目标类 
       <context:component-scan 
                base-package="com.spring.beans.annotation" >
                <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
         </context:component-scan>--> 
         
       <!-- 包含指定注解下的目标类 context:include-filter要和use-default-filters属性配合使用(默认扫描所有相关注解)
       <context:component-scan 
                base-package="com.spring.beans.annotation" use-default-filters="false" >
                <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
         </context:component-scan>--> 
         
        <!-- 排除指定类bean的加载 
       <context:component-scan 
                base-package="com.spring.beans.annotation" >
                <context:exclude-filter type="assignable" expression="com.spring.beans.annotation.repository.UserRepositoryImpl"/>
         </context:component-scan>--> 
         
       <!-- 包含指定类bean的加载 context:include-filter需要和use-default-filter属性配合使用--> 
       <context:component-scan 
                base-package="com.spring.beans.annotation"  use-default-filters="false">
                <context:include-filter type="assignable" expression="com.spring.beans.annotation.repository.UserRepositoryImpl"/>
         </context:component-scan>

以上目录结构:

src
 |__com.spring.beans.annotation
 |   |__Main.java
 |   |__TestObject.java
 |   |__service
 |       |__UserService.java
 |   |__respository
 |       |__UserRespository.java
 |   |__controller
 |       |__UserController.java
 |__beans.annotation.xml

UserService.java类带有@Service注解

package com.spring.beans.annotation.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    public void add()
    {
        System.out.println("UserService add...");
    }
}

其他User*.java分别带有如下注解

@Repository("userRepository")
@Controller
@Repository("userRepository"

Bean之间属性互相依赖的解决方案:

Paste_Image.png
Paste_Image.png
package com.spring.beans.annotation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import com.spring.beans.annotation.TestObject;
import com.spring.beans.annotation.repository.UserRepository;
import com.spring.beans.annotation.service.UserService;

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    
    @Autowired
    //如果IOC容器中存在多个UserRepository接口的实现类,
    //可以指定具体装在哪个实现类,注意首字母小写(spring规定)
    @Qualifier("jdbcRespository")
    private UserRepository userRepository;
    
   //如果IOC容器中没有testObj这个bean,则使用@Autowired(required=false,否则异常
    @Autowired(required=false)    
    private TestObject testObj;
    public void execute()
    {
        System.out.println("UserControler execute...");
        userService.add();
    }
}

十七.Spring4.* 泛型的依赖注入

在实际开发中,往往我们可以将一些结构职能相似的类抽象出一定的泛型超类。这样相应结构中的类之间的依赖关系我们可以通过泛型超类来建立,而其他的子类只要继承了它们,就能够建立相应的依赖关系:

参考链接:https://my.oschina.net/happyBKs/blog/482508

代码演示点击

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容