Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器(框架)
Spring是一个IOC(DI)和AOP容器框架
Spring的优良特性:
下载的文件名spring-framework-x.x.x.RELEASE-dist.zip
libs目录下的jar包说明
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导入依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.0.RELEASEversion>
dependency>
导包
核心容器
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运行的时候依赖一个日志包;没有就报错;
新建一个Person类,添加set、get方法
public class Person {
private String lastName;
private Integer age;
private String gender;
private String email;
//get set方法
}
在classpath下创建一个Spring配置文件ApplicationContext.xml,注册bean。
使用bean标签注册一个Person对象,Spring会自动创建这个Person对象
class:写要注册的组件的全类名
id:这个对象的唯一标识
使用property标签为Person对象的属性值,name:指定属性名;value:指定属性值
<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="person01" class="com.zb.bean.Person">
<property name="lastName" value="张三">property>
<property name="age" value="18">property>
<property name="email" value="[email protected]">property>
<property name="gender" value="男">property>
bean>
beans>
通过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 protected]]
}
1、关于类路径
src:源码文件夹开始的路径,称为类路径的开始,所有源码文件夹里面的东西都会被合并放在类路径里面;
Java文件的类路径为:/bin/
JavaWeb文件的类路径为:/WEB-INF/classes/
2、ApplicationContext(IOC容器的接口)
3、Person对象是什么时候创建好了呢?
4、同一个组件在ioc容器中是单实例的;容器启动完成都已经创建准备好的。
5、容器中如果没有这个组件,获取组件?报异常
6、IOC容器用property标签创建这个组件对象的时候,会利用setter方法为其属性赋值,注意属性名是set方法后的那串的首字母小写
1、IOC(Inversion of Control):控制反转
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。
其本意就是将原本在程序中手动创建对象的控制权,交给Spring来管理
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
使用 IOC 目的:为了耦合度降低
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
IOC的底层原理:xml 解析、工厂模式、反射
2、DI(Dependency Injection):依赖注入,就是注入属性
3、IOC容器在Spring中的实现
BeanFactory
:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象ApplicationContext
:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。加载配置文件时候就会把在配置文件对象进行创建4、ApplicationContext的主要实现类
5、ConfigurableApplicationContext
6、WebApplicationContext
从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);
}
<alias name="userT" alias="userNew"/>
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
<import resource="{path}/beans.xml"/>
<bean id="book" class="com.atguigu.spring5.Book">
<property name="bname" value="易筋经">property>
<property name="bauthor" value="达摩老祖">property>
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>
<bean id="action" class="com.atguigu.spring.ref.Action">
<property name="service" ref="service"/>
<property name="service.dao.dataSource" value="DBCP"/>
bean>
为了简化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>
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="[email protected]" c:gender="1">
bean>
新建一个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;
}
可以使用字符串表示的值,可以通过value属性或value子节点的方式指定
基本数据类型及其封装类、String等类型都可以采取字面值注入的方式
注意:若字面值中包含特殊字符,可以使用把字面值包裹起来
<bean id="user" class="com.zb.spring5.User">
<property name="name" value="<<南京>>">property>
bean>
<bean id="person01" class="com.zb.bean.Person">
<property name="lastName">
<null/>
property>
bean>
<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">
<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
<bean id="person02" class="com.zb.bean.Person">
<property name="lastName" value="张三">property>
<property name="car">
<bean id="car02" class="com.zb.bean.Car">
<property name="carName" value="自行车">property>
bean>
property>
bean>
<bean id="shop" class="com.atguigu.spring.bean.Shop" >
<property name= "categoryList">
<list>
<value> 历史value >
<value> 军事value >
list>
property>
<property name= "bookList">
<list>
<ref bean= "book01"/>
<ref bean= "book02"/>
list>
property>
bean >
<bean id="person04" class="com.zb.bean.Person">
<property name="maps">
<map>
<entry key="key01" value="value01">entry>
<entry key="key02">
<value>value02value>
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>
<bean class="com.atguigu.spring.bean.DataSource" id="dataSource">
<property name="properties">
<props>
<prop key="userName">rootprop>
<prop key="password">rootprop>
<prop key="url">jdbc:mysql:///testprop>
<prop key="driverClass">com.mysql.jdbc.Driverprop>
props>
property>
bean>
<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>
<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:map id="myMap">
<entry key="key01" value="value01">entry>
<entry key="key02">
<value>value02value>
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>
<bean id="person04" class="com.zb.bean.Person">
<property name="maps" ref="myMap">property>
bean>
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;
}
}
<bean id="airPlane01" class="com.zb.factory.AirPlaneStaticFactory"
factory-method="getAirPlane">
<constructor-arg value="李四">constructor-arg>
bean>
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>
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]
}
在配置文件中注册工厂对象
<bean id="myFactoryBeanImple" class="com.zb.factory.MyFactoryBeanImple" >
bean>
注意:IOC容器启动时不会创建实例,使用getBean时才会创建
<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>
<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="[email protected]">property>
bean>
<bean id="person09" parent="person08">
<property name="lastName" value="李四">property>
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 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>
可以在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环境 |
生命周期:从对象创建到对象销毁的过程
bean 生命周期:
1、Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务
2、Spring IOC容器对bean的生命周期进行管理的过程:
3、在配置bean时,通过init-method
和destroy-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的后置处理器
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>
当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属性文件的位置
<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>
1、自动装配的概念
[1]手动装配:以value或ref的方式明确指定属性值都是手动装配。
[2]自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。
②装配模式
[1]根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
[2]根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同
[3]通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
3、选用建议
设置
元素的:autowire
属性
注意:
如果按照byName
自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名
如果安装byType
注入,如果Spring容器中同一个类型有多个实例,报 bean不是唯一类型错误;
1、根据属性名称自动注入
2、根据属性类型自动注入
<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>
<property name="lastName" value="#{car01.carName}">property>
<property name="car" value="#{car01}">property>
<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(1,3)}">property>
<property name="gender" value="#{car01.getCarName()}">property>
bean>
相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式
①普通组件:@Component
②持久化层组件:@Respository
③业务逻辑层组件:@Service
④表述层控制器组件:@Controller
⑤组件命名规则
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
注解标识组件的使用步骤:
导入AOP包的依赖
开启组件扫描
给bean上添加注解之后,还需要告诉Spring,自动扫描加了注解的组件,依赖context名称空间
<context:component-scan base-package="com.zb">context:component-scan>
创建类,在类上面添加创建对象注解
//组件的id。默认为_组件类名首字母小写
@Controller("bookServlethh")或(value="bookServlethh")//等价于
public class BookServlet{}
//在注解里面 value 属性值可以省略不写,
总结:使用注解将组件快速的加入到容器中需要几步:
指定要扫描的包
JAR包
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类
[2]当需要扫描多个包时可以使用逗号分隔
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类
component-scan下可以拥有若干个include-filter和exclude-filter子节点
<context:component-scan base-package="com.atguigu" >context:component-scan>
<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:component-scan base-package="com.zb" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller">context:include-filter>
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接口 |
①需求
②实现依据
③@Autowired注解
④@Resource
⑤@Inject
直接在成员上添加@Autowired就可以完成自动装配。
@Service
public class BookService {
//使用@Autowired完成成员BookDao的自动装配
@Autowired
private BookDao bookDao;
public void read() {
this.bookDao.readBook();
}
}
Autowired的执行流程:
首先按照类型去容器中找对应的组件,如果找到一个就赋值,找不到就抛异常;
如果有多个类型匹配时,会使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功,找不到就报错。
结合注解@Qualifer,指定一个id:在自动按照类型注入的基础之上,再按照指定的bean的id去查找。
@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 //根据类型进行注入
@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异同
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
}
@Test
public void testService2() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
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);
}
}
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
}
}
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服务器中加载资源 |