1、Spring——IOC、DI

  • 官网: https://spring.io
  • 文档:https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/core.html#spring-core
  • 下载地址: https://repo.spring.io/libs-release-local/org/springframework/spring/

第1章、Spring概述

1、Spring简介

  • Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器(框架)

  • Spring是一个IOC(DI)和AOP容器框架

  • Spring的优良特性:

    1. 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
    2. 依赖注入:DI——Dependency Injection,控制反转(IOC)最经典的实现
    3. 面向切面编程:Aspect Oriented Programming——AOP
    4. 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
    5. 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
    6. 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)
  • 下载的文件名spring-framework-x.x.x.RELEASE-dist.zip

  • 在这里插入图片描述

  • libs目录下的jar包说明

  • 在这里插入图片描述

2、Spring体系结构

在这里插入图片描述

  • Test:Spring的单元测试模块

    • spring-test-4.0.0.RELEASE
      
  • Core Container:核心容器(IOC),包括4部分:

    • spring-core:提供了框架的基本组成部分,包括 IOC 和依赖注入功能。

    • spring-beans:提供 BeanFactory,

    • spring-context:模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能

    • spring-expression:提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等

    • spring-beans-4.0.0.RELEASE
      spring-core-4.0.0.RELEASE
      spring-context-4.0.0.RELEASE
      spring-expression-4.0.0.RELEASE
      
  • AOP+Aspects:面向切面编程模块

    • spring-aop-4.0.0.RELEASE
      spring-aspects-4.0.0.RELEASE
      
  • Data Access:数据访问模块

    • spring-jdbc-4.0.0.RELEASE
      spring-orm(Object Relation Mapping)-4.0.0.RELEASE
      spring-ox(xml)m-4.0.0.RELEASE
      spring-jms-4.0.0.RELEASE
      spring-tx-4.0.0.RELEASE(事务)
      
  • Web:Spring开发Web引用模块

    • spring-websocket-4.0.0.RELEASE
      spring-web-4.0.0.RELEASE ------原生web相关(servlet)
      spring-webmvc-4.0.0.RELEASE------开发web项目(web)
      spring-webmvc-portlet-4.0.0.RELEASE—开发web应用的组件集成
      
    • 使用maven导入依赖

    • <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.0.RELEASE</version>
      </dependency>
      

第2章、入门案例

1、案例代码

  1. 导包

    核心容器
    spring-beans-4.0.0.RELEASE.jar
    spring-context-4.0.0.RELEASE.jar
    spring-core-4.0.0.RELEASE.jar
    spring-expression-4.0.0.RELEASE.jar
    commons-logging-1.1.3.jar
    Spring运行的时候依赖一个日志包;没有就报错;
    
  2. 新建一个Person类,添加set、get方法

    public class Person {
    	private String lastName;
    	private Integer age;
    	private String gender;
    	private String email;
        //get  set方法
    }
    
  3. 在classpath下创建一个Spring配置文件ApplicationContext.xml,注册bean。

    使用bean标签注册一个Person对象,Spring会自动创建这个Person对象
    class:写要注册的组件的全类名
    id:这个对象的唯一标识
    使用property标签为Person对象的属性值,name:指定属性名;value:指定属性值

    <?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">
    	
    	<!-- 注册一个Person对象,Spring会自动创建这个Person对象 -->
    	<!-- 
    		一个Bean标签可以注册一个组件(对象、类)  
    		class:写要注册的组件的全类名
    		id:这个对象的唯一标示;
    	 -->
    	<bean id="person01" class="com.zb.bean.Person">
    		<!--使用property标签为Person对象的属性赋值  
    			name="lastName":指定属性名
    			value="张三":为这个属性赋值
    		-->
    		<property name="lastName" value="张三"></property>
    		<property name="age" value="18"></property>
    		<property name="email" value="zhangsan@zb.com"></property>
    		<property name="gender" value=""></property>
    	</bean>
    </beans>
    
  4. 通过Spring的IOC容器创建Person类实例

    @Test
    public void test() {
           //ApplicationContext:代表ioc容器
           //ClassPathXmlApplicationContext:当前应用的xml配置文件在 ClassPath下
           //根据spring的配置文件得到ioc容器对象
           ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
           
           //容器帮我们创建好对象了
           Person bean = (Person) ioc.getBean("person01");
           
           System.out.println(bean);//Person [lastName=张三, age=18, gender=男, email=zhangsan@zb.com]
    }
    

