1) IOC(Inversion of Control):反转控制
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
总结: IOC 就是一种反转控制的思想, 而DI是对IOC的一种具体实现。
前提: Spring中有IOC思想, IOC思想必须基于 IOC容器来完成, 而IOC容器在最底层实质上就是一个对象工厂.
2)Spring提供了IOC容器的两种实现方式
① BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
② ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
1) ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
2) FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
3) 在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
1) 是ApplicationContext的子接口,包含一些扩展方法
2) refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。
1) 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
3、通过类型获取bean
HelloWorld helloWorld = cxt.getBean(HelloWorld. class);
HelloWorld helloWorld = cxt.getBean(“helloWorld”,HelloWorld. class);
4、给bean的属性赋值
1) 依赖注入的方式:通过bean的setXxx()方法赋值
Hello World中使用的就是这种方式,注意属性名称,实际是通过set方法,所以属性名称在SET方法中首字母要大写。
2). 通过bean的构造器赋值:Spring自动匹配合适的构造器
-1)通过bean的构造器赋值
<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 >
-2)通过索引值指定参数位置
<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 >
-3)通过类型不同区分重载的构造器
<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 >
3)、p名称空间
为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。Spring 从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean 的属性。使用p命名空间后,基于XML的配置方式将进一步简化。
<bean id="studentSuper" class="com.atguigu.helloworld.bean.Student" p:studentId="2002" p:stuName="Jerry2016" p:age="18" />
5、可以使用的值
1) 字面量
1) 可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
2) 基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
3) 若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
2)null值
<bean class="com.atguigu.spring.bean.Book" id="bookNull" > <property name= "bookId" value ="2000"/> <property name= "bookName"> <null/> </property> <property name= "author" value ="nullAuthor"/> <property name= "price" value ="50"/> </bean >
3)给bean的级联属性赋值
<bean id="action" class="com.atguigu.spring.ref.Action"> <property name="service" ref="service"/> <!-- 设置级联属性(了解) --> <property name="service.dao.dataSource" value="DBCP"/> </bean>
4)外部已声明的bean、引用其他的bean
<bean id="s5" class="com.atguigu.spring.di.Student"> <property name="id" value="10055"></property> <property name="name" value="张三三"></property> <property name="age" value="23"></property> <property name="sex" value="男"></property> <property name="teacher" ref="teacher"></property> //引用外部的bean teacher <property name="teacher.tname" value="小红"></property> </bean> <bean id="teacher" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="10000"></property> <property name="tname" value="小明"></property> </bean>
5)内部bean
当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property>或<constructor-arg>元素里。内部bean不能使用在任何其他地方
<bean id="s6" class="com.atguigu.spring.di.Student"> <property name="id" value="10066"></property> <property name="name" value="崔八"></property> <property name="age" value="18"></property> <property name="sex" value="男"></property> <property name="teacher"> <bean id="tt" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="2222"></property> <property name="tname" value="admin"></property> </bean> </property> </bean>
6、集合属性
在Spring中可以通过一组内置的XML标签来配置集合属性,例如:<list>,<set>或<map>。
1)数组和List
配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素。这些标签 可以通过<value>指定简单的常量值,通过<ref>指定对其他Bean的引用。通过<bean>指定内置bean定义。通过<null/>指定空元素。甚至可以内嵌其他集合。
数组的定义和List一样,都使用<list>元素。
配置java.util.Set需要使用<set>标签,定义的方法与List一样。
<bean id="t1" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="10001"></property> <property name="tname" value="佟刚"></property> <property name="cls"> <list> <value>A</value> <value>B</value> <value>C</value> </list> </property> </bean> <bean id="t2" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="10002"></property> <property name="tname" value="婷姐"></property> <property name="students"> <list> <ref bean="s1"/> <ref bean="s2"/> <ref bean="s3"/> </list> </property> </bean>
2) Map
Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签。每个条目包含一个键和一个值。
必须在<key>标签里定义键。
因为键和值的类型没有限制,所以可以自由地为它们指定<value>、<ref>、<bean>或<null/>元素。
可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。
<bean id="t3" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="10003"></property> <property name="tname" value="admin"></property> <property name="bossMap"> <map> <entry> <key> <value>10001</value> </key> <value>佟老师</value> </entry> <entry> <key> <value>10002</value> </key> <value>陈老师</value> </entry> </map> </property> </bean>
3)集合类型的bean
如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要 将集合bean的配置拿到外面,供其他bean引用。
配置集合类型的bean需要引入util名称空间
<bean id="t4" class="com.atguigu.spring.di.Teacher"> <property name="tid" value="10004"></property> <property name="tname" value="root"></property> <property name="students" ref="students"></property> </bean> <util:list id="students"> <ref bean="s4"/> <ref bean="s5"/> <ref bean="s6"/> </util:list> <util:map id="map"> <entry> <key> <value>1</value> </key> <value>张三</value> </entry> </util:map>
7、FactoryBean
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
工厂:隐藏类的创建过程,获取对象过程通过工厂去获取,创建对象的过程在工厂中体现,而不是在获取类的地方体现。
<?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"> <bean id="factory" class="com.atguigu.spring.factorybean.MyFactory"></bean> </beans>
package com.atguigu.spring.factorybean; import org.springframework.beans.factory.FactoryBean; public class MyFactory implements FactoryBean<Car> { @Override public Car getObject() throws Exception { Car car = new Car(); car.setBrand("奥迪"); car.setPrice(200000.0); return car; } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return Car.class; } @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; } }
package com.atguigu.spring.factorybean; public class Car { private String brand; private Double price; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } @Override public String toString() { return "Car [brand=" + brand + ", price=" + price + "]"; } }
package com.atguigu.spring.factorybean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("factory-bean.xml"); Object object = ac.getBean("factory"); System.out.println(object); } }
8、bean的作用域
在Spring中,可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。
9、bean的生命周期
1) Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
2) Spring IOC容器对bean的生命周期进行管理的过程:
① 创建:通过构造器或工厂方法创建bean实例
② 依赖注入:为bean的属性设置值和对其他bean的引用
③ 初始化:调用bean的初始化方法
④ 使用:bean可以使用了
⑤ 销毁:当容器关闭时,调用bean的销毁方法
3) 在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法
4) bean的后置处理器
① bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
② bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。
其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
③ bean后置处理器需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:(实现接口的类需要在配置文件中配置class属性,<bean class="com.atguigu.ioc.life.AfterHandler"></bean>)
●postProcessBeforeInitialization(Object, String)
●postProcessAfterInitialization(Object, String)
5) 添加bean后置处理器后bean的生命周期
①通过构造器或工厂方法创建bean实例
②为bean的属性设置值和对其他bean的引用
③将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
④调用bean的初始化方法
⑤将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
⑥bean可以使用了
⑦当容器关闭时调用bean的销毁方法
10、 引用外部文件
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
1)直接配置
<!-- 直接配置 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="root"/> <property name="password" value="root"/> <property name="jdbcUrl" value="jdbc:mysql:///test"/> <property name="driverClass" value="com.mysql.jdbc.Driver"/> </bean>
2)使用外部的属性文件
-1. 创建properties属性文件
# k = v
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456
-2)引入context命名空间
-3)指定properties属性文件的位置,从properties属性文件中引入属性值
<context:property-placeholder location="db.properties"/> <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
11、自动装配(自动赋值)
1) 手动装配:以value或ref的方式明确指定属性值都是手动装配。
2) 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
自动装配的属性必须是非字面量的属性,也就是需要用 ref 的属性。
<!-- autowire:根据某种策略自动为非字面量属性赋值 autowire="byName|byType" byName:通过属性名和spring容器中bean的id进行比较,若一致则可直接赋值 byType:通过spring容器中bean的类型,为兼容性的属性赋值 在使用byType的过程中,要求spring容器中只能有一个能为属性赋值的bean 选用建议: 当设置autowire属性,会作用于该bean中所有的非字面量属性,因此谁都不用 -->
1) 根据类型自动装配byType:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
2) 根据名称自动装配byName:必须将目标bean的名称和属性名设置的完全相同
3) 通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用
12、通过注解配置bean
相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。
必须在原有JAR包组合的基础上再导入一个:spring-aop-4.0.0.RELEASE.jar
-1) 普通组件:@Component
标识一个受Spring IOC容器管理的组件
-2) 持久化层组件:@Repository
标识一个受Spring IOC容器管理的持久化层组件,(DAO层,数据访问层,实现数据持久化)
-3) 业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件,(Service,处理业务逻辑)
-4) 表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件,(例如web中,Servlet容器,用来处理请求和响应)
-5) 组件命名规则
①默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
②使用组件注解的value属性指定bean的id
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将 @Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以 @Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确 当前的组件扮演的角色。
3)扫描组件
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
-1) 指定被扫描的package
<!--
<context:component-scan>:扫描组件,对设置的包下面的类进行扫描,会将加上注解的类作为spring的组件进行加载
组件:指spring中管理的bean
作为spring的组件进行加载:会自动在spring的配置文件中生成相对应的bean,这些bean的id会以类的首字母小写为值
<context:include-filter>:在设定的包结构下,再次通过注解或类型具体包含到某个或某几个类
注意:在使用包含时,一定要设置use-default-filters="false",将默认的过滤(即扫描包下所有的类)关闭
<context:exclude-filter>:在设定的包结构下,再次通过注解或类型排除某个或某几个类
注意:在使用排除时,一定要设置use-default-filters="true",将默认的过滤(即扫描包下所有的类)打开
切记:一个<context:component-scan>中可以出现多个include,也可以同时出现多个exclude,但是两个不能同时出现
-->
<context:component-scan base-package="com.atguigu.component"/>
-2) 详细说明
①base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
②当需要扫描多个包时可以使用逗号分隔。
③如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
<context:component-scan base-package="com.atguigu.component" resource-pattern="autowire/*.class"/>
④包含与排除
●<context:include-filter>子节点表示要包含的目标类
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false, 禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
●<context:exclude-filter>子节点表示要排除在外的目标类
●component-scan下可以拥有若干个include-filter和exclude-filter子节点
<context:component-scan base-package="com.atguigu.ioc.userMod" use-default-filters="true"> <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> --> <!-- <context:include-filter type="assignable" expression="com.atguigu.ioc.userMod.service.UserServiceImpl"/> --> <!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> --> <!-- <context:exclude-filter type="assignable" expression="com.atguigu.ioc.userMod.dao.UserDaoImpl"/> --> </context:component-scan>
●过滤表达式
类别 |
示例 |
说明 |
annotation |
com.atguigu.XxxAnnotation |
过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。 |
assignable |
com.atguigu.BaseXxx |
过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 |
aspectj |
com.atguigu.*Service+ |
所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 |
regex |
com.atguigu.anno.* |
所有com.atguigu.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 |
custom |
com.atguigu.XxxTypeFilter |
使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org. springframework. core. type. filter. TypeFilter 接口 |
组件装配
1) 需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到 Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
2) 实现依据
在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。
3) @Autowired注解
①根据类型实现自动装配。
②构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解
③默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。
④若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
⑤默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。
@Autowired @Qualifier(value="userDaoMybatisImpl") public void setUserDao(UserDao userDao) { this.userDao = userDao; }
⑥@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
⑦@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
⑧@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
4) @Resource
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
5) @Inject
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。
/** * 基于注解的组件化管理: * @Component,@Controller(控制层),@Service(业务层),@Repository(持久层) * 以上四个注解功能完全相同,不过在实际开发中,要在实现不同功能的类上加上相应的注解 * 完成组件化管理的过程: * 1、在需要被spring管理的类上加上相应注解 * 2、在配置文件中通过<context:component-scan>对所设置的包结构进行扫描,就会将加上注解的类,作为spring的组件进行加载 * 组件:指spring中管理的bean 作为spring的组件进行加载:会自动在spring的配置文件中生成相对应的bean,这些bean的id会以类的首字母小写为值; 也可以通过@Controller("beanId")为自动生成的bean指定id * 自动装配:在需要赋值的非字面量属性上,加上@Autowired,就可以在spring容器中,通过不同的方式匹配到相对应的bean * @Autowired装配时,会默认使用byType的方式,此时要求spring容器中只有一个能够为其赋值 * 当byType实现不了装配时,会自动切换到byName,此时要求spring容器中,有一个bean的id和属性名一致 * 若自动装配时,匹配到多个能够复制的bean,可使用@Qualifier(value="beanId")指定使用的bean * @Autowired和@Qualifier(value="beanId")可以一起作用域一个带形参的方法上,此时,@Qualifier(value="beanId") * 所指定的bean作用于形参 * */