SSM之Spring 01 —— 第一个Spring程序、配置说明、IOC、DI、自动装配、注解/Java方式开发

系列文章

SSM之Spring 01 —— 第一个Spring程序、配置说明、IOC、DI、自动装配、注解/Java方式开发

SSM之Spring 02 —— 动态代理、AOP、Spring-MyBatis、声明式事务



1、Spring

1.1、简介

  • Spring:春天的意思,给软件行业带来春天!

  • 2002年,首次推出Spring框架的雏形:Interface21框架

  • Spring框架即以Interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版

  • Rod Johnso,Srping Framework 创始人,著名作者。他是悉尼大学的博士,然而专业不是计算机,而是音乐学,很难想象Rod Johnso的学历。

  • Srping的理念:使现有技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

    SSH:Struct2 + Spring + Hibernate

    SSM:SpringMVC +Spring + MyBatis

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-p-namespace

下载地址:https://repo.spring.io/release/org/springframework/spring/

GitHub:https://github.com/spring-projects/spring-framework/releases/tag/v5.2.13.RELEASE

maven:

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

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.3</version>
</dependency>

1.2、优点

  • Spring是一个开源的免费的框架(容器)!
  • Spring是一个轻量级的(导包即用)、非入侵式的(导包后不改变现有项目)框架!
  • 控制反转(IOC)、面向切面(AOP)
  • 支持事务的处理,对框架整合的支持!

总的来说:Spring就是一个轻量级的控制反转(IOC)和面向切面(AOP)的框架!
 

1.3、组成

在这里插入图片描述
 

1.4、拓展

在这里插入图片描述

现代化的Java开发,就是基于Spring的开发!!

  • SpringBoot:一个快速开发的脚手架,基于SpringBoot能快速开发单个微服务,和maven一样也是约定大于配置。
  • SpringCloud:基于SpringBoot实现的,用于协调。

现在大多数公司都在使用SpringBoot开发,而学习SpringBoot的前提,需要完全掌握Spring和SpringMVC。

Spring配置十分繁琐,违背了原本的理念,人称"配置地狱",直到SpringBoot,配置简化。

2、IOC理论推导

IOC原型

考虑下面情形,当我们业务层实现类ServiceImpl调用DAO层时,如果用户需求发生改变,一开始是默认调用,后面用MySQL,再后面用Oracle,那么我们就会不断修改代码,这显然是不合理的。

//---------DAO层------
public interface UserDao {
    
    
    public void getUser();
}

public class UserDaoImpl implements UserDao {
    
    

    public void getUser() {
    
    
        System.out.println("默认的getUser");
    }
}

public class UserDaoMySQLImpl implements UserDao {
    
    
    public void getUser() {
    
    
        System.out.println("MySQL的getUser");
    }
}

public class UserDaoOracle implements UserDao {
    
    
    public void getUser() {
    
    
        System.out.println("Oracle的getUser");
    }
}

//--------service层----------
public interface UserService {
    
    
    public void getUser();
}

public class UserServiceImpl implements UserService {
    
    
    //这部分会不断修改
    private UserDao userDao = new UserDaoImpl();
    //private UserDao userDao = new UserDaoMySQLImpl();
    //private UserDao userDao = new UserDaoOracle();

    public void getUser() {
    
    
        userDao.getUser();
    }
}

改进方法:我们通过set来对UserDao接口进行注入。

public class UserServiceImpl implements UserService {
    
    
    //这部分会不断修改
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
    
    
        this.userDao = userDao;
    }

    public void getUser() {
    
    
        userDao.getUser();
    }
}

革命性变化

在这里插入图片描述

  • 之前,程序是主动创建对象,控制权在程序员手上!
  • 使用set注入后,程序不再具有主动性,而是变成被动的接受对象。
  • 控制权从程序员手上到用户手上,这就是控制反转。
  • 这种思想,从本质上解决了问题,使得程序员不再去管理对象的创建,系统的耦合性大大降低,可以更加专注在业务的实现上,这就是IOC的原型。

IOC本质

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

控制反转(Inversion of Control)是一种设计思想,依赖注入(DI)是实现IoC的一种方式,所谓控制反转就是获得依赖对象的方式反转了。

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

3、Hello Spring

1、第一个Spring程序

项目结构:

1614078001684

1、创建实体类Hello.java

package com.zcy.pojo;

public class Hello {
    
    
    private String str;

    public void setStr(String str) {
    
    
        this.str = str;
    }

    public String getStr() {
    
    
        return str;
    }

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

2、在resources目录下新建一个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">
    
