01-Java框架-Spring01-IOC

一、Spring介绍

官网:https://spring.io/

Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。

一句话:Spring是一个开源,轻量化,具有IOC和AOP两大核心功能的容器型框架。

  • IOC:控制反转,指将创建对象的权利交给Spring框架,让框架帮我们维护和管理所需对象,本文主要来讨论该特性。
  • AOP:面向切面编程,下节讨论。

对于Spring而言,发展到今天已经不单单是一个框架了,而是一个全家桶技术(包含了很多模块)。有:Spring、SpringMVC、SpringBoot、SpringData、SpringSecurity等等。

二、Bean规范

在正式学习Spring前,我们先了解一个重要的概念:Bean规范,所谓Bean规范,就是对创建Java类提出一些要求,满足这些要求的类就是一个满足Bean规范的类。

  • 必须是一个公开且具体的类
  • 建议属性私有化
  • 必须有get和set方法
  • 必须有无参构造
public class User {  //一个满足Bean规范的类
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

前面咱们说到Spring可以帮我们创建和管理对象,那么类必须满足Bean规范,Spring才能进行管理。

三、IOC - 控制反转

咱们先来学习他的第一个特性IOC控制反转,让框架来帮我们创建对象。

3.1 环境搭建

1.创建普通maven项目,导入spring-context包即可。

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>5.2.22.RELEASE</version>
</dependency>

这里需要注意,虽然只导入了spring-context包,但是会关联导入其他相关的依赖包。
在这里插入图片描述
也就是说,如果要自己全部来导一遍的话,配置应该如下:

<!--spring上下文的包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.22.RELEASE</version>
</dependency>
<!--spring的core包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.22.RELEASE</version>
</dependency>
<!--spring的bean包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.22.RELEASE</version>
</dependency>
<!--spring表达式的包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.2.22.RELEASE</version>
</dependency>
<!--spring依赖的日志包(可选)-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

3.2 Spring的IOC初体验

1.准备一个满足Bean规范的类

2.在resource目录下,准备配置文件:applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	<!--
    <bean id="..." class="..."> </bean>
    id: bean的id,需要唯一
    class:类的全限定名(包名.类名)
    -->
	
	<bean id="user" class="cn.spring.bean.User"> </bean>
</beans>

3.读取配置文件创建IOC容器,获取对象。这里注意,在spring中把对象/实例也叫做bean。

//1.加载配置文件,并创建IOC容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取容器中的bean(对象)
//参数1:bean的id  参数2:对应的类型
User user = context.getBean("user", User.class);
System.out.println(user);

这里spring构建对象默认调用的是类的无参构造。

3.3 ApplicationContext 和 BeanFactory 的区别(面试题)

ApplicationContext容器 和 BeanFactory容器 都是IOC容器。也就是说使用BeanFactory也能进行对象的管理。

//1.读取配置文件
Resource resource = new ClassPathResource("applicationContext.xml");
//2.创建容器对象
BeanFactory factory = new XmlBeanFactory(resource);
//3.获取bean
User user = factory.getBean("user", User.class);
System.out.println(user);

那么他俩有什么区别呢?
1.ApplicationContext 是 BeanFactory 的子接口。BeanFactory 是所有容器的顶级接口。
在这里插入图片描述
2.ApplicationContext是立即加载bean,容器启动就会把bean创建好。而BeanFactory是懒加载,容器启动不会加载bean,调用时才会加载bean。

3.4 SpringBean懒加载 - lazy

默认情况下bean是在容器启动时就创建好了,如何实现懒加载呢?即:调用的时候再创建,不调用就不创建。只需要为bean增加配置:lazy-init="true"

<bean id="user" class="包路径.User" lazy-init="true"> </bean>

3.5 SpringBean的作用域 - scope

默认情况下bean是单例的,只会创建一个对象,就算获取多次,也返回的是同一个对象。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
System.out.println(user1); //cn.spring.bean.User@57baeedf
System.out.println(user2); //cn.spring.bean.User@57baeedf

如何设置为多例呢?(这里一把不叫多例,而是:原型)即:每次获取都创建新的对象,需要增加配置:scope="prototype"原型,默认是scope="singleton"单例。

<bean id="user" class="包路径.User" scope="prototype"> </bean>

除了singleton单例和prototype原型这两个作用域范围,Spring还有提供有request(表示每个请求需要容器创建一个全新Bean)、session(表示每个会话需要容器创建一个全新Bean)、GlobalSession(似于标准的HTTP Session作用域)等3种作用域。

3.6 Spring创建bean的方式

1.使用无参构造(默认的),上边我们一直使用的就是该方式。

<bean id="xx" class="xxx">

2.静态工厂:构建工厂类,并提供静态方法来创建bean

public class MyBean {
}
//使用静态工厂的方式创建bean
public class MyBeanFactory {
	//提供创建bean的静态方法
    public static MyBean getBean(){
        return new MyBean();
    }
}

配置如下:

<!--
class: 配置静态工厂的全限定名
factory-method:配置静态工厂中创建实例的方法名
spring就会调用getBean()创建实例
-->
<bean class="包路径.MyBeanFactory" factory-method="getBean"></bean>

3.实例工厂:构建工厂类,并提供实例方法来创建bean

public class MyBean {
}
//使用实例工厂
public class MyBeanFactory {
	//提供创建bean的实例方法
    public MyBean getBean(){
        return new MyBean();
    }
}

配置如下:

<!--1.创建工厂的实例-->
<bean id="beanFactory" class="包路径.MyBeanFactory"></bean>
<!--2.调用工厂中的实例方法创建bean
factory-bean:指向实例工厂的bean id
factory-method:实例工厂中的方法名
-->
<bean id="myBean" factory-bean="beanFactory" factory-method="getBean"></bean>

4.SpringBean工厂:实现Spring提供的FactoryBean接口来创建bean

public class MyBean {
}
//实现spring提供的FactoryBean接口
public class MyBeanFactoryBean implements FactoryBean<MyBean> {
    @Override //创建对象的方法,最终会调用该方法创建实例
    public MyBean getObject() throws Exception {
        return new MyBean();
    }
    @Override  //获取bean的类型
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    @Override  //设置是否为单例
    public boolean isSingleton() {
        return true;
    }
}

配置如下:

<bean id="myBean" class="包路径.MyBeanFactoryBean"></bean>

面试题:BeanFactory 和 FactoryBean 的区别?

