一、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(二)

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

