IoC(inversion of control):控制反转。
在JavaSE中我们使用new class类名()的方式去创建我们需要的对象,而SpringIoC是将主动权交给了Spring容器,由容器来帮我们创建我们所需要的对象。
IoC在Spring容器中的实现:
1.在通过IoC容器读取Bean的实例之前,需要先将IoC容器本身实例化。
2.Spring提供了IoC容器的两种实现方式:
(1)BeanFactory:是IoC容器的基本实现,是Spring的基础设施,是面向Spring本身的,与开发人员无关。
(2)ApplicationContext:是BeanFactory的子接口,提供了更多的高级特性许多场合都是用ApplicationContext而不是底层的BeanFactory。
ApplicationContext的主要实现类:
ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件。
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件。
在初始化时就创建单例的bean,也可以通过配置的方式制定创建的Bean是多实例的。
(3)ConfigurableApplicationContext
[1]是ApplicationContext的子接口,包含一些扩展方法
[2]refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。
(4)WebApplicationContext
专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
下面是如何是用SpringIOC来创建对象。
GirlFriend.java
package com.qianfeng.part01;
public class GirlFriend {
private String look;
private int age;
private String height;
private double money;
private boolean sex;
private Addr home;
private Addr now;
// public GirlFriend(String look, int age, String height, double money) {
// this.look = look;
// this.age = age;
// this.height = height;
// this.money = money;
// System.out.println("This is constructor with 4 param");
// }
//5参构造器
public GirlFriend(String look, int age, String height, double money, boolean sex) {
this.look = look;
this.age = age;
this.height = height;
this.money = money;
this.sex = sex;
System.out.println("This is constructor with 5 param");
}
//全参构造器
public GirlFriend(String look, int age, String height, double money, boolean sex, Addr home, Addr now) {
this.look = look;
this.age = age;
this.height = height;
this.money = money;
this.sex = sex;
this.home = home;
this.now = now;
}
//Getter和Setter方法
public GirlFriend() {
System.out.println("This is constructor without param");
}
public Addr getHome() {
return home;
}
public void setHome(Addr home) {
this.home = home;
}
public Addr getNow() {
return now;
}
public void setNow(Addr now) {
this.now = now;
}
public String getLook() {
return look;
}
public void setLook(String look) {
this.look = look;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
@Override
public String toString() {
return "GirlFriend{" +
"look='" + look + '\'' +
", age=" + age +
", height='" + height + '\'' +
", money=" + money +
", sex=" + (sex ? "boy" :"girl") +
", home=" + home +
", now=" + now +
'}';
}
}
beans.xml
<?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为标识,class为全类名。
<bean id="gf1" class="com.qianfeng.part01.GirlFriend">
//使用<property></property>标签里的name和value属性来给属性赋值
//注意此时使用的是Setter方法而不是构造器。
<property name="age" value="23"/>
<property name="look" value="zhangliu"/>
<property name="height" value="159"/>
<property name="money" value="5000"/>
<property name="sex" value="true"/>
</bean>
<bean id="gf2" class="com.qianfeng.part01.GirlFriend">
//constructor-arg标签使用的是有参构造器,
<constructor-arg name="age" value="23"/>
<constructor-arg name="look" value="zhangliu"/>
<constructor-arg name="height" value="159"/>
<constructor-arg name="money" value="5000"/>
<!--<property name="sex" value="true"/>-->
</bean>
<bean id="gf3" class="com.qianfeng.part01.GirlFriend">
//constructor-arg标签不写name属性,就按照有参构造器顺序依次赋值
//注意如果参数类型不同会出错
<constructor-arg value="zhangliu"/>
<constructor-arg value="23"/>
<constructor-arg value="159"/>
<constructor-arg value="5000"/>
<constructor-arg value="false"/>
</bean>
<bean id="gf4" class="com.qianfeng.part01.GirlFriend">
//index可以给所要赋值的属性按照0,1,2,3编号
<constructor-arg index="0" value="zhangliu"/>
<constructor-arg index="1" value="23"/>
<constructor-arg index="3" value="5000"/>
<constructor-arg index="4" value="true"/>
<constructor-arg index="2" value="159"/>
</bean>
<bean id="gf5" class="com.qianfeng.part01.GirlFriend">
//type是属性的类型,注意要写全类名
<constructor-arg type="String" value="zhangliu"/>
<constructor-arg type="int" value="23"/>
<constructor-arg type="String" value="159"/>
<constructor-arg type="double" value="5000"/>
<constructor-arg type="boolean" value="false"/>
</bean>
<bean id="home" class="com.qianfeng.part01.Addr">
<property name="prov" value="anhui"/>
<property name="city" value="anqing"/>
<property name="area" value="taihu"/>
</bean>
<bean id="now" class="com.qianfeng.part01.Addr">
<property name="prov" value="anhui"/>
<property name="city" value="chuzhou"/>
<property name="area" value="dingyuan"/>
</bean>
<bean id="gf6" class="com.qianfeng.part01.GirlFriend">
//home是一个对象,可以先用bean标签创建,然后用ref赋值。
<property name="age" value="23"/>
<property name="look" value="zhangliu"/>
<property name="height" value="159"/>
<property name="money" value="5000"/>
<property name="sex" value="false"/>
<property name="home" ref="home"/>
<property name="now" ref="now"/>
</bean>
</beans>
GrilFriendSpring.java
package com.qianfeng.part01;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class GirlFriendSpring {
@Test
public void testGirlFriend(){
//获取对应类路径下的配置文件
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
//通过id获取bean
Object obj=ac.getBean("gf1");
System.out.println(obj);
GirlFriend gf1=(GirlFriend) ac.getBean("gf2");
System.out.println(gf1);
GirlFriend gf2=(GirlFriend) ac.getBean("gf3");
System.out.println(gf2);
GirlFriend gf3=(GirlFriend) ac.getBean("gf4");
System.out.println(gf3);
GirlFriend gf4=(GirlFriend) ac.getBean("gf6");
System.out.println(gf4);
}
}
测试结果
This is constructor without param
This is constructor with 4 param
This is constructor with 5 param
This is constructor with 5 param
This is constructor with 5 param
This is constructor without param
GirlFriend{look='zhangliu', age=23, height='159', money=5000.0, sex=boy, home=null, now=null}
GirlFriend{look='zhangliu', age=23, height='159', money=5000.0, sex=girl, home=null, now=null}
GirlFriend{look='zhangliu', age=23, height='159', money=5000.0, sex=girl, home=null, now=null}
GirlFriend{look='zhangliu', age=23, height='159', money=5000.0, sex=boy, home=null, now=null}
GirlFriend{look='zhangliu', age=23, height='159', money=5000.0, sex=girl, home=Addr{prov='anhui', city='anqing', area='taihu'}, now=Addr{prov='anhui', city='chuzhou', area='dingyuan'}}
p名称空间
为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。
Spring从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean的属性。
使用p命名空间后,基于XML的配置方式将进一步简化。
<bean
id="gf1"
class="com.qianfeng.part01.GirlFriend"
p:look="zhangliu" p:age="23" p:sex="true" />
说明
对于beans.xml文件而言,里面定义的都是bean对象,对于spring容器而言,所有对象都可以被认为是bean对象
- 单纯的bean节点,要求我们bean对象必须包含无参构造器
- 每个bean对象对于spring容器而言,默认都是单例的,通过scope来修改它的创建方式,
四个值singleton, prototype(java), request, session - property属性要求我们的bean对象必须要有标准的setter
- <property name="**home" ref="home" />
对于带有参数构造器的对象的创建方式,使用bean和constructor-arg来调用带参构造器完成对象的创建,其创建方式有四种 - name + value/ref(可读性最高,顺序随意换)
- value(代码最简单,但是可读性最差,顺序不能换)
- index + value/ref(简单,但是可读性⼀般, index值从0开始)
- Type + value/ref(相同类型的值按照顺序赋值,不同类型数据可以⾃由换顺序)
对象赋值
可以使用ref - property赋值
- 构造器赋值
实验1:测试bean的作用域,分别创建单实例和多实例的bean
所谓bean的作用域:就是看bean是否单实例。
prototype:多实例
(1)容器启动默认不会创建多实例Bean
(2)获取的时候创建这个Bean
(3)每次获取都会创建一个新的对象
singleton:单实例、默认的
(1)在容器启动完成之前就已经创建好对象了,保存在容器中。
(2)任何时候获取都是获取之前创建好的那个对象。
request:在web环境下,同一次请求创建一个Bean实例(没有用)
session:在web环境下,同一次会话创建一个Bean实例(没有用)
实验2:配置通过静态工厂创建bean,通过实例工厂创建bean
bean的创建默认就是矿建通过反射new出来的bean实例。
工厂模式,通过工厂帮我们创建对象,有一个类帮我们创建对象,这
个类就是工厂类。
Airplane air=AirplaneFactory.getAirplane(String airName)
静态工厂:工厂本身不用创建对象,通过静态方法调用:
对象=工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象:
工厂类 工厂对象=new 工厂类();
工厂对象.工厂方法名();
beans3.xml
<?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">
<!--静态工厂-->
//静态工厂不需要创建工厂类对象
//直接以AirplaneStaticFactory.getAirplane();形式直接创建对象
//factory-method:标识工厂类的方法
<bean id="air01" class="com.qianfeng.factory.AirplaneStaticFactory" factory-method="getAirplane">
//此时对应的参数是调用的getAirplane方法的参数。
<constructor-arg value="jian20"></constructor-arg>
</bean>
<!--实例工厂-->
//需要以new AirplaneInstanceFactory();的形式先创建工厂类对象,再去调用getAirplane();方法来创建对象
<bean id="AirplaneInstanceFactory" class="com.qianfeng.factory.AirplaneInstanceFactory" >
</bean>
//factory-bean:标识用的工厂
<bean id="air02" class="com.qianfeng.factory.Airplane" factory-bean="AirplaneInstanceFactory" factory-method="getAirplane">
<constructor-arg value="jian10"></constructor-arg>
</bean>
</beans>
实验3基于XML的自动装配(自动赋值):给自定义类型属性赋值,与基本类型无关。
property:手动赋值
autowire="default/no":默认、不自动赋值
autowire="byName":表示以属性名称来赋值(前提Spring容器中必须有这个组件即已经赋值的组件,如果没有这个组件,则赋值NULL)
autowire="byType":表示以属性类型来赋值,没有找到也是赋值NULL,多个会报异常:BeanDefinitionParsingException: Configuration problem: Bean name 'home' is already used in this <beans> element
Offending resource: class path resource [beans.xml]。
autowire="constructor":按照有参构造器进行装配(赋值)
1.先按照参构造器的类型进行装配(成功就赋值),如果多个就赋值NULL。
2.如果类型找到了多个,按照id进行赋值,找到就装配,找不到就NULL。
实验4:SpEL测试(Spring Expression Language)
在SpEL中使用字面量:
JSP是:${};
Spring :#{};
引用其他Bean的某个属性值:
1.#{id.属性名},直接引用
引用其他Bean
2.#{id},当属性类型为自定义类型时,
代码:
<bean id="gf9" class="com.qianfeng.part01.GirlFriend">
在SpEL中使用字面量:
<property name="age" value="#{12*5}"/>
引用其他Bean的某个属性值
<property name="look" value="#{gf6.look}"/>
引用其他Bean
<property name="home" value="#{home}"/>
</bean>
调用静态方法:
#{T(方法全类名).方法(参数列表)}(注意可以连续调用)
调用非静态方法:
#{对象ID.方法(参数列表)}
实验5 通过注解分别创建Dao、Service、Controller(控制器:控制网站跳转逻辑Servlet)
通过给bean上添加某些注解,可以快速的讲bean加入到ioc容器中
某个类上添加上任何一个注解都能够快速的将这个组件加入到ioc容器中的管理中;
Spring有四个注解:
@Controller:控制器:我们推荐给控制器层(Servlet包下的这些)的组件加这个注解
@Service:业务逻辑:我们推荐业务逻辑层的组件添加这个组件
@Repository:给数据库层(持久化层,dao层)
@Component:给不属于以上几层的组件添加这个注解;
使用注解将组件快速的加入到容器中需要几步;
1.给要添加的组件上标四个注解的任何一个
2.告诉Spring,自动扫描加了注解的组件,依赖context名称空间
3.一定要导入aop包,支持加注解模式的
代码如下:
context:component-scan:表示自动组件扫描
base-package:指定扫描的基础包,吧基础包及下面所有的包加了注解的类,自动的扫描进ioc容器中。
<context:component-scan base-package="com.qianfeng"/>
总结:使用注解加入到容器中的组件,和使用配置加入到容器中的组件行为都是默认一样的:
1.组件的id默认是组件的类名首字母小写。
2.组件的作用域,默认就是单例的。
实验6:使用context:exclude-filter指定扫描包时不包含的类
扫描的时候可以排除一些不要的组件
type="annotation":指定排除规则,按照注解进行排除,标注了指定注解的组件不要
expression=“”:注解的全类名
type="assignable":指定排除某个具体的类,按照类排除
expression=“”:类的全类名
以下三种不常用
type="aspectj":后来aspectj表达式
type="custom":自定义一个TypeFilter;自己写代码决定哪些使用
type="regex":还可以写正则表达式
实验7使用context:exclude-filter指定扫描包时要包含的类
只扫描进入哪些组件,默认都是全部扫描进来
· 一定要禁用掉默认过滤规则:use-default-filters="false"
其他跟实验6一样。
实验8:自动装配,主动赋值
在属性前面加上@Autowired
Autowired:Spring自动为这个属性赋值,一定是去容器中找这个属性对应的组件
原理:1.先按照类型去容器中找到对应的组件;
找到一个就赋值;
没找到,抛异常;
找到多个,就按属性的ID去找。
按属性ID找到就赋值,
没找到,抛异常,但是这时可在属性前面加上@Qualifier("指
定属性名")来指定要找的属性是哪个。
Autowired:一定会给你找到这个属性,但是可以通过设置@Autowired(required=“false”)指定某个属性找不到就赋值NULL.