2、几个细节

  • 1、关于类路径

    • src:源码文件夹开始的路径,称为类路径的开始,所有源码文件夹里面的东西都会被合并放在类路径里面;

    • Java文件的类路径为:/bin/

    • JavaWeb文件的类路径为:/WEB-INF/classes/

  • 2、ApplicationContext(IOC容器的接口)

    • 在这里插入图片描述

    • new ClassPathXMlApplicationContext(“ioc.xml”);IOC容器的配置文件在类路径下;

    • FileSystemXmlApplicationContext(“F://ioc.xml”);IOC容器的配置文件在磁盘路径下;

  • 3、Person对象是什么时候创建好了呢?

    • 容器中对象的创建在容器创建完成的时候就已经创建好了
  • 4、同一个组件在ioc容器中是单实例的;容器启动完成都已经创建准备好的。

  • 5、容器中如果没有这个组件,获取组件?报异常

    • org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘person03’ is defined
  • 6、IOC容器用property标签创建这个组件对象的时候,会利用setter方法为其属性赋值,注意属性名是set方法后的那串的首字母小写

第3章、IOC和Bean的配置

1、IOC和DI

  • 1、IOC(Inversion of Control):控制反转

    • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。

    • 其本意就是将原本在程序中手动创建对象的控制权,交给Spring来管理

    • 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。

    • 反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

    • 使用 IOC 目的:为了耦合度降低

    • IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

    • IOC的底层原理:xml 解析、工厂模式、反射

  • 2、DI(Dependency Injection):依赖注入,就是注入属性

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

    • 1、在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
    • 2、Spring提供了IOC容器的两种实现方式
      • 1、BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
      • 2、ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。加载配置文件时候就会把在配置文件对象进行创建
  • 4、ApplicationContext的主要实现类

    • 1、ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件

    • 2、FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

    • 在这里插入图片描述

    • 3、在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。

  • 5、ConfigurableApplicationContext

    • 1、是ApplicationContext的子接口,包含一些扩展方法
    • 2、refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。
  • 6、WebApplicationContext

    • 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作

2、通过类型获取bean

  • 从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的

  • 从IOC容器中获取bean的实例

    • Object getBean (String beanName):根据bean对象在容器中的名称来获取

    • T getBean(Class requiredType):按照指定的类型去寻找bean对象

    • T getBean(String name,@Nullable Class requiredType):根据bean的类型 + ID 去寻找.

    public void test02(){
    	ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    	//方法一:
    	//通过bean的id获取bean的实例,需要强转
    	Person bean1 = (Person)ioc.getBean("person01");
    	System.out.println(bean1);
    	//方法二:
    	//根据bean的类型从IOC容器中获取bean的实例,但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常
    	Person bean2 = ioc.getBean(Person.class);
    	System.out.println(bean2);
    	//方法三:
    	//通过bean的id和bean的类型获取bean的实例
    	Person bean3 = ioc.getBean("person01",Person.class);
    	System.out.println(bean3);
    }
    

3、简单的Spring配置

【1】别名配置

  • alias 设置别名 , 为bean设置别名 , 可以设置多个别名
  <!--设置别名:在获取Bean的时候可以使用别名获取-->
  <alias name="userT" alias="userNew"/>

【2】Bean配置

  <!--bean就是java对象,由Spring创建和管理-->
  <!--
     id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
     如果配置id,又配置了name,那么name是别名
     name可以设置多个别名,可以用逗号,分号,空格隔开
     如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
   
  class是bean的全限定名=包名+类名
  -->
  <bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
     <property name="name" value="Spring"/>
  </bean>

【3】import配置

  • 团队的合作通过import来实现
<import resource="{path}/beans.xml"/>

4、给bean的属性赋值

【1】赋值的途径

【1.1】通过bean的setXxx()方法赋值
  • 需要借助set方法,使用propetry标签。(保证bean对象中有无参构造方法)
  • 在这里插入图片描述
<bean id="book" class="com.atguigu.spring5.Book">
 <!--使用 property 完成属性注入
 name:类里面属性名称
 value:向属性注入的值
 -->
 <property name="bname" value="易筋经"></property>
 <property name="bauthor" value="达摩老祖"></property>
</bean>
【1.2】通过bean的构造器赋值
  • 使用constructor-arg标签,则调用构造器进行属性注入,需要借助有参构造

  • 构造器赋值——只写value属性,会默认按顺序寻找构造方法进行匹配

    •  <bean id="book" class="com.atguigu.spring.bean.Book" >
           <constructor-arg value= "10010"/>
           <constructor-arg value= "Book01"/>
           <constructor-arg value= "Author01"/>
           <constructor-arg value= "20.2"/>
      </bean>
      
  • 通过索引值指定参数位置

    • <bean id="book" class="com.atguigu.spring.bean.Book" >
          <constructor-arg value= "10010" index ="0"/>
          <constructor-arg value= "Book01" index ="1"/>
          <constructor-arg value= "Author01" index ="2"/>
          <constructor-arg value= "20.2" index ="3"/>
      </bean>
      
  • 通过类型不同区分重载的构造器

    • <bean id="book" class="com.atguigu.spring.bean.Book" >
            <constructor-arg value= "10010" index ="0" type="java.lang.Integer" />
            <constructor-arg value= "Book01" index ="1" type="java.lang.String" />
            <constructor-arg value= "Author01" index ="2" type="java.lang.String" />
            <constructor-arg value= "20.2" index ="3" type="java.lang.Double" />
      </bean>
      
【1.3】给bean的级联属性赋值
  • propetry标签中的name标签,可以使用级联属性,修改属性的属性,但是原来属性的值会被修改
<bean id="action" class="com.atguigu.spring.ref.Action">
    <property name="service" ref="service"/>
    <!-- 设置级联属性(了解) -->
    <property name="service.dao.dataSource" value="DBCP"/>
</bean>
【1.4】p名称空间
  • 为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息

  • Spring从2.5版本开始引入了一个新的p命名空间,可以通过元素属性的方式配置Bean的属性

  • 使用p命名空间后,基于XML的配置方式将进一步简化。

  • 本质上是调用的set方法 导入头文件约束

  • xmln:p="http://www.springframework.org/schema/p"
    
    
<bean id="studentSuper" 
	class="com.atguigu.helloworld.bean.Student"
	p:studentId="2002" p:stuName="Jerry2016" p:age="18">
</bean>
【1.5】c命名空间注入(了解)
  • c(构造: Constructor)命名空间注入,本质上使用的是构造器注入

  • 导入头文件约束

    • xmlns:c=“http://www.springframework.org/schema/c”
      
<bean id="person05" class="com.xiao.bean.Person"
      c:lastName="zhangsan" c:age="30" c:email="zhangsan@qq.com" c:gender="1">
</bean>

【2】可以使用的值

新建一个Person类:

public class Person {
	
	private String lastName = "张三";
	private Integer age;
	private String gender;
	private String email;
	private Car car;
	private List<Object> objs;
	private Map<String,Object> maps;
	private Properties properties;
}
【2.1】字面量
  • 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定

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

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

<bean id="user" class="com.zb.spring5.User">
    <!--属性值包含特殊符号
     方法一: 把<>进行转义 &lt; &gt;
     方法二: 把带特殊符号内容写到 CDATA
    -->
    <!--方法一-->
    <property name="name" value="&lt;&lt;南京&gt;&gt;"></property>
    <!--方法二-->
    <!--        <property name="name">-->
    <!--            <value><![CDATA[<<南京>>]]></value>-->
    <!--        </property>-->
</bean>
【2.2】null值
  • 如果有属性给了初始值,想注入为null,则在property内部需要使用null标签:
<bean id="person01" class="com.zb.bean.Person">
	<property name="lastName">
		<null/>
	</property>
</bean>
【2.3】外部已声明的bean
  • 可以使用ref引用外部的bean
<!-- 先注册一个Car对象 -->
<bean id="car01" class="com.zb.bean.Car">
	<property name="carName" value="宝马"></property>
	<property name="color" value="绿色"></property>
	<property name="price" value="30000"></property>
</bean>

<bean id="person01" class="com.zb.bean.Person">
	<!-- ref:代表引用外面的一个值 引入其他bean -->
	<property name="car" ref="car01"></property>
</bean>

注意:ref是严格的引用,通过容器拿到的Car实例就是Person实例中的Car属性

ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext2.xml");
Person bean = (Person)ioc.getBean("person01");
Car car01 = (Car)ioc.getBean("car01");
System.out.println(car01 == bean.getCar());//true
【2.4】内部bean
  • 内部bean声明直接包含在<property>或<constructor-arg>元素里,不需要设置任何id或name属性
  • 内部bean只能内部使用,外面获取不到
<bean id="person02" class="com.zb.bean.Person">
	<property name="lastName" value="张三"></property>
	<property name="car">
		<!-- 对象我们可以使用bean标签创建 car = new Car() 引入内部bean-->
		<bean id="car02" class="com.zb.bean.Car">
			<property name="carName" value="自行车"></property>
		</bean> 
	</property>
</bean>

【3】集合属性

【3.1】数组和List
  • 配置java.util.List类型的属性,需要指定标签,在标签里包含一些元素。这些标签可以通过指定简单的常量值,通过指定对其他Bean的引用。通过指定内置bean定义。通过指定空元素。甚至可以内嵌其他集合
  • 数组的定义和List一样,都使用元素
  • 配置java.util.Set需要使用标签,定义的方法与List一样。
<bean id="shop" class="com.atguigu.spring.bean.Shop" >
    <property name= "categoryList">
        <!-- 以字面量为值的List集合 -->
        <list>
            <value> 历史</value >
            <value> 军事</value >
        </list>
    </property>
    <property name= "bookList">
        <!-- 以bean的引用为值的List集合 -->
        <list>
            <ref bean= "book01"/>
            <ref bean= "book02"/>
        </list>
    </property>
</bean >
【3.2】Map
  • <map>标签里可以使用多个<entry>作为子标签。每个条目包含一个键和一个值
  • 必须在<key>标签里定义键
  • 因为键和值的类型没有限制,所以可以自由地为它们指定<value>、<ref>、<bean>或<null/>元素
  • 可以将Map的键和值作为的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。
<bean id="person04" class="com.zb.bean.Person">
	<property name="maps">
		<!-- 相当于new LinkedHashMap<>(); -->
		<map>
			<entry key="key01" value="value01"></entry>
			<entry key="key02">
				<value>value02</value><!-- 两种方式相同 -->
			</entry>
			<entry key="key03" value="18"></entry>
			<entry key="key04" value-ref="book01"></entry>
			<entry key="key05">
				<bean class="com.zb.bean.Car">
					<property name="carName" value="宝马"></property>
				</bean>
			</entry>
			<entry key="key06" value-ref="car01">
			</entry>
		</map>
	</property>
</bean>
【3.3】Properties
  • 使用定义java.util.Properties,该标签使用多个作为子标签。每个标签必须定义key属性
<bean class="com.atguigu.spring.bean.DataSource" id="dataSource">
	<property name="properties">
		<props>
			<prop key="userName">root</prop>
			<prop key="password">root</prop>
			<prop key="url">jdbc:mysql:///test</prop>
			<prop key="driverClass">com.mysql.jdbc.Driver</prop>
		</props>
	</property>
</bean>
【3.4】集合类型的bean
<!--创建多个 course 对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="Spring5 框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
    <property name="cname" value="MyBatis 框架"></property>
</bean>
<!--注入 list 集合类型,值是对象-->
<property name="courseList">
    <list>
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </list>
</property>

util名称空间

  • 如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用。

  • 配置集合类型的bean需要引入util名称空间

头文件约束:xmlns:util="http://www.springframework.org/schema/util"

<!-- util名称空间创建集合类型的bean;方便别人引用 -->
<!-- 相当于new LinkedHashMap<>() -->
<util:map id="myMap">
	<entry key="key01" value="value01"></entry>
	<entry key="key02">
		<value>value02</value><!-- 两种方式相同 -->
	</entry>
	<entry key="key03" value="18"></entry>
	<entry key="key04" value-ref="book01"></entry>
	<entry key="key05">
		<bean class="com.zb.bean.Car">
			<property name="carName" value="宝马"></property>
		</bean>
	</entry>
</util:map>
<!-- 提取 map 集合类型属性注入使用-->
<bean id="person04" class="com.zb.bean.Person">
    <property name="maps" ref="myMap"></property>
</bean>

第4章、通过工厂创建bean

1、静态工厂(了解)

  • 调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不用关心创建对象的细节
  • 声明通过静态方法创建的bean需要在bean的class属性里指定静态工厂类的全类名,同时在factory-method属性里指定工厂方法的名称。最后使用<constrctor-arg>元素为该方法传递方法参数
public class AirPlaneStaticFactory {
	//AirPlaneStaticFactory.getAirPlane()
	public static AirPlane getAirPlane(String jzName){
		System.out.println("AirPlaneStaticFactory...正在为你造飞机");
		AirPlane airPlane = new AirPlane();
		airPlane.setFdj("太行");
		airPlane.setFjsName("lfy");
		airPlane.setJzName(jzName);
		airPlane.setPersonNum(300);
		airPlane.setYc("198.98m");
		return airPlane;
	}
}
<!-- 1、静态工厂(不需要创建工厂本身)
	factory-method="getAirPlane":指定哪个方法是工厂方法
	class:指定静态工厂全类名
	constructor-arg:可以为方法传参
-->
<bean id="airPlane01" class="com.zb.factory.AirPlaneStaticFactory"
	factory-method="getAirPlane">
	<!-- 可以为方法指定参数 -->
	<constructor-arg value="李四"></constructor-arg>
</bean>

2、实例工厂(了解)

  • 实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节。
  • 实现方式:
    • 1、配置工厂类实例的bean
    • 2、在factory-method属性里指定该工厂方法的名称
    • 3、使用 construtor-arg 元素为工厂方法传递方法参数
public class AirPlaneInstanceFactory {
	// new AirPlaneInstanceFactory().getAirPlane();
	public  AirPlane getAirPlane(String jzName){
		System.out.println("AirPlaneInstanceFactory...正在造飞机");
		AirPlane airPlane = new AirPlane();
		airPlane.setFdj("太行");
		airPlane.setFjsName("lfy");
		airPlane.setJzName(jzName);
		airPlane.setPersonNum(300);
		airPlane.setYc("198.98m");
		return airPlane;
	}
}
 <!-- 实例工厂使用 -->
 <bean id="airPlaneInstanceFactory" class="com.zb.factory.AirPlaneInstanceFactory">
 </bean>
 <!-- factory-bean:指定当前对象创建使用哪个工厂
	1、先配置出实例工厂对象
	2、配置我们要创建的AirPlane使用哪个工厂创建
		1)、factory-bean:指定使用哪个工厂实例
		2)、factory-method:使用哪个工厂方法
 -->
 <bean id="airPlane02" class="com.zb.bean.AirPlane"
 	factory-bean="airPlaneInstanceFactory" factory-method="getAirPlane">
 	<constructor-arg value="王五"></constructor-arg>
 </bean>

