Spring学习笔记01

175 阅读10分钟

1.Spring概述

1.1 简介

0mwZYF.png

Spring :春天----> 给软件行业带来了春天

2002年,Rob Jahnson首次推出了Spring框架原型interface21框架

2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版

Spring理念:使现有技术更加实用,整合现有的框架技术

官网:spring.io/

官方文档: docs.spring.io/spring-fram…

官方下载地址:repo.spring.io/release/org…

GitHub地址:github.com/spring-proj…

优点:

  • Spring是一个免费开源的框架,容器
  • spring是一个轻量级的框架,非侵入式的
  • 控制反转IoC 面向切面 Aop
  • 对事务的支持,对框架的支持

总结:Spring是一个轻量级的控制反转(IoC)和面向切面(Aop)的容器(框架)

1.2 组成

0m0ZHP.png

Spring框架是一个分层架构,由7个定义良好的模块组成,Spring模块构建在核心容器之上,核心容器定义了创建,配置,和管理bean的方式

0mrgU0.png

组成Spring框架的每个模块都可以单独存在,或者与其它一个或多个模块联合实现,每个模块的功能如下

  • 核心容器:核心容器提供Spring框架的基本功能,核心容器的主要组件是BeanFactory,他是工厂模式的实现

BeanFactory使用控制反转的模式(IoC)将应用程序的配置和依赖性规范与实际的应用程序代码公开

  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP: 通过配置管理特性,Spring AOP模式直接将面向切面的编程功能,集成到了Spring框架中,所以,可以很容易的使Spring框架管理任何支持AOP的对象,Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务,通过使用Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中
  • Spring DAO: JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC框架:MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架变成为高度可配置的,MVC容纳了大量视图技术,其中包括JSP、Velocity、iText和POI

1.3 拓展

Spring Boot与Spring Cloud

  • Spring Boot是spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务
  • Spring Cloud是基于Spring Boot 实现的
  • Spring Boot专注与快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架
  • Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现的,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud 离不开Spring Boot,属于依赖的关系
  • Spring Boot 在SpringCloud中起到了承上启下的作用,如果要学习Spring Cloud 必须要学习Spring Boot

2.IOC理论指导

2 .1 IOC基础

新建一个maven项目

先用之前的方式写一个项目

1.先写一个UserDao 接口

package com.jcsune.dao;
public interface UserDao {
    void getUser();
}

2.再去写Dao的实现类

package com.jcsune.dao;
public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("默认获取用户的数据");
    }
}

3.接着写UserService的接口

package com.jcsune.service;
public interface UserService {
    public void getUser();
}

4.接着写UseService的实现类

package com.jcsune.service;
import com.jcsune.dao.UserDao;
import com.jcsune.dao.UserDaoImpl;
public class UserServiceImpl  implements UserService{
    private UserDao userDao=new UserDaoImpl() ;
    public void getUser() {
        userDao.getUser();
    }
}

5.测试

import com.jcsune.service.UserService;
import com.jcsune.service.UserServiceImpl;
import org.junit.Test;
public class MyTest {
      @Test
          public void test() {
            UserService service= new UserServiceImpl();
            service.getUser();
        }
    }

6.运行结果

0KmhU1.png

7.如果我们把UserDao的实现类增加一个UserDaoMysqlImpl

package com.jcsune.dao;
public class UserDaoMysqlImpl implements UserDao {
    public void getUser() {
        System.out.println("Mysql获取用户数据");
    }
}

8.我们如果要去使用Mysql的话,我们就需要去service的实现类里面修改对应的实现

import com.jcsune.dao.UserDao;
import com.jcsune.dao.UserDaoImpl;
import com.jcsune.dao.UserDaoMysqlImpl;
public class UserServiceImpl  implements UserService{
    /*private UserDao userDao=new UserDaoImpl() ;*/
    private UserDao userDao=new UserDaoMysqlImpl();
    public void getUser() {
        userDao.getUser();
    }
}

9.测试结果(这时测试代码不用修改)

0KuAyD.png

