一、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(二)
后置处理器===处理属性