    <!-- 
        现在beans.xml就是一个容器,存放各种对象
        这里控制反转,由Spring帮我们创建对象,而不是程序,对象创建只需要通过配置文件操作
        bean标签相当于帮我们创建了对象,等价于
        Hello hello = new Hello(),id就是变量名,class是对象类型
        property标签相当于帮我们给hello对象的属性 str 赋值为 Hello World
     -->
    <bean id="hello" class="com.zcy.pojo.Hello">
        <property name="str" value="Hello World"/>
    </bean>

</beans>

3、测试

import com.zcy.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //加载配置文件,拿到容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //这时没有new,对象已经被Spring创建好了,
        //由Spring托管的类,都可以通过这种方式获取Spring创建好的对象
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.getStr());
    }
}

结果:

在这里插入图片描述

当我们的类由Spring托管时,左边看得到叶子标记
在这里插入图片描述
 

2、修改之前IOC原型

添加一个Spring配置文件,将三个DAO实现类和一个Service实现类交给Spring。

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

    <!-- 三个DAO实现类,它们都没成员变量,无需property标签    -->
    <bean id="userDaoImpl" class="com.zcy.dao.UserDaoImpl"/>
    <bean id="userDaoMySQLImpl" class="com.zcy.dao.UserDaoMySQLImpl"/>
    <bean id="userDaoOracle" class="com.zcy.dao.UserDaoOracle"/>

    <!-- service的实现类,有一个UserDao成员变量,需要property标签    -->
    <bean id="userServiceImpl" class="com.zcy.service.UserServiceImpl">
        <!-- 用ref引用Spring已经托管对象,以后userServiceImpl修改成员只需在配置文件改  -->
        <property name="userDao" ref="userDaoImpl"/>
    </bean>
</beans>

如果有不同需求,只需修改配置文件,而测试代码永远不变!

import com.zcy.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestIOC {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
        userServiceImpl.getUser();
    }
}

3、思考问题?

  • Hello对象是谁创建的?hello对象在我们获得容器时就被Spring创建。
  • Hello对象的属性怎么设置?由Spring容器(beans.xml)设置

这个过程就叫控制反转:

  1. 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制的,使用Spring后,对象是由Spring来创建的;
  2. 反转:程序本身不创建对象,而变为被动的接收对象;
  3. 依赖注入:就是利用set方法来注入的(如果我们去掉Hello中的set方法,Spring就无法生效)
  4. IOC是一种编程思想,由主动的编程变为被动的接收

OK,到了现在,我们彻底不用再去程序中改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话,就是对象由Spring创建、管理、分配!

4、IOC创建对象的方式

1、默认使用无参构造创建对象!

2、使用有参构造:

//自己写好对应的set\get,有参构造、无参构造,下面三个注解是Lombok,不用写
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private String name;
    private String password;
}
<?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.zcy.pojo.User">
        <constructor-arg name="name" value="小白"/>
        <constructor-arg name="password" value="111111"/>
    </bean>
    <!-- 方式二:通过类型匹配来设置,但所有同类型都会被赋值,不推荐-->
    <bean id="user" class="com.zcy.pojo.User">
        <constructor-arg type="java.lang.String" value="小白"/>
    </bean>
    <!-- 方式三:通过下标来设置    -->
    <bean id="user" class="com.zcy.pojo.User">
        <constructor-arg index="0" value="小红"/>
        <constructor-arg index="1" value="333333"/>
    </bean>
</beans>

配置文件加载时,容器(ApplicationContext)中管理的对象就已经初始化了。

5、Spring配置说明

5.1、Alias 别名

为bean标签中id起别名

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

调用时

User user = (User) context.getBean("u1");

5.2、Bean的配置

id是bean的唯一标识,相当于对象名;class是bean对象对应的全限定名(包名+类型);name也是别名,而且比Alias更强大,不仅可以起多个别名,它的分隔符也很随意。

<bean id="user" class="com.zcy.pojo.User" name="u2 u3,u4;u5">
    <constructor-arg name="name" value="小白"/>
    <constructor-arg name="password" value="111111"/>
</bean>

5.3、import

import一般用于团队使用,它能将其他配置文件导入合并。假设现在项目由多个人开发,每个人负责不同的类,而不同的类就需要注册在不同的bean中,我们可以利用import将所有人的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">

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

6、依赖注入

依赖注入:

  • 依赖:bean对象的创建依赖于容器
  • 注入:bean对象中所有属性由容器注入

