一、Spring概述
1、Spring概述
①Spring是一个开源框架②Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能。同样的功能,在EJB中要通过繁琐的配置和复杂的代码才能够实现,而在Spring中却非常的优雅和简洁。
③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目前的版本
⑥Spring模块
2、安装Spring插件
①插件包:springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite.zip②操作步骤:Help----》Install new software---->add---->arichive--->选择对应的插件版本--->选中带SpringIDE的直接下一步就行
3、搭建Spring运行时环境
①加入JAR包[1]Spring自身JAR包:spring-framework-4.0.0.RELEASE\libs目录下
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
[2]commons-logging-1.1.1.jar(日志包)
②根据需要创建Spring配置文件
4、HelloWorld
①目标:使用Spring创建对象,为属性赋值②创建Student类
③创建Spring配置文件(applicationContext.xml)
<!-- 使用bean元素定义一个由IOC容器创建的对象 -->
<!-- class属性指定用于创建bean的全类名 -->
<!-- id属性指定用于引用bean实例的标识 -->
<bean id="studen" class="com.atguigu.helloworld.bean.Student">
<!-- 使用property子元素为bean的属性赋值 -->
<property name="studentId" value="1001"/>
<property name="stuName" value="Tom2015"/>
<property name="age" value="20"/>
</bean>
④测试:通过Spring的IOC容器创建Student类实例
//1.创建IOC容器对象
ApplicationContext iocContainer = new ClassPathXmlApplicationContext("helloworld.xml");
//2.根据id值获取bean实例对象
Student student = (Student) iocContainer.getBean("student");
//3.打印bean
System.out.println(student);
⑤验证:Spring在创建IOC容器对象时,就已经完成了bean的创建和属性的赋值。
在student的构造方法中打印一句话,在还没利用IOC容器获取bean实例对象的时候,构造器方法、以及属性的赋值方法就已经执行。
二、 IOC容器和Bean的配置
1、IOC和DI
①IOC(Inversion of Control):反转控制。在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
②DI(Dependency Injection):依赖注入。
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
③IOC容器在Spring中的实现
[1]在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
[2]Spring提供了IOC容器的两种实现方式
(1)BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
(2)ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
④ApplicationContext的主要实现类
[1]ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
[2]FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
[3]在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
⑤ConfigurableApplicationContext
[1]是ApplicationContext的子接口,包含一些扩展方法
[2]refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。
⑥WebApplicationContext:专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作
2通过类型获取bean
从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。HelloWorld helloWorld = ioc.getBean(HelloWorld. class);
3给bean的属性赋值
3.1赋值的途经
①通过bean的setXxx()方法赋值HelloWorld中使用的就是这种方式
②通过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 >
●通过 索引值指定参数位置
<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 >
③给bean的 级联属性赋值
<bean id="action" class="com.atguigu.spring.ref.Action">
<property name="service" ref="service"/>
<!-- 设置级联属性(了解) -->
<property name="service.dao.dataSource" value="DBCP"/>
</bean>
④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" />
3.2可以使用的值
①字面量[1]可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
[2]基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
[3]若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
②null值
<bean class="com.atguigu.spring.bean.Book" id="bookNull" >
<property name= "bookId" value ="2000"/>
<property name= "bookName">
<null/>
</property>
<property name= "author" value ="null"/>
<property name= "price" value ="50"/>
</bean >
③外部已声明的bean
<bean id="shop" class="com.atguigu.spring.bean.Shop" >
<property name= "book" ref ="book"/>
</bean >
④内部bean
当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property>或<constructor-arg>元素里,不需要设置任何id或name属性,内部bean不能使用在任何其他地方
<bean id="shop2" class="com.atguigu.spring.bean.Shop" >
<property name= "book">
<bean class= "com.atguigu.spring.bean.Book" >
<property name= "bookId" value ="1000"/>
<property name= "bookName" value="innerBook" />
<property name= "author" value="innerAuthor" />
<property name= "price" value ="50"/>
</bean>
</property>
</bean >
3.3集合属性
在Spring中可以通过一组内置的XML标签来配置集合属性,例如:<list>,<set>或<map>。
①数组和List
配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素。这些标签可以通过<value>指定简单的常量值,通过<ref>指定对其他Bean的引用。
通过<bean>指定内置bean定义。通过<null/>指定空元素。甚至可以内嵌其他集合。
数组的定义和List一样,都使用<list>元素。
配置java.util.Set需要使用<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 >
②Map
Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签。每个条目包含一个键和一个值。
必须在<key>标签里定义键。
因为键和值的类型没有限制,所以可以*地为它们指定<value>、<ref>、<bean>或<null/>元素。
可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。
<bean id="cup" class="com.atguigu.spring.bean.Cup">
<property name="bookMap">
<map>
<entry>
<key>
<value>bookKey01</value>
</key>
<ref bean="book01"/>
</entry>
<entry>
<key>
<value>bookKey02</value>
</key>
<ref bean="book02"/>
</entry>
</map>
</property>
</bean>
③Properties
使用<props>定义java.util.Properties,该标签使用多个<prop>作为子标签。每个<prop>标签必须定义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>
④集合类型的bean
如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用。
配置集合类型的bean需要引入util名称空间
<util:list id="bookList">
<ref bean="book01"/>
<ref bean="book02"/>
<ref bean="book03"/>
<ref bean="book04"/>
<ref bean="book05"/>
</util:list>
<util:list id="categoryList">
<value>编程</value>
<value>极客</value>
<value>相声</value>
<value>评书</value>
</util:list>
4通过工厂创建bean
4.1静态工厂
调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不用关心创建对象的细节。
声明通过静态方法创建的bean需要在bean的class属性里指定静态工厂类的全类名,同时在factory-method属性里指定工厂方法的名称。最后使用<constrctor-arg>元素为该方法传递方法参数。
<!-- 注册静态工厂,以及那个方法是用来创建实例的 -->
<bean id="book06" class="com.atguigu.bean.BookStaticFactory" factory-method="getBook" >
<!--方法调用时传参,要动态的在获取bean的时候传参需要制定scope="prototype" -->
<constructor-arg value="b2"></constructor-arg>
</bean>
4.2实例工厂
实现方式
①配置工厂类实例的bean
②在factory-method属性里指定该工厂方法的名称
③使用 construtor-arg 元素为工厂方法传递方法参数
<!--1)、先要在ioc容器注册这个工厂 -->
<bean id="instanceFactory" class="com.atguigu.bean.BookInstanceFactory"></bean>
<!--2)、配置这本书是从工厂方法中获取的
factory-bean:指定实例工厂的bean
-->
<bean id="book07" class="com.atguigu.spring.bean.Book"
factory-bean="instanceFactory"
factory-method="getBook" scope="prototype">
<!--还是通过constructor-arg传参 scope="prototype"才能接受动态传参 -->
<constructor-arg value="b3"></constructor-arg>
</bean>
4.3FactoryBean
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
<!--Spring中有一个接口FactoryBean,他是用来做bean工厂的。
1)、编写实现类,
2)、注册factorybean
-->
<bean id="book08" class="com.atguigu.bean.BookImplSpring"></bean>
package com.atguigu.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* BookImplSpring是一个spring认识的工厂
* <T>指定这个工厂使用来生成哪个对象的
*
*/
public class BookImplSpring implements FactoryBean<Book>{
/**
* getObject():是用来返回工厂创建的实例对象的
*/
@Override
public Book getObject() throws Exception {
System.out.println("调用getObject().....");
return new Book();
}
/**
* 返回的对象的类型。
* 可以使用这个类型在ioc容器中查找对象
*/
@Override
public Class<?> getObjectType() {
return Book.class;
}
/**
*false 默认不是单例
*true 单例
*但是并不代表就要在ioc容器启动的时候就创建对象。
*1、启动ioc容器的时候不会调用getObject方法创建一个对象放在ioc容器中
*2、还是在获取这个对象的时候才调用getObject创建出对象
*3、如果返回true代表只有这个对象一个实例
*/
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
5、bean的高级配置
5.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>
②配置信息的继承
<!-- 以emp01作为父bean,继承后可以省略公共属性值的配置 -->
<bean id="emp02" parent="emp01">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
</bean>
Spring允许继承bean的配置,被继承的bean称为父bean。继承这个父bean的bean称为子bean;
子bean从父bean中继承配置,包括bean的属性配置;
子bean也可以覆盖从父bean继承过来的配置
③补充说明
父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>的abstract 属性为true,这样Spring将不会实例化这个bean
如果一个bean的class属性没有指定,则必须是抽象bean
并不是<bean>元素里的所有属性都会被继承。比如:autowire,abstract等。
也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时abstract必须设为true。
5.2bean之间的依赖
有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Employee对象的时候必须创建Department。这里需要注意的是依赖关系不等于引用关系,Employee即使依赖Department也可以不引用它。
<bean id="emp03" class="com.atguigu.parent.bean.Employee" depends-on="dept">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
</bean>
5.3bean的作用域
在Spring中,可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:
所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。
5.4bean的生命周期
①Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。②Spring IOC容器对bean的生命周期进行管理的过程:
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]调用bean的初始化方法
[4]bean可以使用了
[5]当容器关闭时,调用bean的销毁方法
③在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法
<bean id="emp04" class="com.atguigu.parent.bean.Employee"
init-method="init" destroy-method="destory" >
</bean>
④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)
public class TestProcessor implements BeanPostProcessor{
/**
* 每一个bean初始化前调用,初始化前的预处理
* Object bean:刚刚通过构造器创建好的bean
* String beanName:刚刚创建的bean的名字(id)。
* 返回处理后的bean
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("初始化前的bean"+bean+"-->BeanName:"+beanName);
return bean;
}
/**
* 每一个bean初始化后调用,初始化后进行处理工作,最后返回给用户
* Object bean:刚刚通过创建好的bean
* String beanName:刚刚创建的bean的名字(id)。
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("初始化后的bean"+bean+"-->BeanName:"+beanName);
//各种逻辑处理
return null;
}
}
⑤添加bean后置处理器后bean的生命周期
[1]通过构造器或工厂方法创建bean实例
[2]为bean的属性设置值和对其他bean的引用
[3]将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
[4]调用bean的初始化方法
[5]将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
[6]bean可以使用了
[7]当容器关闭时调用bean的销毁方法
5.5引用外部属性文件
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
①直接配置
<!-- 直接配置 -->
<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>
②创建jdbc. properties属性文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
③引入context名称空间
④指定properties属性文件的位置
<!-- 指定properties属性文件的位置 -->
<!-- classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
⑤从properties属性文件中引入属性值
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="jdbcUrl" value="${prop.url}"/>
<property name="driverClass" value="${prop.driverClass}"/>
</bean>
5.6自动装配
①自动装配的概念[1]手动装配:以value或ref的方式明确指定属性值都是手动装配。
<bean id="bookDao" class="com.atguigu.dao.BookDao"></bean>
<bean id="bookService" class="com.atguigu.service.BookService">
<property name="bookDao" ref="bookDao"></property>
</bean>
[2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
<bean id="bookService" class="com.atguigu.service.BookService" autowire="byName"></bean>
<bean id="bookService" class="com.atguigu.service.BookService" autowire="byName"></bean>
②装配模式
[1]根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
[2]根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同
[3]通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
③选用建议
相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
6、SpEL
6.1简介
Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图。
和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
6.2基本语法
SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。
6.3使用字面量
●整数:<property name="count" value="#{5}"/>
●小数:<property name="frequency" value="#{89.7}"/>
●科学计数法:<property name="capacity" value="#{1e4}"/>
●String类型的字面量可以使用单引号或者双引号作为字符串的定界符号
<property name=“name” value="#{'Chuck'}"/>
<property name='name' value='#{"Chuck"}'/>
●Boolean:<property name="enabled" value="#{false}"/>
6.4引用其他bean
<bean id="emp04" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="detp" value="#{dept}"/>
</bean>
6.5引用其他bean的属性值作为自己某个属性的值
<bean id="emp05" class="com.atguigu.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="deptName" value="#{dept.deptName}"/>
</bean>
6.6调用非静态方法
<!-- 创建一个对象,在SpEL表达式中调用这个对象的方法 -->
<bean id="salaryGenerator" class="com.atguigu.spel.bean.SalaryGenerator"/>
<bean id="employee" class="com.atguigu.spel.bean.Employee">
<!-- 通过对象方法的返回值为属性赋值 -->
<property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
</bean>
6.7调用静态方法
<bean id="employee" class="com.atguigu.spel.bean.Employee">
<!-- 在SpEL表达式中调用类的静态方法 -->
<property name="circle" value="#{T(java.lang.Math).PI*20}"/>
</bean>
6.8运算符
①算术运算符:+、-、*、/、%、^
<property name="price" value="#{98+99}"></property>
②字符串连接:+
③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
④逻辑运算符:and, or, not, |
⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
⑥正则表达式:matches
7、通过注解配置bean
7.1概述
相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。7.2使用注解标识组件
①普通组件:@Component标识一个受Spring IOC容器管理的组件
②持久化层组件:@Respository
标识一个受Spring IOC容器管理的持久化层组件
③业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件
④表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件
⑤组件命名规则
[1]默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
[2]使用组件注解的value属性指定bean的id
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
<!-- 1、给类上加上对应的注解
2、告诉ioc容器去自动扫描加了注解的类.要能自动扫描依赖context名称空间。
以及一个jar(aop)spring-aop-4.0.0.RELEASE.jar
base-package:基础包
他会扫描基础包以及他下面的所有子孙包
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
7.3扫描组件
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
①指定被扫描的package
<context:component-scan base-package="com.atguigu.component"/>
②详细说明
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
[2]当需要扫描多个包时可以使用逗号分隔。
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
<context:component-scan base-package="com.atguigu.component" resource-pattern="autowire/*.class"/>
[4]包含与排除
●<context:include-filter>子节点表示要包含的目标类
<!--指定只将哪些符合规则加入进来
因为默认规则就是将所有的都加入到ioc容器中的,
所以要是得context:include-filter生效必须取消默认规则, use-default-filters="false"
-->
<context:component-scan base-package="com.atguigu"use-default-filters="false">
<!-- 指定只将哪些符合规则加入进来 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
●<context:exclude-filter>子节点表示要排除在外的目标类
<context:component-scan base-package="com.atguigu">
<!-- 指定将哪些符合指定规则的组件不加入到ioc中
expression:指定规则
type:
annotation:指定哪些注解的类不会被扫描进来 最常用的
assignable:指定哪些类不扫 基本常用
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="assignable" expression="com.atguigu.dao.BookDao"/></context:component-scan>
●component-scan下可以拥有若干个include-filter和exclude-filter子节点
●过滤表达式
类别 | 示例 | 说明 |
---|---|---|
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接口 |
③JAR包
必须在原有JAR包组合的基础上再导入一个:spring-aop-4.0.0.RELEASE.jar
7.4组件装配
①需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
@Service
public class BookService {
/**
* 1、先按照类型进行查找,如果找到就返回
* 2、如果按照类型都找不到,报错
No qualifying bean of type [com.atguigu.dao.BookDao]
found for dependency: expected at least 1 bean
3、如果找到唯一的一个则返回。
4、有多个相同的类型
bookDao,bookExtDao
按照类型会找到多个,然后再按照属性名作为id去再次锁定确定的bean.
没有锁定到任何bean。报错
exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.atguigu.dao.BookDao]
is defined: expected single matching bean but found 2:
bookDao,bookExtDao
*/
@Autowired
private BookDao bookDao;
public BookService(){
System.out.println("bs...create....");
}
public BookDao getBookDao() {
return bookDao;
}
}
②实现依据
在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了@Autowired、
@Resource或@Inject注解的属性。
③@Autowired注解
[1]根据类型实现自动装配。
[2]构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解
[3]默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。
[4]若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
/* 告诉ioc容器这个属性不是一定要装配上的。
* required默认是true。必须装上,装配不上报错
*/
@Autowired(required=false)
private BookDao bookDao2;
[5]默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。
此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。
* //* 1、先按照类型找BookDao,找到两个
* 2、在按照属性名(set方法参数的参数名)找,
* 3、使用@Qualifier指定注入bean的名称
* @param bookDao2
*/
@Autowired
public void setBookDao2(@Qualifier("bookExtDao")BookDao bookDao2) {
this.bookDao2 = bookDao2;
}
[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属性。
8、泛型依赖注入
8.1简介
Spring 4.x中可以为子类注入子类对应的泛型类型的成员变量的引用。8.2实现
public abstract class BaseDao<T> {
public abstract void save();
}
public class BaseService<T> {
@Autowired
protected BaseDao<T> baseDao;
}
@Repository
public class BookDao extends BaseDao<Book>{
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("保存了图书....");
}
}
@Repository
public class UserDao extends BaseDao<User>{
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("保存了一个用户....");
}
}
@Service
public class BookService extends BaseService<Book>{
public void save(){
baseDao.save();
}
}
@Service
public class UserService extends BaseService<User>{
public void save(){
baseDao.save();
}
}
测试
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test() {
BookService bean = ioc.getBean(BookService.class);
bean.save();
UserService bean2 = ioc.getBean(UserService.class);
bean2.save();
}
执行结果:
保存一本图书....
保存一个用户....
9、整合多个配置文件
9.1Spring允许通过<import>将多个配置文件引入到一个文件中,进行配置文件的集成。这样在启动Spring容器时,仅需要指定这个合并好的配置文件就可以。9.2import元素的resource属性支持Spring的标准的路径资源