  • BeanFactory:他是Spring IOC 容器的最顶层接口,它的作用是管理Bean,即实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
  • FactoryBean:就是一个工厂Bean,一个能生产对象的工厂Bean,通过其提供的getObject方法获取你定制化实现的bean,并可以通过isSingleton方法控制是否为单例。

四、DI - 依赖注入

4.1 什么是依赖注入呢?

假设我们有一个类,类中还有属性,按照目前的方式创建的对象,属性都只有默认值,看案例。

public class MyBean {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override  //增加 toString() 方便打印看结果
    public String toString() {
        return "MyBean{" + "id=" + id + ", name='" + name +"'}";
    }
}

2.在xml中进行bean的配置

<bean id="myBean" class="包路径.MyBean"></bean>

3.执行结果:

MyBean{id=0, name='null'}  //都是默认值

所谓DI(Dependency Injection)依赖注入:就是在创建对象时为对象的属性进行赋值。

4.2 依赖注入为基本类型的属性赋值

<!--
	在bean标签中使用 property属性 进行赋值
	name:属性名
	value:属性值
-->
<bean id="myBean" class="包路径.MyBean">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
</bean>

此时的执行结果为:MyBean{id=1, name='张三'}

为8种基本类型 和 String类型 的属性赋值,使用上述方式即可。

4.3 依赖注入为对象属性赋值

案例:如果MyBean的属性中还是一个对象属性,该如何赋值呢?

//MyBean类
public class MyBean {
    private int id;
    private String name;
    private User user;  //对象属性 User
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    @Override
    public String toString() {
        return "MyBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", user=" + user +
                '}';
    }
}
//User类
public class User {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

使用 ref 为对象属性赋值:

<!--1.创建User的实例,bean的id为user -->
<bean id="user" class="cn.spring.bean.User">
    <property name="username" value="admin"></property>
    <property name="password" value="123456"></property>
</bean>
<!--2.创建MyBean的实例-->
<bean id="myBean" class="cn.spring.bean.MyBean">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
    <!--使用ref关联已经创建好User的实例-->
    <property name="user" ref="user"></property>
</bean>

4.4 依赖注入为集合,数组等属性赋值

public class OtherBean {
}

public class MyBean {
    // 数组
    private String[] arrays;
    // 集合中存字符串
    private List<String> list;
    private Set<String> set;
    // 集合中存对象
    private List<OtherBean> otherBeanList;
    private Set<OtherBean> otherBeanSet;
    //get和set
    public String[] getArrays() {
        return arrays;
    }
    public void setArrays(String[] arrays) {
        this.arrays = arrays;
    }
    public List<String> getList() {
        return list;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public Set<String> getSet() {
        return set;
    }
    public void setSet(Set<String> set) {
        this.set = set;
    }
    public List<OtherBean> getOtherBeanList() {
        return otherBeanList;
    }
    public void setOtherBeanList(List<OtherBean> otherBeanList) {
        this.otherBeanList = otherBeanList;
    }
    public Set<OtherBean> getOtherBeanSet() {
        return otherBeanSet;
    }
    public void setOtherBeanSet(Set<OtherBean> otherBeanSet) {
        this.otherBeanSet = otherBeanSet;
    }
    @Override
    public String toString() {
        return "MyBean{" +
                "arrays=" + Arrays.toString(arrays) +
                ", list=" + list +
                ", set=" + set +
                ", otherBeanList=" + otherBeanList +
                ", otherBeanSet=" + otherBeanSet +
                '}';
    }
}

注入配置:

<bean id="otherBean" class="包路径.OtherBean"></bean>

<bean id="myBean" class="包路径.MyBean">
    <!--使用 array标签为数组赋值 -->
    <property name="arrays">
        <array>
            <value>arrays1</value>
            <value>arrays2</value>
            <value>arrays3</value>
        </array>
    </property>
    <!--使用 list标签为list赋值 -->
    <property name="list">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>
    <!--使用 set标签为set赋值 -->
    <property name="set">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>
    <!--使用list标签为list赋值,不同在于集合中为OtherBean-->
    <property name="otherBeanList">
        <list>
            <!--直接bean标签赋值-->
            <bean class="包路径.OtherBean"></bean>
            <bean class="包路径.OtherBean"></bean>
            <!--直接ref关联一个bean的id-->
            <ref bean="otherBean"></ref>
            <ref bean="otherBean"></ref>
        </list>
    </property>
    <!--使用set标签为set赋值,不同在于集合中为OtherBean-->
    <property name="otherBeanSet">
        <set>
            <!--直接bean标签赋值-->
            <bean class="包路径.OtherBean"></bean>
            <bean class="包路径.OtherBean"></bean>
            <!--直接ref关联一个bean的id-->
            <ref bean="otherBean"></ref>
            <ref bean="otherBean"></ref>
        </set>
    </property>
</bean>

到这里咱们已经把Spring的IOC和DI有了一定的了解,会发现IOC和DI是相辅相成的两个技术,IOC构建出对象,DI辅助IOC为对象的属性赋值。

前面都是使用xml配置文件实现依赖注入,还是比较麻烦,Spring还提供了注解的方式实现依赖注入,该注解是:@Autowired,也是后期常用的,在Spring的单元测试中就可以体会到。

五、依赖注入的2种方式

5.1 set方式注入

前面咱们在xml中的bean标签中使用property 标签为属性赋值,用到的就是set方式,也就是调用属性对应的set方法进行值的注入,这也是为什么我们一开始要求必须要有get和set方法的原因。

5.2 构造器注入

使用有参构造进行属性值的注入,案例如下:

public class MyBean { 
    private Long id;     //基本属性
    private String name; //基本属性
    //提供全参构造
    public MyBean(Long id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public String toString() {
        return "MyBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

为基本属性赋值:

<!--使用构造器注入方式1: name的方式(参数名)  -->
<bean id="myBean" class="包路径.MyBean">
	<constructor-arg name="id" value="1"></constructor-arg>
	<constructor-arg name="name" value="张三"></constructor-arg>
</bean>

<!--使用构造器注入方式2: index的方式(参数下标)-->
<bean id="myBean" class="包路径.MyBean">
	<constructor-arg index="0" value="2"></constructor-arg>
	<constructor-arg index="1" value="李四"></constructor-arg>
</bean>

<!--使用构造器注入方式3: type的方式(参数类型) -->
<bean id="myBean" class="包路径.MyBean">
	<constructor-arg type="java.lang.Long" value="3"></constructor-arg>
	<constructor-arg type="java.lang.String" value="王五"></constructor-arg>
</bean>

为对象属性赋值:

public class OtherBean {
}

public class MyBean { //为MyBean的属性赋值
    private Long id;     //基本属性
    private String name; //基本属性
    private OtherBean otherBean;  //对象属性
    //提供全参构造
    public MyBean(Long id, String name,OtherBean otherBean) {
        this.id = id;
        this.name = name;
        this.otherBean = otherBean;
    }
    @Override
    public String toString() {
        return "MyBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", otherBean=" + otherBean +
                '}';
    }
}
<!--为对象属性赋值, 方式1 -->
<bean id="otherBean" class="包路径.OtherBean"></bean>
<bean id="myBean" class="包路径.MyBean">
	<constructor-arg name="id" value="1"></constructor-arg>
	<constructor-arg name="name" value="赵六"></constructor-arg>
	<!--使用ref关联一个存在的bean的id -->
	<constructor-arg name="otherBean" ref="otherBean"></constructor-arg>
</bean>

<!--为对象属性赋值, 方式2 -->
<bean id="myBean" class="包路径.MyBean">
	<constructor-arg name="id" value="1"></constructor-arg>
	<constructor-arg name="name" value="赵六"></constructor-arg>
	<constructor-arg name="otherBean">
		<!--在内部直接使用bean标签创建bean-->
		<bean class="包路径.OtherBean"></bean>
	</constructor-arg>
</bean>

六、Spring的单元测试和@Autowired

1.导包,在原有基础上加入 spring-test 和 junit的包

<!--Spring的测试包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.22.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

2.编写测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml") //加载配置文件并创建IOC容器
public class SpringTest {

	//如果容器中有User的实例,就会根据类型注入
	//如果容器中没有User的实例,就会注入失败
    @Autowired  
    private User user; 	//将容器中的bean注入给属性user(自动装配)
	
	//特殊情况下,如果User在容器中有两个实例,那么就会根据属性名user去容器中去匹配两个实例中哪个的id是user,
	//如果找到了就注入,没找到就注入失败。
	//所以,咱们说@Autowired优先按类型注入,其次按名字注入
	
    @Test
    public void test1(){
        System.out.println(user);
    }
}

3.用于注入(自动装配)的注解有@Autowired和@Resource (面试题)
@Autowired:该注解由Spring提供,优先按照类型注入,相同类型有多个时,再按照名字注入。
@Resource:该注解由Java提供(javax.annotation.Resource),也可以完成依赖注入,但是不同在于它优先按名字查询对应的bean。

七、SpringBean的生命周期

bean的生命周期,即:一个bean从生到死的过程。大致分为:创建 - 初始化 - 提供服务 - 销毁。

public class MyBean {
	public MyBean() {
		System.out.println("-----MyBean创建了------");
	}
	public void init() {
		System.out.println("-----MyBean初始化了------");
	}
	public void destroy() {
		System.out.println("-----MyBean销毁了------");
	}
}

配置如下:

<!--
	init-method: 配置初始化方法
	destroy-method:配置销毁方法
-->
<bean id="myBean" class="包路径.MyBean" init-method="init" destroy-method="destroy"></bean>

最终的执行顺序是:
默认情况下容器启动就执行:构造器 和 初始化方法,在容器关闭时执行销毁方法。
如果设置了懒加载,则容器启动时不会创建对象,在调用时才会调用构造器创建对象,并调用初始化方法,在容器关闭时执行销毁方法。

八、Spring提供了4个注解用于创建bean

Spring提供如下几个注解来标注Spring Bean:

  • @Component: 标注一个普通的Spring Bean类
  • @Controller: 标注一个控制器组件类
  • @Service: 标注一个业务逻辑组件类
  • @Repository: 标注一个DAO组件类

试想,如果有很多对象都需要交给Spring管理,那么在xml中就需要配置很多的<bean>标签,这个问题Spring也提供了对应的注解来简化配置。仅需进行包扫描,就会把包下被上述4种注解修饰的类创建好对应的实例。使用方式为:1.在xml配置文件中加上包扫描的配置 2.在类上使用注解。

<context:component-scan base-package="需要扫描的包"/>

整个案例试试:
1.编写类,并使用注解修饰

package cn.spring.bean;
import org.springframework.stereotype.Component;

@Component
public class MyBean {
}

2.配置文件如下

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
        
	<!-- cn.spring.bean 包下被注解修饰的类就会创建对应的实例 -->
    <context:component-scan base-package="cn.spring.bean"/>
    
</beans>

3.测试,启动容器打印对象看看

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
    @Autowired
    private MyBean myBean;
    @Test
    public void test1(){
        System.out.println(myBean); //cn.spring.bean.MyBean@3b69e7d1
    }
}

九、纯注解版Spring

前面咱们使用了xml进行相关配置,其实Spring支持纯注解编程。咱们来感受下:
1.准备一个配置类 ,在全注解的模式下,会使用配置类来替代xml

@Configuration //标记该类是一个Spring的配置类
public class SpringConfig {
    @Bean //用于修饰方法
    //将该方法返回的User实例交给Spring管理,相当于 <bean id="xx" class="xx"></bean>
    public User user(){
        return new User();
    }
}

2.进行测试
方式1:自己创建容器进行测试

//1.创建IOC容器
//使用AnnotationConfigApplicationContext来读取配置类,并创建对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.获取容器中的实例
User user = context.getBean("user", User.class);
System.out.println(user);

方式2:使用Spring的单元测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class) //加载配置类
public class SpringTest {

    @Autowired
    private User user;

    @Test
    public void test1(){
        System.out.println(user);
    }
}

好,到这里关于Spring的IOC就差不多了,咱们接着去讨论下Spring的AOP。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值