接着如果我们要使用Oracle,又需要去service实现类里面修改对应的实现万一需求大的话,这种方式根本不适用,耦合性太高,牵一发儿动全身

10.解决方法

  • 首先新增一个UserDao的实现类UserDaoOracleImpl
package com.jcsune.dao;
public class UserDaoOracleImpl implements UserDao {
    public void getUser() {  
    }
}
  • 我们在需要用到它的地方,不去实现它,而是留出一个接口,利用set
package com.jcsune.service;
import com.jcsune.dao.UserDao;
import com.jcsune.dao.UserDaoImpl;
import com.jcsune.dao.UserDaoMysqlImpl;
public class UserServiceImpl  implements UserService{
    /*private UserDao userDao=new UserDaoImpl() ;*/
    /*private UserDao userDao=new UserDaoMysqlImpl();*/
    private UserDao userDao;
    //利用set实现
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void getUser() {
        userDao.getUser();
    }
}
  • 最后去测试类中进行测试(此时测试代码需要更改)
import com.jcsune.dao.UserDaoOracleImpl;
import com.jcsune.service.UserService;
import com.jcsune.service.UserServiceImpl;
import org.junit.Test;
public class MyTest {
      @Test
          public void test() {
            UserService service= new UserServiceImpl();
            service.setUserDao(new UserDaoOracleImpl());
            service.getUser();
        }
    }

0KQWhn.png

没使用set之前,所有的东西都是由程序去进行控制创建,而使用set之后由我们自行控制创建对象,把主动权交给了调用者,程序不用去管怎么创建,怎么实现的了,它只负责提供一个接口,这种思想从本质上解决了问题,程序员不用去管对象的创建的了,更多的去关注业务的实现,耦合性大大降低,这也就是IOC的原型

2.2 IOC本质

控制反转 IoC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法,在没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序里,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓的控制反转就是获得依赖对象的方式反转了

IOC是Spring框架的核心内容,使用多种方式完美实现了IOC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时在从Ioc容器中取出需要的对象

0K1jOK.png

采用XML方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入

3.HelloSpring

3.1 HelloSpring

新建一个maven项目,导入jar包

注:Spring需要导入commons-logging进行日志记录,我们利用maven,它会自动下载对应的依赖项

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>

1.编写一个Hello实体类

package com.jcsune.pojo;
public class Hello {
    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("Hello,"+name);
    }
}

2.编写spring文件,这里我们可以命名为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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
    <bean id="hello" class="com.jcsune.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>
</beans>

3.进行测试

import com.jcsune.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
    public static void main(String[] args) {
        //解析beans.xml文件,生成管理对应的Bean对象
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       //getBean:参数即为spring配置文件中bean的id
        Hello hello=(Hello) context.getBean("hello");
        hello.show();
    }
}

4.结果

0KN3c9.png

思考

  • Hello对象是由谁创建的? 答:Hello对象是由Spring创建的
  • Hello对象的属性是怎么设置的? 答:hello对象的属性是由Spring容器设置的,这个过程就叫控制反转
  • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
  • 反转:程序本身不创建对象,而变成被动的接收对象

依赖注入:就是利用set方法来进行注入的

IoC是一种编程思想,由主动的编程变成被动的接收

3.2 IoC创建对象的方式

通过无参构造方法来创建

  1. User.java
package com.jcsune.pojo;

public class User {
    private String name;
    public User(){
        System.out.println("user无参构造方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name="+name);
    }
}
  1. 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.jcsune.pojo.User">
        <property name="name" value="jcsune"/>

    </bean>

</beans>
  1. 测试类
import com.jcsune.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       //在执行getBean的时候,user已经创建好了,通过无参构造
       User user = (User)context.getBean("user");
       //调用对象的方法
        user.show();
    }

}

  1. 结果

0KUQVP.png

通过有参构造方法来编写

  1. UserT.java
package com.jcsune.pojo;

public class UserT {
    private String name;
    public UserT(String name){
        this.name=name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name="+name);
    }
}

  1. bean.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="user" class="com.jcsune.pojo.User">-->