3、FactoryBean

  • Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean

  • 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象

  • 工厂bean必须实现org.springframework.beans.factory.FactoryBean接口

  • 在这里插入图片描述

/**
 * 实现了FactoryBean接口的类是Spring可以认识的工厂类;
 * Spring会自动调用工厂方法创建实例
 * @author ZB
 *
 */
public class MyFactoryBeanImple implements FactoryBean<Book> {
	/**
	 * getObject:工厂方法;
	 * 		返回创建的对象
	 */
	@Override
	public Book getObject() throws Exception {
		System.out.println("MyFactoryBeanImple..帮你创建对象..");
		Book book = new Book();
		book.setBookName(UUID.randomUUID().toString());
		return book;
	}

	/**
	 * 返回创建的对象的类型;
	 * Spring会自动调用这个方法来确认创建的对象是什么类型
	 */
	@Override
	public Class<?> getObjectType() {
		// TODO Auto-generated method stub
		return Book.class;
	}
	
	/**
	 * isSingleton:是否是单例
	 * false:不是单例
	 * true:是单例
	 */
	@Override
	public boolean isSingleton() {
		// TODO Auto-generated method stub
		return false;
	}
}

@Test
public void test01() {
    //1 加载 spring 配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("myFactoryBeanImple", Book.class);
    System.out.println(book);//Book [bookName=87404f3e-3b55-476d-9107-2fb95c1bc667, author=null]
}

