Spring图文详细教学笔记

Spring 概述

(我是先学了springboot,现在穿越回来啃啃spring老祖宗,看看有什么不一样的发现)

 

Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。(以上是官方话)

简单来说:spring诞生的目的是为了解决企业应用开发的复杂性!

创始人:Rod Johnson ----一个音乐学博士 有点东西给是?

 

两个开发框架组合:

SSH:Struct2 + Spring + Hibernate

SSM: SpringMvc + Spring + Mybatis

 

Spring的优点:

1.Spring是一个开源的、免费的框架。

2.Spring是一个轻量级的、非入侵式的框架(非入侵就是在原有项目中导入spring项目,不会影响原项目。)

3.控制反转、面向切面编程(AOP)

4.支持事物处理,对框架整合的的支持。

 

IoC本质.

IoC在SpringBoot笔记中已经说明了,这里不做过多的解释了。

控制反转loC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法。

没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序

中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。

 

创建第一个Spring

需要先给出官方文档地址:

https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html

先生成一个实体类

package com.heyexi.pojo;


public class Hello
{
    private String name;

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @Override
    public String toString()
    {
        return "Hello{" +
                "name='" + name + '\'' +
                '}';
    }
}

然后需要创建一个配置文件,以下是标准格式

<?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">

</beans>

然后就可以在里面配置我们的Javabean了。

<?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="hi" class="com.heyexi.pojo.Hello">
        <property name="name" value="何夜息"></property>
    </bean>
</beans>

我们要自动装配一个bean,那么可以通过bean标签来设置,配置一个bean就相当于实例化了一个对象,把这个对象交给spring去管理,需要用到时候我们只需要通过spring上下文就可以获取了。

其中:id是相对于对象的名字,例如: Dog hsq = new Dog();那么id就是hsq,然后后面的class是告诉spring这个实体类的具体位置。

那么如何给这个对象设置属性呢?

可以通过下面的property 标签来设置对象的属性,name就是属性名,value就是值,也很简单,很好理解。

既然我们已经配置好了,那么如果获取呢?通过:实例化容器(ApplicationContext )来获取:!

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest
{
    public static void main(String[] args)
    {
        //获取Spring上下文对象,这句话是固定写法
        //ClassPathXmlApplicationContext里面可以写多个xml配置文件的名字
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        System.out.println(applicationContext.getBean("hi"));

    }
}

可以看到,我们在测试类中先生成一个ApplicationContext 对象,这个是用来获取bean对象的。

然后通过getBean("bean的ID")就可以获取到我们配置的对象了,如输出结果如下:可以看到成功获取了对象,而不必须我们去自己new一个对象,这就会控制反转。

7月 10, 2020 4:27:03 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
Hello{name='何夜息'}

注意,如果我们自定义了有参构造方法,那么配置文件中的bean就会报错,因为IOC创建不了无参对象,所有才会有Javabean的规范,就是要有无参构造方法。

那要如何配置有构造参数的bean呢?有多种方法,这里使用获取构造方法下标,也就是使用第几个构造方法来配置。

<?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="hi" class="com.heyexi.pojo.Hello">
        <constructor-arg index="0" value="何夜息"></constructor-arg>
    </bean>
</beans>

可以看到是使用了<constructor-arg>这个标签来说创建的。

还有一种最常用的方式,就是通过直接构造方法中的参数赋值就行。

<bean id="hi" class="com.heyexi.pojo.Hello">
    <constructor-arg name="name" value="何夜息"></constructor-arg>
</bean>

name的值跟的就是构造方法的参数名!!例如假设构造参数多了一个age,那么我们可以:就很方便!!!

<bean id="hi" class="com.heyexi.pojo.Hello">
    <constructor-arg name="name" value="何夜息"></constructor-arg>
    <constructor-arg name="age" value="20"></constructor-arg>
</bean>

特别重要的一个知识点:不管bean是否被调用,只要在配置文件中配置了,那么IOC就已经把对象给创建好了!

比如:我新建了一个实体类--User,然后在无参构造方法中输出一段话,也就是只要这个类被实例化就输出这句话!

package com.heyexi.pojo;

public class User
{
    private String name;
    private Integer id;

    public User()
    {
        System.out.println("用户被创建!");
    }
    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public Integer getId()
    {
        return id;
    }

    public void setId(Integer id)
    {
        this.id = id;
    }
}

然后我把它装配到配置文件中:

<?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="hi" class="com.heyexi.pojo.Hello">-->
<!--        <constructor-arg index="0" value="何夜息"></constructor-arg>-->
<!--    </bean>-->

    <bean id="hi" class="com.heyexi.pojo.Hello">
        <constructor-arg name="name" value="何夜息"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
    </bean>

    <bean id="user" class="com.heyexi.pojo.User"></bean>
</beans>

然后在测试类中只调用第一个对象!看看运行结果!

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest
{
    public static void main(String[] args)
    {
        //获取Spring上下文对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        System.out.println(applicationContext.getBean("hi"));
    }
}

 

结果显示User也被创建了!!!说明只要是弄到配置文件中,Spring都会把所有的Bean都生成出来,不管你是否调用!

Spring配置的其他配置:

别名:别名相当于换了个id名,很简单。没啥用,因为bean的name可以用空格取多个名字。

<alias name="user" alias="user2"></alias>

import:导入其他的bean配置文件。

<import resource="beans2.xml"></import>

依赖注入(DI)

什么是依赖注入?我的理解就是:

依赖:就是我们的应用程序依赖于容器,比如在某个业务中,我们需要一个连接数据库的Connection对象,但我们可以通过依赖容器,让容器给我们这个对象。

注入:就是在配置文件中,把对象的各种属性注入。

 

如何注入多种类型的属性?

比如,我们创建一个Javabean

package com.heyexi.pojo;

import lombok.Data;

import java.util.List;
import java.util.Map;

@Data
public class Teacher
{
    private String name;
    private Hello hello;
    private String[] course;
    private List<String> hobby;
    private Map<String,String> id;
}

然后把这个注入到到配置文件中,请看配置信息:

<bean id="teacher" class="com.heyexi.pojo.Teacher">
    <!--普通类型通过value注入-->
    <property name="name" value="何夜息"></property>

    <!--引用对象类型通过ref注入-->
    <property name="hello" ref="hi"></property>

    <!--数组也是属于引用对象类型-->
    <property name="course">
        <array>
            <value>数据结构与算法</value>
            <value>数据库导论</value>
            <value>离散数学</value>
        </array>
    </property>

    <!--List注入-->
    <property name="hobby">
        <list>
            <value>写代码</value>
            <value>听音乐</value>
            <value>跑步</value>
            <value>弹吉他</value>
        </list>
    </property>

    <!--map注入-->
    <property name="id">
        <map>
            <entry key="教师编号" value="18744"></entry>
            <entry key="住址" value="云南"></entry>
        </map>
    </property>
</bean>

需要注意的也就是map的注入方式,是通过实体标签,通过给实体赋值键值对进行注入的。

然后我们在测试类中引入这个对象。

public class Mytest
{
    public static void main(String[] args)
    {
        //获取Spring上下文对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        System.out.println(applicationContext.getBean("teacher"));
    }
}

输出结果如下:

信息: Loading XML bean definitions from class path resource [beans.xml]
Teacher(name=何夜息, hello=Hello{name='何夜息'}, 
course=[数据结构与算法, 数据库导论, 离散数学], 
hobby=[写代码, 听音乐, 跑步, 弹吉他], id={教师编号=18744, 住址=云南})

bean的作用域(Bean Scopes)

以上就是官网的六种作用域,默认是单例模式,如下是中文版!把最后两个合并为了一个。

作用域

描述

singleton

在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值

prototype

每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()

request

每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境

session

同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境

global-session

一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境

  1. 单例模式

    就是全局共享一个对象,不管在哪调用都是同一个对象。

<bean id="user" class="com.heyexi.pojo.User" scope="singleton"></bean>
  1. 原型模式

          这个相当于每次从容器中拿的时候都是新new出来的对象。

<bean id="user" class="com.heyexi.pojo.User" scope="prototype"></bean>

Spring Beans 自动装配

除了使用可以使用配置文件中<property>来我们自己指定id获取bean外,我们也可以通过属性autowire来让spring自己去找需要的bean。

这个在SpringBoot中不是随处可见么?也没啥可以解释的了吧,一下是获取方式。

no

这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。

byName

由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。

byType

由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。

constructor

类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。

autodetect

Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

自动装配的局限性

当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不使用自动装配,它可能会使开发人员混淆的使用它来连接只有一个或两个 bean 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应该在使用它们之前考虑到自动装配的局限性和缺点。

 

只要注意byName是根据Javabean的set方法来获取的,就只需要保证xml中bean的id唯一。

byType则是根据xml中class的类型来匹配的,就不能有两个相同 class的配置文件,就会出错,需要保证bean的class唯一。

 

Spring的注解

首先,要使用注解需要在xml中提供注解约束

需要加入如下来句话:

xmlns:context="http://www.springframework.org/schema/context"