<!--    <property name="name" value="jcsune"/>-->

<!--</bean>-->
<!--    第一种根据index参数下标设置-->
    <bean id="userT" class="com.jcsune.pojo.UserT">
<!--        index指构造方法,下标从0开始-->
        <constructor-arg index="0" value="jcsune2"/>

    </bean>
<!--    第二种根据参数名字来设置-->
<bean id="userT" class="com.jcsune.pojo.UserT">
<!--    name指参数名-->
    <constructor-arg name="name" value="jcsune2"/>
</bean>
<!--    第三种根据参数类型设置-->
    <bean id="userT" class="com.jcsune.pojo.UserT">
        <constructor-arg type="java.lang.String" value="jcsune2"/>
    </bean>

</beans>
  1. 测试
 @Test
    public void testT(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserT userT=(UserT)context.getBean("userT");
        userT.show();
    }
  1. 结果

0KUTRe.png

3.3 Spring配置

1. 别名

alias 设置别名,为bean设置别名,在获取bean的时候可以使用别名获取

 <alias name="userT" alias="ccsune"/>

2.Bean的配置

<!--bean就是java对象,由Spring创建和管理-->
<!--
   id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
   如果配置id,又配置了name,那么name是别名
   name可以设置多个别名,可以用逗号,分号,空格隔开
   如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
   <property name="name" value="Spring"/>
</bean>

3.import

团队的合作使用import来实现,它可以把多个配置文件合并为一个

<import resource="{path}/beans.xml"/>

4.依赖注入

4.1 概念

  • 依赖注入(Dependency Injectiojn, DI)
  • 依赖:指Bean对象的创建依赖于容器。 Bean对象的依赖资源
  • 注入: 指Bean对象所依赖的资源,由容器来设置和装配

4.2 构造器注入

见 3.2 IoC创建对象的方式

4.3 Set注入(重点)

要求被注入的属性,必须有set方法,set方法的方法名是由set+属性首字母大写,如果属性是boolean类型,没有set方法。是is

测试类:

Address.java

package com.jcsune.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

Student.java

package com.jcsune.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.getAddress() +
                ", books=" + Arrays.toString(books) +
                ", hobbies=" + hobbies +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="addr" class="com.jcsune.pojo.Address">
        <property name="address"  value="河南"/>
    </bean>

    <bean id="student" class="com.jcsune.pojo.Student">
<!--    常量注入-->
    <property name="name" value="小明"/>
<!--    Bean 注入-->
    <property name="address" ref="addr"/>
<!--    数组注入 -->
    <property name="books">
        <array>
            <value>西游记</value>
            <value>水浒传</value>
            <value>红楼梦</value>
        </array>
    </property>
<!--    List注入    -->
    <property name="hobbies">
        <list>
            <value>听歌</value>
            <value>打游戏</value>
        </list>
    </property>
<!--    Map注入-->
     <property name="card">
         <map>
             <entry key="中国邮政" value="5487745887"/>
             <entry key="建设银行" value="4448785884"/>
         </map>
     </property>
<!--    Set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
            </set>
        </property>
<!--     Null注入-->
        <property name="wife"><null/></property>
<!--     Properties注入-->
        <property name="info">
            <props>
                <prop key="学号">15401700214</prop>
                <prop key="性别"></prop>
                <prop key="姓名">小明</prop>
            </props>

        </property>
</bean>


</beans>

测试类

import com.jcsune.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student=(Student)context.getBean("student");
        System.out.println(student.toString());

    }
}

运行结果:

0KajX9.png

4.5 p命名和c命名注入

User.java

package com.jcsune.pojo;

public class User {
    private String name;
    private int age;

    public User() {

    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int id) {
        this.age= age;
    }

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

beansuser.xml

0Kd09U.png

c就是所谓的构造器注入

测试:

public void testT(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beansuser.xml");
        User user=(User)context.getBean("user");
        System.out.println(user.toString());
        User user2=(User)context.getBean("user2");
        System.out.println(user2.toString());
    }

运行结果:

0Kd6BR.png