在配置文件中注册工厂对象

<!-- FactoryBean★(是Spring规定的一个接口);
		只要是这个接口的实现类,Spring都认为是一个工厂;
	1、ioc容器启动的时候不会创建实例 
	2、FactoryBean;获取的时候的才创建对象,而且为多实例类型
	-->
<bean id="myFactoryBeanImple" class="com.zb.factory.MyFactoryBeanImple" >
</bean>

注意:IOC容器启动时不会创建实例,使用getBean时才会创建

第5章、bean的高级配置

1、配置信息的继承

【1】背景

  • 查看下面两个Employee的配置,其中dept属性是重复的
<bean id="dept" class="com.atguigu.parent.bean.Department">
	<property name="deptId" value="100"/>
	<property name="deptName" value="IT"/>
</bean>

<bean id="emp01" class="com.atguigu.parent.bean.Employee">
	<property name="empId" value="1001"/>
	<property name="empName" value="Tom"/>
	<property name="age" value="20"/>

	<!-- 重复的属性值 -->	
	<property name="detp" ref="dept"/>
</bean>

<bean id="emp02" class="com.atguigu.parent.bean.Employee">
	<property name="empId" value="1002"/>
	<property name="empName" value="Jerry"/>
	<property name="age" value="25"/>

	<!-- 重复的属性值 -->
	<property name="detp" ref="dept"/>