6.1、构造器注入

就是前面有参构造的方法
 

6.2、Set方式注入(重点)

【实体类】

1、复杂类型

public class Address {
    
    
    private String address;
    ...实现get/set方法
}

2、真实测试对象

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 wifi;
    private Properties info;

  ....实现get、set方法
}

3、set注入方式

<?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="address" class="com.zcy.pojo.Address">
        <property name="address" value="湖南长沙"/>
    </bean>

    <bean id="student" class="com.zcy.pojo.Student">
        <!-- 基本类型,普通注入 -->
        <property name="name" value="李华"/>
        
        <!-- 注入Spring托管的类,bean注入   -->
        <property name="address" ref="address"/>
        
        <!-- 数组注入  -->
        <property name="books">
            <array>
                <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="111111222222223333"/>
                <entry key="学生卡" value="11122223333"/>
            </map>
        </property>
        
        <!-- Set注入  -->
        <property name="games">
            <set>
                <value>英雄联盟</value>
                <value>皇室战争</value>
            </set>
        </property>
        
        <!-- null值注入        -->
        <property name="wifi">
            <null/>
        </property>
        
        <!-- Properties注入,配置文件  -->
        <property name="info">
            <props>
                <prop key="driver">jdbc:mysql://localhost:3306/mybatis</prop>
                <prop key="url">useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false</prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>

</beans>

6.3、扩展方式注入

需要导入xml约束(放在最上面的beans标签里)

xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"

p命名空间等价于property,c命名空间等价于构造器注入,但主要还是用前面Set注入。

<!-- p 命名空间注入-->
<bean id="student3" class="com.zcy.pojo.Student"
      p:name="小白" p:address-ref="address"/>
<!-- c 命名空间注入    -->
<bean id="address2" class="com.zcy.pojo.Address"
      c:address="北京"/>

6.4、Bean的作用域

在这里插入图片描述

1、singleton(默认):单例模式,每次从容器中get到的Bean都是同一个对象

<bean id="address" class="com.zcy.pojo.Address" scope="singleton">
    <property name="address" value="湖南长沙"/>
</bean>

2、prototype:原型模式,每次从容器中get到的Bean都不是同一个对象

<bean id="address" class="com.zcy.pojo.Address" scope="prototype">
    <property name="address" value="湖南长沙"/>
</bean>

3、request、session、application等都是Web开发才用

@Test
public void test() {
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Address address1 = context.getBean("address", Address.class);
    Address address2 = context.getBean("address", Address.class);
    System.out.println(address1 == address2);
    //单例结果为true,原型结果为false
}

7、Bean的自动装配

  • 自动装配是Spring满足Bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给Bean装配属性!

Spring有三种装配方式:

  1. xml装配,就是之前的那些方式。
  2. java装配,比较麻烦。
  3. 隐式自动装配【重要】

7.1、ByName 自动装配

设置自动装配autowire属性为byName后,people1会在容器中上下文查找和自己对象set方法后面的值对应的Bean id。

通俗的说,就是会根据自己setCat和setDog方法,找ID为cat和dog的Bean。因此需要保证所有Bean的id唯一,并且这个Bean要和自动注入的属性的set方法名一致!(例如 setCat233就无法找到Cat)

<bean id="cat" class="com.zcy.pojo.Cat"/>
<bean id="dog" class="com.zcy.pojo.Dog"/>
<bean id="people1" class="com.zcy.pojo.People" autowire="byName"/>

7.2、ByType 自动装配

设置自动装配autowire属性为byType后,people2会在容器中上下文查找和自己对象属性类型相同的Bean。

通俗的说,就是会找class为Cat和Dog的Bean。因此需要保证所有Bean的class唯一,这个Bean要和自动注入的属性的类型一致。

<bean id="cat" class="com.zcy.pojo.Cat"/>
<bean id="dog" class="com.zcy.pojo.Dog"/>
<bean id="people1" class="com.zcy.pojo.People" autowire="byName"/>

7.3、注解实现自动装配

需要先导入context命名空间以及注解标签:

增加的内容:

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

这两行是在引号内的
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd

<context:annotation-config/>

完整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"
       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">

        <context:annotation-config/>
        <bean id="cat" class="com.zcy.pojo.Cat"/>
        <bean id="dog1" class="com.zcy.pojo.Dog"/>
        <bean id="dog2" class="com.zcy.pojo.Dog"/>
        <bean id="people" class="com.zcy.pojo.People"/>
</beans>

在People类上使用注解:

public class People {
    
    
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    @Qualifier("dog1")
    private Dog dog;

    public void setDog(@Nullable Dog dog) {
    
    
        this.dog = dog;
    }

    public Cat getCat() {
    
    
        return cat;
    }

    public Dog getDog() {
    
    
        return dog;
    }
}
  • 注解@Autowired:等同于bean中设置属性 autowire=byType,即默认按类型自动装配。该注解的参数required = false,表明该变量可以为null值,等价于在set方法的参数里添加注解@Nullable。

  • 注解@Autowired可以写在成员变量或者set方法上,如果写在成员变量上,则可以省略对应的set方法。

  • 注解@Qualifier("")常搭配@Autowired,由于@Autowired是默认按类型查找,当出现相同class时,用@Qualifier("")指定bean的id(相当于按类型查找失败,就按名字查找)

  • @Resource是Java内置的注解[但依然需要导入context],是@Autowired和@Qualifier的结合,它先通过名字查再通过类型查。但我们一般还是用的@Autowired。

8、使用注解开发

在Spring4之后,要使用注解开发,首先必须要保证AOP的包导入了,其次要导入context约束,增加注解支持!

在这里插入图片描述

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

    <!-- 加载注解驱动,写了这个才能用注解(针对所有范围) -->
    <context:annotation-config/>

    <!-- 指定要扫描的包,这个包下的注解才会生效  -->
    <context:component-scan base-package="com.zcy.pojo"/>
</beans>

1、Bean

放在类上

//这里可以看成等价于<bean id="user" class="com.zcy.pojo.User"/>
//为了让Spring扫描,以便注册该bean
@Component

@Component的衍生:我们在Web开发中,常按照MVC三层架构进行分层,所以在不同层@Component的名字不一样,但它们效果是相同的,都是代表将某个类注册到Spring中,装配Bean。

  • Dao层,用@Repository
  • Service层,用@Service
  • Controller层,用@Controller

2、属性赋值

在属性上加@Value(“江小白”),但一般也就给普通类型赋值,它无法引用其他类。

@Component
@Scope("singleton")
public class User {
    
    
    @Value("江小白")
    private String userName;

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public String getUserName() {
    
    
        return userName;
    }
}

3、自动装配

  • @AutoWired:通过类型自动装配,如果AutoWired不能识别,需要加上@Qualifier(value=“xxx”)
  • @Nullable:方法的参数前加这个注解,表明可以为null
  • @Resource:自动先通过名字,再通过类型查找。

4、作用域

@Scope("singleton")//在类上写

5、小结

xml与注解:

  • xml更加万能,适用于各种场合,维护简单方便!

  • 注解,无法引用其他类,维护相对复杂。

  • xml与注解的实践:

    • xml用来管理Bean,里面就写Bean标签最好。

    • 注解只负责完成属性注入,但碰到复杂的属性最好也都在xml完成。

    • 使用过程中一定要注意加入下面代码才能使用注解!!

      <!-- 加载注解驱动,写了这个才能用注解(针对所有范围) -->
      <context:annotation-config/>
      
      <!-- 可选项:指定要扫描的包,这个包下的注解就会生效  -->
      <context:component-scan base-package="com.zcy"/>
      

9、使用Java方式配置Spring

我们现在可以完全不适用Spring的XML配置,全权交给Java来做。

JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!

在SpringBoot中,这种纯Java方式配置随处可见!

实体类 User.java

@Component//这个注解表明,该类由Spring接管,注册到容器中
public class User {
    
    
    @Value("小明")//属性注入
    private String name;
	...get/set方法
}

配置类 MyConfig.java

//@Configuration本质也是@Component,由Spring接管
//同时@Configuration还表明当前类替代了配置文件beans.xml
@Configuration
//扫描包,等价于<context:component-scan base-package="com.zcy"/>
@ComponentScan("com.zcy")
@Import(MyConfig2.class)//导入其他配置类
public class MyConfig {
    
    
    //注册一个bean,相当于之前我们写的bean标签
    //方法名就是id属性,返回值就是class属性
    @Bean
    public User getUser(){
    
    
        return new User();//返回的对象就被注入到bean中
    }
}

测试类 ConfigTest.java

public class ConfigTest {
    
    
    @Test
    public void test(){
    
    
        // 如果完全使用配置类去做,我们就不用ClassPathXmlApplicationContext(加载配置文件),
        // 而用AnnotationConfigApplicationContext(加载配置类)
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        //这里的getUser就是相当于id属性
        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39763246/article/details/114021379
今日推荐