<context:annotation-config/>

 

@Component注解:

这个注解的作用就是可以不再xml中配置bean了,比如我们新建一个实体类,然后在类上使用该注解。

package com.heyexi.pojo;
import org.springframework.stereotype.Component;

@Component  //相当于:<bean id="dog" class="com.heyexi.pojo.User" />
public class Dog
{
    private String name;
    
    @Override
    public String toString()
    {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}

然后就可以在测试类中使用它了!

import com.heyexi.pojo.Dog;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest
{
    public static void main(String[] args)
    {
        //获取Spring上下文对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

        Dog dog = (Dog)applicationContext.getBean("dog");
        System.out.println(dog);
    }
}
//输出
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7637f22: startup date [Sun Jul 12 00:22:04 CST 2020]; root of context hierarchy
7月 12, 2020 12:22:04 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
Dog{name='null'}

 

注意:调用时这个组件的名字就是类名的小写。

但是要使用这个组件注解,我们需要在配置文件中指定组件所在的包!就是下面这一句!

<context:component-scan base-package="com.heyexi.pojo" />

 

@Component有几个衍生注解,功能都一样,并没有其他特殊的功能,都是把类注入到spring容器中,只是为了区分类的作用,取了不同的名字。

  • 在dao层我们使用 @Repository注解
  • 在service层我们使用 @Service
  • 在controller层我们使用 @Controller

@Value注解:

这个注解相当于是xml配置文件中的property,就是给成员变量赋值,可以配合Comment一起使用的话,完全就可以脱离配置文件了。

@Component
public class Dog
{
    @Value("哈士奇")  //直接就把值给注入了
    private String name;
    
    
输出:
信息: Loading XML bean definitions from class path resource [beans.xml]
Dog{name='哈士奇'}

要多用注解,因为在SpringBoot中基本只使用注解,代替了配置。

但是注解和配置都有自己特点:

使用配置文件的好处就是维护方便,所有的bean都统一管理,而注解维护不方便,要修改就需要去单独找。

 

所以,最佳的实践就是:

     Xml中负责配置bean

     注解(@value)负责在类中注入属性,而不再xml文件中使用属性,看着方便管理。

 

 

使用Configuration完全代替xml配置:

我们可以使用@Configuration注解,来代替xml配置文件,这个在SpringBoot中就是这么做的,我都不清楚这两个有什么区别了。

这个特别简单,就是直接生成一个类,给这个类配置@Configuration,然后在里面通过@bean注解配置bean,就会由spring来接管,如下代码:

package com.heyexi.config;

import com.heyexi.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig
{
    @Bean
    public User getUser()
    {
        return new User();
    }
}

我弄了一个Bean,是用来获取User对象的,然后在测试类试着拿出来。

import com.heyexi.config.MyConfig;
import com.heyexi.pojo.Dog;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest
{
    public static void main(String[] args)
    {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println(applicationContext.getBean("getUser"));
    }
}


输出结果:
WARNING: All illegal access operations will be denied in a future release
用户被创建!
User{name='null', id=null}

可以看到,现在上下文是通过new AnnotationConfigApplicationContext("配置类的字节码")来获取的,不是通过xml,结果都是一样的。

只需要注意getBean默认是通过配置类的方法名获取的,当然也可以取别名,这个在SpringBoot笔记中写得很清楚了。

作用域可以使用@Scope注解来使用!这个还是很重要的,有时候某些实体我们需要使用单例模式,比如dao类,有时候有些实体又需要使用原型模式,这样才能拿到不同的对象。

比如我们定义为原型:

@Configuration
public class MyConfig
{
    @Bean
    @Scope("prototype")
    public User getUser()
    {
        return new User();
    }
}

然后在测试类中:

public class Mytest
{
    public static void main(String[] args)
    {
//        //获取Spring上下文对象
//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//
//        Dog dog = (Dog)applicationContext.getBean("dog");
//        System.out.println(dog);

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

        User user1 = (User) applicationContext.getBean("getUser");
        User user2 = (User) applicationContext.getBean("getUser");

        System.out.println(user1.hashCode());
        System.out.println(user2.hashCode());
}
输出结果:
WARNING: All illegal access operations will be denied in a future release
用户被创建!
用户被创建!
1948471365
1636506029

说明拿到的是不同的对象。

如果我们改为单例模式:输出结果为:

WARNING: All illegal access operations will be denied in a future release
用户被创建!
60292059
60292059

拿到的是同一个对象,同时可以看到构造方法都只执行了一个,说明都是同一个对象,因为是单例模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值