</bean>

【2】配置信息的继承

  • 指定parent属性为要重用的bean的id值,不写的属性就沿用,也可以重写定义属性
  • 还可以指定属性abstract=“true”,这样的bean只能被用来继承信息,不能获取实例。否则会报异常 BeanIsAbstractException
  • Spring允许继承bean的配置,被继承的bean称为父bean。继承这个父bean的bean称为子bean
  • 子bean从父bean中继承配置,包括bean的属性配置
  • 子bean也可以覆盖从父bean继承过来的配置
<!-- abstract="true":这个bean的配置是一个抽象的,不能获取他的实例,只能被别人用来继承 -->
<bean id="person08" class="com.zb.bean.Person" abstract="true">
	<property name="lastName" value="张三"></property>
	<property name="age" value="18"></property>
	<property name="gender" value=""></property>
	<property name="email" value="zhangsan@atguigu.com"></property>
</bean>

<!--parent:指定当前bean的配置信息继承于哪个,继承后可以省略公共属性值的配置  -->
<bean id="person09" parent="person08">
	<property name="lastName" value="李四"></property>
</bean>

【3】补充说明

  • 父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>的abstract 属性为true,这样Spring将不会实例化这个bean
  • 如果一个bean的class属性没有指定,则必须是抽象bean
  • 并不是<bean>元素里的所有属性都会被继承。比如:autowire,abstract等
  • 也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时abstract必须设为true。

2、bean之间的依赖

  • 多个bean的默认创建顺序,是按照配置顺序创建的
<bean id="person" class="com.zb.bean.Person"></bean>
<bean id="car" class="com.zb.bean.Car"></bean>
<bean id="book" class="com.zb.bean.Book"></bean>
  • 有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Person对象的时候必须创建Car和Book对象
<!-- 创建Person对象的时候必须创建Car和Book对象 -->
<bean id="person" class="com.zb.bean.Person" depends-on="book,car"></bean>
<bean id="car" class="com.zb.bean.Car"></bean>
<bean id="book" class="com.zb.bean.Book"></bean>

3、bean的作用域(重点)

  • 在 Spring 里面,默认情况下,bean 是单实例对象

在这里插入图片描述

  • 可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的

  • <bean id="" class="" scope="作用域"/>
    
  • singleton: 单例模式,是默认模式。在容器启动完成之前就已经创建好对象保存在容器中了

  • prototype :多实例的。容器启动不会去创建实例bean,每次调用getBean()的时候会产生一个新的对象

  • singleton 和 prototype 区别:

    • 1、singleton 单实例,prototype 多实例

    • 2、设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象

    • 3、设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象

类别说明
singleton在 SpringlOC容器中仅存在一个 Bean 实例,Bean以单实例的方式存在
prototype每次调用getBean()时都会返回一个新的实例
request每次HTTP请求都会创建一个新的Bean ,该作用域仅适用于基于Web的Spring ApplicationContext环境
session同一个HTTP Session共享一个 Bean ,不同的HTTP Session使用不同的 Bean。该作用域仅适用于WebApplicationContext环境

4、bean的生命周期(重点)

  • 生命周期:从对象创建到对象销毁的过程

  • bean 生命周期:

  • 1、Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务

  • 2、Spring IOC容器对bean的生命周期进行管理的过程:

    • [1]通过构造器或工厂方法创建bean实例
    • [2]为bean的属性设置值和对其他bean的引用
    • [3]调用bean的初始化方法
    • [4]bean可以使用了
    • [5]当容器关闭时,调用bean的销毁方法
  • 3、在配置bean时,通过init-methoddestroy-method 属性为bean指定初始化和销毁方法

public class Orders { //无参数构造
    public Orders() {
        System.out.println("第一步 执行无参数构造创建 bean 实例");
    }
    private String oname;
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步 调用 set 方法设置属性值");
    }
    //创建执行的初始化的方法
    public void initMethod() {
        System.out.println("第三步 执行初始化的方法");
    }
    //创建执行的销毁的方法
    public void destroyMethod() {
        System.out.println("第五步 执行销毁的方法");
    }
}

//在配置文件中设置
<bean id="book01" class="com.zb.bean.Book" destroy-method="myDestory" init-method="myInit"></bean>
<bean id="orders" class="com.zb.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>

测试:

@Test
public void testBean3() {
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("bean1.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四步 获取创建 bean 实例对象");
    System.out.println(orders);
    //手动让 bean 实例销毁
    context.close();//ApplicationContext对象没有该方法
}
单实例bean:容器启动的时候就会创建好,容器关闭也会销毁创建的bean
	(容器启动)构造器 —> 调用set方法 —> 初始化方法 —> (容器关闭)销毁方法
	
多实例bean:获取的时候才去创建
	获取bean(构造器--->初始化方法)--->容器关闭不会调用bean的销毁方法
  • 4、bean的后置处理器

    • [1]bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
    • [2]bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性
    • [3] bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
      • postProcessBeforeInitialization(Object, String)
      • postProcessAfterInitialization(Object, String)
  • 5、添加bean后置处理器后bean的生命周期,bean 生命周期有七步

    • - [1]通过构造器或工厂方法**创建bean实例**(无参数构造)
      - [2]为bean的**属性设置值**和对其他bean的引用(调用 set 方法)
      - [3]将bean实例传递给bean后置处理器的**postProcessBeforeInitialization()**方法
      - [4]调用bean的**初始化**方法(需要进行配置初始化的方法)
      - [5]将bean实例传递给bean后置处理器的**postProcessAfterInitialization()**方法
      - [6]bean可以使用了(对象获取到了)
      - [7]当容器关闭时调用bean的**销毁方法**(需要进行配置销毁的方法)
      
  • 后置处理器的实现案例

/**
 * 1)、编写后置处理器的实现类
 * 2)、将后置处理器注册在配置文件中(感觉类似于javaweb过滤器)
 * @author zb
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor{

	/**
	 * 初始化之前调用
	 * Object bean, 传递过来的,将要初始化的bean
	 * String beanName:bean在xml中配置的id
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("postProcessBeforeInitialization...【"+beanName+"】bean将要调用初始化方法了....这个bean是这样:【"+bean+"】");
		//返回传入的bean
		return bean;
	}
	
	/**
	 * postProcessAfterInitialization:
	 * 		初始化方法之后调用
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("postProcessAfterInitialization...【"+beanName+"】bean初始化方法调用完了...AfterInitialization..");
		//初始化之后返回的bean;返回的是什么,容器中保存的就是什么
		return bean;
	}
}
//在配置文件中注册:会给在这个容器中的所有bean添加后置处理器
<bean id="beanPostProcessor" class="com.zb.bean.MyBeanPostProcessor">
</bean>

<bean id="orders" class="com.zb.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
</bean>

5、引用外部属性文件(重点)

  • 当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置

  • Spring管理连接池(以配置C3P0的数据库连接池为例)

【1】直接配置

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="user" value="root"></property>
	<property name="password" value="root"></property>
	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
	<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>

【2】创建properties属性文件(重点)

  • 1、首先新建一个数据库连接池的配置文件db.properties:

    jdbc.username=root
    jdbc.password=root
    jdbc.jdbcUrl=jdbc:mysql://localhost:3306/test
    jdbc.driverClass=com.mysql.jdbc.Driver
    
  • 2、引入context命名空间:

    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/context
    
  • 3、指定properties属性文件的位置

    <!-- 指定properties属性文件的位置 -->
    <!-- classpath:xxx 表示属性文件位于类路径下 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    
  • 4、用$从properties属性文件中引入属性值:

    <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"
            xmlns:util="http://www.springframework.org/schema/util"
            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/util 
                                http://www.springframework.org/schema/util/spring-util.xsd 
                                http://www.springframework.org/schema/context 
                                http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:property-placeholder location="classpath:dbconfig.properties"/>
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
            <property name="driverClass" value="${jdbc.driverClass}"></property>
        </bean>
    </beans>
    

6、基于XML的自动装配(不推荐)

  • 1、自动装配的概念

    • [1]手动装配:以value或ref的方式明确指定属性值都是手动装配。

    • [2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

  • ②装配模式

    • [1]根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

    • [2]根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同

    • [3]通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

  • 3、选用建议

    • 相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现
  • 设置<bean/>元素的:autowire属性

    • autowire属性:让Spring按照一定的规则方式自己去找合适的对象,并完成DI注入操作

      在这里插入图片描述

  • 注意:

    • 如果按照byName自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名

    • 如果安装byType注入,如果Spring容器中同一个类型有多个实例,报 bean不是唯一类型错误;

  • 1、根据属性名称自动注入

    • 在这里插入图片描述
  • 2、根据属性类型自动注入

    • 在这里插入图片描述

第6章、SpEL表达式(了解)

1、简介:

  • Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图
  • 和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯

2、基本语法:

  • SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式
	<bean id="car01" class="com.zb.bean.Car">
		<property name="carName" value="宝马"></property>
	</bean>
	
	<bean id="person04" class="com.zb.bean.Person">
		<!-- 进行运算符操作 -->
		<property name="salary" value="#{20*12}"></property>
		<!-- 引用其他bean的属性值作为自己某个属性的值-->
		<property name="lastName" value="#{car01.carName}"></property>
		<!-- 引用其他的bean -->
		<property name="car" value="#{car01}"></property>
		<!-- 
		      调用静态方法: UUID.randomUUID().toString();
		      #{T(全类名).静态方法名(1,2)}
		-->
		<!-- 调用静态方法 -->
		<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(1,3)}"></property>
		<!--   调用非静态方法;  对象.方法名  -->
		<property name="gender" value="#{car01.getCarName()}"></property>
	</bean>

第7章、通过注解配置bean(重点)

1、概述

相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式

2、使用注解标识组件

  • 普通组件:@Component

    • 标识一个受Spring IOC容器管理的组件
  • 持久化层组件:@Respository

    • 标识一个受Spring IOC容器管理的Dao持久化层组件
  • 业务逻辑层组件:@Service

    • 标识一个受Spring IOC容器管理的Service业务逻辑层组件
  • 表述层控制器组件:@Controller

    • 标识一个受Spring IOC容器管理的表述层控制器组件
  • ⑤组件命名规则

    • [1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
    • [2]使用组件注解的value属性指定bean的id
  • 注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。

注解标识组件的使用步骤:

  1. 导入AOP包的依赖

  2. 开启组件扫描
    给bean上添加注解之后,还需要告诉Spring,自动扫描加了注解的组件,依赖context名称空间

    <!--开启组件扫描
     1 如果扫描多个包,多个包使用逗号隔开
     2 扫描包上层目录
    -->
    <context:component-scan base-package="com.zb"></context:component-scan>
    
  3. 创建类,在类上面添加创建对象注解

    //组件的id。默认为_组件类名首字母小写
    @Controller("bookServlethh")(value="bookServlethh")//等价于<bean id="bookServlethh" class=".."/>
    public class BookServlet{}
    //在注解里面 value 属性值可以省略不写,
    

总结:使用注解将组件快速的加入到容器中需要几步:

  • 1)给需要添加的组件上标四个注解的任何一个
  • 2)告诉Spring自动扫描加了注解的组件;依赖context名称空间
  • 3)一定要导入aop包,才支持加注解模式

3、扫描组件

  • 指定要扫描的包

  • JAR包

    • 必须在原有JAR包组合的基础上再导入一个:spring-aop-4.0.0.RELEASE.jar
  • [1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类

  • [2]当需要扫描多个包时可以使用逗号分隔

  • [3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类

  • component-scan下可以拥有若干个include-filter和exclude-filter子节点

    <!-- 
    	context:component-scan:自动组件扫描 
     	base-package:指定扫描的基础包
     -->
    <context:component-scan base-package="com.atguigu" ></context:component-scan>
    
  • <context:exclude-filter>子节点表示要排除在外的目标类

    <!-- 
    	使用context:exclude-filter指定扫描包时不包含的类
    	annotation:按照注解进行排除,expression属性中指定要排除的注解的全类名
    	assignable:排除某个具体的类,expression属性中指定要排除的类的全类名
    	aspectj:后来aspectj表达式(了解)
    	custom:自定义一个TypeFilter;自己写代码决定哪些使用(了解)
    	regex:使用正则表达式(了解)
     -->
    <context:component-scan base-package="com.zb" >
    	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    	<context:exclude-filter type="assignable" expression="com.zb.dao.BookDao"></context:exclude-filter>
    </context:component-scan>
    
    
  • 使用context:include-filter指定扫描包时要包含的类,只扫描进入哪些组件

    <!-- 
    	使用context:include-filter指定扫描包时要包含的类,只扫描进入哪些组件,默认都是全部扫描进来
    	注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了
     -->
    <context:component-scan base-package="com.zb" use-default-filters="false">
    	<!-- 指定只扫描哪些组件,表示值扫描Controller注解的类 -->
    	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>
    
类别示例说明
annotationcom.atguigu.XxxAnnotation过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。
assignablecom.atguigu.BaseXxx过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。
aspectjcom.atguigu.*Service+所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。
regexcom.atguigu.anno.*所有com.atguigu.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。
customcom.atguigu.XxxTypeFilter使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口

4、组件装配

  • ①需求

    • Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
  • ②实现依据

    • 在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了**@Autowired**、@Resource或@Inject注解的属性。
  • ③@Autowired注解

    • [1]根据类型实现自动装配。
    • [2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解
    • [3]默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常
    • [4]若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
    • [5]默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称
    • [6]@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配
    • [7]@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean
    • [8]@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键
  • ④@Resource

    • @Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
  • ⑤@Inject

    • @Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。

直接在成员上添加@Autowired就可以完成自动装配。

@Service
public class BookService {
    //使用@Autowired完成成员BookDao的自动装配
    @Autowired
    private BookDao bookDao;

    public void read() {
        this.bookDao.readBook();
    }
}

Autowired的执行流程:

  1. 首先按照类型去容器中找对应的组件,如果找到一个就赋值,找不到就抛异常;

  2. 如果有多个类型匹配时,会使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功,找不到就报错。

  3. 结合注解@Qualifer,指定一个id:在自动按照类型注入的基础之上,再按照指定的bean的id去查找

  4. @Autowired标注的属性如果找不到就会报错,可以指定required属性,找不到就自动装配null

    @Controller
    public class BookServlet {
    	/**
    	 * 按照类型找,找到一个就赋值,找到多个按照变量名作为id继续匹配
    	 * @Qualifier:指定一个名作为id,让spring别使用变量名作为id,如果变量名和该id都可以匹配到,则按照该id匹配
    	 * 找不到的话:@Autowired(required=false),找不到赋值为null
    	 */
    	@Qualifier("bookServiceExt")
    	@Autowired(required=false)
    	private BookService bookService1;
    }
    

注解加在方法上

/**
 * 在方法上@Autowired,ioc容器加载时就会执行该方法,同时也会为形参自动装配
 * 
 */
@Autowired(required=false)
public void hahaha(Person person,@Qualifier("bookServiceExt")BookService bookService1){
	System.out.println("Spring运行了这个方法"+person+bookService1);
}

@Autowired、@Resource、@Inject都可以作为注入注解

@Autowired:最强大;Spring的注解
①扩展性差,依赖Spring容器框架

@Resource:j2ee;java的标准【jdk标准】
①扩展性强:切换成另一个容器框架,其还会可以使用
【@Inject:在EJB环境下使用()】

@Resource:可以根据类型注入,可以根据名称注入

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

@Value:注入普通类型属性

@Value(value = "abc")
private String name;

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配

  • @Qualifier不能单独使用。

  • 1、配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

    <bean id="dog1" class="com.kuang.pojo.Dog"/>
    <bean id="dog2" class="com.kuang.pojo.Dog"/>
    <bean id="cat1" class="com.kuang.pojo.Cat"/>
    <bean id="cat2" class="com.kuang.pojo.Cat"/>
    
  • 2、没有加Qualifier测试,直接报错

  • 3、在属性上添加Qualifier注解

    @Autowired
    @Qualifier(value = "cat2")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
    
    //测试,成功输出!
    

【4.1】@Autowired与@Resource异同

  • 1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上
  • 2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
  • 3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配
  • 它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

5、完全注解开发

  • 1、创建配置类,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
	public class SpringConfig {
}
  • 2、编写测试类
@Test
public void testService2() {
	//加载配置类
	ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
	UserService userService = context.getBean("userService", UserService.class);
	System.out.println(userService);
	userService.add();
}

第8章、Spring的单元测试(了解)

 1、导入:Spring单元测试包。spring-test-4.0.0.RELEASE.jar
 2、@ContextConfiguration(locations = ""),指定Spring的配置文件位置
 3、@RunWith(),指定用哪种驱动进行单元测试,默认junit
          @RunWith(SpringJUnit4ClassRunner.class)
          使用Spring的单元测试模块来执行@Test注解的测试方法;
          之前@Test由Junit执行
 好处:
     不需要ioc.getBean()获取组件;直接AutoWired组件,Spring自动装配
/**
 * 使用Spring的单元测试:
 * 1、@ContextConfiguration(locations="")指定Spring配置文件的位置
 * 2、@RunWith(),指定用哪种驱动进行单元测试
 */
@ContextConfiguration(locations="classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {

	ApplicationContext ioc = null;
	@Autowired
	BookService bookService;
	@Autowired
	BookServiceExt bookServiceExt;
	@Test
	public void test() {
		System.out.println(bookService);
		System.out.println(bookServiceExt);
	}
}

第9章、泛型的依赖注入

BaseDao

public abstract class BaseDao<T> {
	public abstract void save();
}

BookDao

@Repository
public class BookDao extends BaseDao<Book> {
	@Override
	public void save() {
		System.out.println("向数据库中保存了book");
	}
}

UserDao

@Repository
public class UserDao extends BaseDao<User> {
	@Override
	public void save() {
		System.out.println("向数据库中保存了User");
	}
}

BaseService

public class BaseService<T> {
	
	@Autowired
	private BaseDao<T> baseDao;
	
	public void save(){
		System.out.println("自动注入的dao:"+baseDao);
		baseDao.save();
	}
}

BookService

@Service
public class BookService extends BaseService<Book> {
}

UserService

@Service
public class UserService extends BaseService<User>{
}

测试:

public class IOCTest {
	@Test
	public void test01(){
		ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
		BookService bookService = ioc.getBean(BookService.class);
		UserService userService = ioc.getBean(UserService.class);
		bookService.save();//向数据库中保存了book
		userService.save();//向数据库中保存了User
	
		System.out.println(bookService.getClass().getGenericSuperclass());//com.zb.service.BaseService<com.zb.bean.Book>
	}
}

在这里插入图片描述

第10章、整合多个配置文件

  • Spring允许通过<import>将多个配置文件引入到一个文件中,进行配置文件的集成。这样在启动Spring容器时,仅需要指定这个合并好的配置文件就可以

  • import元素的resource属性支持Spring的标准的路径资源

地址前缀示例对应资源类型
classpath:classpath:spring-mvc.xml从类路怪下加载资源,classpath:和 classpath:/是等价的
file:file:/conf/security/spring-shiro.xml从文件系统目录中装载资源,可采用绝对或相对路怪
http://http://www.atguigu.com/resource/beans.xml从WEB服务器中加载资源
ftp://ftp://www.atguigu.com/resource/beans.xml从FTP服务器中加载资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值