第6天学习打卡(Spring:简介、IOC理论基础、HelloSpring、IOC创建对象的方式、Spring配置、依赖注入、引用的Bean的自动装配、使用注解开发注册bean和属性注入)

1.Spring

image-20210708165204417

1.1 简介

  • Spring:春天 ==>给软件行业带来了春天
  • 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架
  • 2004年,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版
  • Rod Jahnson是悉尼大学的音乐学博士,专业不是计算机
  • Spring理念:使现有技术更加实用,本身就是一个大杂烩,整合现有的框架技术

官网:https://spring.io

官方下载地址:repo.spring.io

GitHub:https://github.com/spring-projects

Maven包:

  • 使用Spring

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.5version>
dependency>
  • Spring整合jdbc

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.3.5version>
dependency>

1.2 优点

  • Spring是一个开源免费的框架,容器
  • Spring是一个轻量级的框架,非侵入式的
  • 核心特点:控制反转IOC,面向切面AOP
  • 对事务支持,对框架整合的支持

一句话概括:Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器(框架)

1.3 组成

image-20210708170618437

Spring框架是一个分层架构,由7个定义良好的模块组成。

Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean的方式。

image-20210708170814558

组成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、Tiles、iText和POI。

1.4 拓展

Spring Boot:

  • 一个快速开发的脚手架;
  • 基于Spring Boot可以快速开发单个微服务;
  • 使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置;

Spring Cloud:

  • Spring Cloud是基于Spring Boot实现的;
  • Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
  • Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
  • Spring Boot在Spring Cloud中起到了承上启下的作用,学习Spring Cloud必须学习Spring Boot.

image-20210708175844957

Spring弊端:发展了太久之后,违背了原来的理念!配置十分的繁琐,人称:“配置地狱!”

2.IOC理论基础

新建一个空白的maven项目

2.1分析实现

1.原来的MVC方式写一段代码

(1)UserDao接口(dao层)

public interface UserDao {
    public void getUser();
}

(2)UserDaoImpl实现类

public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("获取用户数据");
    }
}

(3)UserService接口(service层)

public interface UserService {
    public void getUser();
}

(4)UserServiceImpl实现类

public class UserServiceImpl implements UserService {
    //在service层关联dao层对象
    private UserDao userDao=new UserDaoOracleImpl();

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

(5)测试(控制层)

public class MyTest {
    @Test
    public void getUser(){
        UserService userService = new UserServiceImpl();
        userService.getUser();
    }
}

2.dao层增加接口,产生问题

(1)增加一个UserDao的实现类UserDaoMysqlImpl:

public class UserDaoMysqlImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("Mysql获取数据");
    }
}

紧接着我们想要使用MySql的话,我们就需要去service实现类中修改对应的实现

public class UserServiceImpl implements UserService {
    //在service层关联dao层对象,修改new对象
    private UserDao userDao=new UserDaoOracleImpl();

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

(2)再假设,我们再增加一个UserDao的实现类UserDaoOracleImpl

public class UserDaoOracleImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("Oracle获取数据");
    }
}

要想使用Oracle,也需要在service实现类中修改对应的new对象实现

public class UserDaoOracleImpl implements UserService {
    //在service层关联dao层对象,修改new对象
    private UserDao userDao=new UserDaoOracleImpl();

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

问题:

  • 如果我们有大量的类似的需求,dao层一个接口有许多实现类,用户访问就需要修改程序(service层引用的dao层实现类)

  • 每次变动都需要修改大量的代码,这种设计的耦合性太高了,牵一发而动全身

3.问题解决【重点】

我们可以在service层中用到dao层的地方不去实现它,而是留一个接口,利用set。我们去代码里修改下:

public class UserServiceImpl implements UserService {
    //在service层关联dao层对象
    private UserDao userDao;
    
    //【革命性变化】在service层中利用到的dao层接口实现类经常变化,所以不去实现它,而是预留一个接口
    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
    }
    

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

测试:

@Test
public void getUser(){
    UserServiceImpl userService = new UserServiceImpl();

    //Mysql实现
    userService.setUserDao(new UserDaoMysqlImpl());
    userService.getUser();

    //Oracle实现
    userService.setUserDao(new UserDaoOracleImpl());
    userService.getUser();
}

**区别:**已经发生了根本性的变化

  • 之前程序主动创建对象,控制权在程序员手上(如service层引用实现dao层实现类对象)
  • 使用了set注入以后,程序已经不再具有主动性,而是变成了被动的接受对象。把主动权交给了调用者,程序不用去管怎么创建,怎么实现了,它只负责提供一个接口。

image-20210708234401199

这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建了

系统的耦合性大大降低,可以更加专注的在业务上,这也就是IOC的原型!

2.2IOC的本质

  • 控制反转IOC(Inversion of Control),是一种思想(不去具体实现,而是提供set接口),DI(依赖注入)是实现IOC的一种方法;
  • 没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制;
  • 控制反转后,将对象的创建转移给第三方;
  • 控制反转(IOC)即使获取依赖对象的方式反转了。

image-20210709013532010

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

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

image-20210709013835268

  • 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的;

  • 而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。

  • 控制反转是一种通过描述(XML或者注解),并通过第三方去生产或获取特定对象的方式。

  • 在Spring中实现控制反转的是IOC容器,其实现方法时依赖注入(Dependency Injectiion,DI)

3.HelloSpring

3.1导入jar包

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


<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.5version>
dependency>

3.2 编写代码

image-20210709103008403

1.编写一个Hello实体类

package com.kuang.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


<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="hello" class="com.kuang.pojo.Hello">
        <property name="name" value="Spring"/>
    bean>

beans>

3.测试

public void test(){
    //解析beans.xml文件,生成管理相应的Bean对象
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //getBean:参数即为Spring配置文件中bean的id
    Hello hello = (Hello) context.getBean("hello");
    hello.show();
}

3.3思考

  • Hello对象时谁创建的?

    hello对象是由Spring创建的

  • Hello对象的属性是怎么设置的?

    hello对象的属性是由Spring容器设置的,这个过程就叫控制反转

  • 控制:谁来控制对象的创建,传统应用程序是由程序本身控制创建的,使用Spring后,对象是由Spring来控制创建的

  • 反转:程序本身不创建对象,而变成被动的接收对象。

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

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

可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。

3.4修改案例一

image-20210709103905278

我们在案例一中,邢增一个Spring配置文件beans.xml


<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="MysqlImpl" class="com.kuang.dao.UserDaoMysqlImpl">
    bean>
    <bean id="OracleImpl" class="com.kuang.dao.UserDaoOracleImpl">
    bean>

    <bean id="ServiceImpl" class="com.kuang.service.UserServiceImpl">
        
        
        <property name="userDao" ref="MysqlImpl"/>
    bean>

beans>

重新测试:

@Test
public void getUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
    serviceImpl.getUser();
}
  • OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改。
  • 所谓IOC,一句话搞定:对象由Spring来创建,管理,装配

3.5我的理解

  1. 最开始dao层一个接口有多个实现类,service层引用接口并具体实现了其中一个实现类,用户(test)要想变更访问不同的dao层实现类,就需要去修改service层对dao层实现类的引用,从而需要修改了程序,主动权在程序手上,当此种需求较大时,程序修改起来很繁琐。

    public class UserDaoOracleImpl implements UserService {
        //在service层关联dao层对象,修改new对象
        private UserDao userDao=new UserDaoOracleImpl();
    
        @Override
        public void getUser() {
            userDao.getUser();
        }
    }
    
  2. 第一次修改:我们将service层对dao层实现类的引用,不去具体实现它,而是将接口变成属性,预留属性set接口。用户(test)想要使用哪个dao层实现类,只需要在test层调用serviceImpl时传入对应的dao层实现类对象,主动权在用户手上

    public class UserServiceImpl implements UserService {
        //在service层关联dao层对象
        private UserDao userDao;
        
        //【革命性变化】在service层中利用到的dao层接口实现类经常变化,所以不去实现它,而是预留一个接口
        public void setUserDao(UserDao userDao){
            this.userDao=userDao;
        }
        
    
        @Override
        public void getUser() {
            userDao.getUser();
        }
    }
    
    @Test
    public void getUser(){
        UserServiceImpl userService = new UserServiceImpl();
    
        //Mysql实现
        userService.setUserDao(new UserDaoMysqlImpl());
        userService.getUser();
    
        //Oracle实现
        userService.setUserDao(new UserDaoOracleImpl());
        userService.getUser();
    }
    
  3. 使用Spring:

    • 项目中的每一个类都去beans.xml中注册
    • 每个类的属性也都在注册时传入设置(注入),包括基本类型属性和引用类型属性(引用其他的对象)
    • 在项目构建的时候,Spring框架就会把所有的类的对象都构建出来,并且整个Spring中所有的对象只保留一份
    • 在使用时(test)通过new ClassPathXmlApplicationContext("beans.xml");获得Spring创建和管理的所有对象
    • 要想修改service层中对dao层对象的引用,可以直接修改beans.xml配置文件,而无需修改程序。
    • 控制:所有对象由Spring创建和管理
    • 反转:通过beans.xml可以配置每个对象的属性及其引用的其他对象,选择权和主动权在用户手上(假设用户可以间接操作beans.xml)

<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="MysqlImpl" class="com.kuang.dao.UserDaoMysqlImpl">
    bean>
    <bean id="OracleImpl" class="com.kuang.dao.UserDaoOracleImpl">
    bean>

    <bean id="ServiceImpl" class="com.kuang.service.UserServiceImpl">
        
        
        <property name="userDao" ref="MysqlImpl"/>
    bean>

beans>

test无需修改,只需修改上面beans.xml配置文件

@Test
public void getUser() {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

   UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
   serviceImpl.getUser();
}

4.IOC创建对象方式

4.1通过无参构造方法来创建

1.实体类User

public class User {
    private String name;

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

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

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

2.Spring配置文件beans.xml


<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.kuang.pojo.User">
        <property name="name" value="kuangshen"/>
    bean>


beans>

3.断点测试

image-20210709192210036

测试结果:

image-20210709192301780

image-20210709192406102

  • 当测试类通过ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");将配置文件beans.xml加载到Spring中执行时,获得Spring的上下文环境。

    此时已经初始化了所有管理的对象,User对象已经通过无参构造初始化了。

  • 该操作中Spring就已经创建和管理了所有在beans.xml中注册配置的对象。

  • 对象属性的配置,还是通过属性对应的set方法进行的。

    删除User中setName方法后,beans.xml配置类的参数会报错。

4.2通过有参构造方法来创建

1.实体类User

public class User {
    private String name;

    //有参构造方法
    public User(String name){
        this.name=name;
        System.out.println("user有参构造方法");
    }

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

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

2.beans.xml的三种方式编写

  • 此时不能在通过初始化实体类User了,因为那只有是无参构造方法时才能创建
  • 有参构造方法,采用配置属性

根据参数名字设置【常用】,后两种不常用


<bean id="user" class="com.kuang.pojo.User">
    <constructor-arg name="name" value="kuangshen"/>
bean>

<bean id="userT" class="com.kuang.pojo.UserT">
    
    <constructor-arg index="0" value="kuangshen2"/>
bean>

<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
bean>

3.测试

image-20210709194224740

  • 同样也是Spring的context一旦获得,即配置文件加载的时候,所有的实体类都初始化了

  • 无论是通过无参构造,此时依靠set方法注入

  • 还是通过有参构造,此时通过有参构造直接注入

  • 可以某些属性通过有参构造,某些属性通过set注入

    <bean id="user" class="com.kuang.pojo.User">
        
        <constructor-arg name="name" value="kuangshen"/> 
        
        <property name="age" value="12"/>
    bean>
    

5.Spring配置

5.1别名

alias 为bean设置别名,可以设置多个别名

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


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

5.2Bean的配置


<bean id="user" class="com.kuang.pojo.User" name="u1,u2 u3;u4">
    <property name="name" value="kuangshen"/>
bean>

5.3import

  • import,一般用于团队开发使用,可以在一个配置文件中导入其他配置文件,从而合并成一个
  • beans.xml正式名为applicationContext.xml
  • 假如项目中有多个人开发,不同人负责不同的类的开发,不同的类需要注册在不同的beans.xml中,此时我们可以利用import将所有人的beans.xml合并为一个总的。
  • 使用的时候,直接使用总的配置就可以了

如:beans1.xml、beans2.xml、beans3.xml ==> applicationContext.xml

image-20210710120055842

applicationContext.xml:


<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="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>

beans>

测试:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

6.依赖注入(重点)

  • **依赖:**bean对象的创建依赖于容器
  • 注入: bean对象中的所有属性,由容器注入,简而言之,就是为对象中属性赋值

6.1构造器注入(前面已讲)

无参构造方法注入==》就是利用set方法注入

有参构造方法注入

6.2Set方法注入

set方法注入的类必须要有无参构造方法

测试对象:

Address类:

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

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

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

Student类:

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 Student() {
    }

    //有参构造
    public Student(String name, Address address, String[] books, List<String> hobbies, Map<String, String> card, Set<String> games, String wife, Properties info) {
        this.name = name;
        this.address = address;
        this.books = books;
        this.hobbies = hobbies;
        this.card = card;
        this.games = games;
        this.wife = wife;
        this.info = info;
    }

    //getter和setter

    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 String[] getBooks() {
        return books;
    }

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

    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;
    }

    //toString

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

beans.xml中类注册和属性注入

注入属性分类:普通注入value、bean注入ref、array、list、map、set、null、properties


<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.kuang.pojo.Address">
        <property name="address" value="北京"/>
    bean>

    
    <bean id="student" class="com.kuang.pojo.Student">
        
        <property name="name" value="小明"/>
        
        <property name="address" ref="address"/>

        
        <property name="books">
            <array>
                <value>红楼梦value>
                <value>西游记value>
                <value>三国演义value>
                <value>水浒传value>
            array>
        property>
        
        <property name="hobbies">
            <list>
                <value>听歌value>
                <value>敲代码value>
                <value>看小姐姐value>
                <value>看电影value>
            list>
        property>
        
        <property name="card">
            <map>
                <entry key="身份证" value="12345678"/>
                <entry key="银行卡" value="87654321"/>
            map>
        property>
        
        <property name="games">
            <set>
                <value>马里奥value>
                <value>魂斗罗value>
                <value>坦克大战value>
            set>
        property>

        
        <property name="wife">
            <null/>
        property>
        
        <property name="info">
            <props>
                <prop key="driver">20210710prop>
                <prop key="url">manprop>
                <prop key="username">rootprop>
                <prop key="password">123456prop>
            props>
        property>

    bean>

beans>

测试:

public void test(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

   Student student = (Student) context.getBean("student");
    System.out.println(student.toString());
}

测试结果:

Student{
    name='小明', 
    address=Address{address='北京'},
    books=[红楼梦, 西游记, 三国演义, 水浒传], 
    hobbies=[听歌, 敲代码, 看小姐姐, 看电影], 
    card={
        身份证=12345678, 
        银行卡=87654321
    }, 
    games=[马里奥, 魂斗罗, 坦克大战], 
    wife='null',
    info={
        password=123456,
        url=man, 
        driver=20210710, 
        username=root
    }
}

6.3拓展方式注入(p命名空间注入、c命名空间注入)

本质:

  • p命名空间注入,还是利用无参构造方法注入(实体类中需要有无参构造方法)
  • c命名空间注入,是利用有参构造方法注入(实体类中需要有有参构造方法)

1.P命名空间注入 : 需要在头文件中加上约束文件

导入约束 : xmlns:p="http://www.springframework.org/schema/p"


<bean id="student" class="com.kuang.pojo.Student" p:name="小明"/>

image-20210710124721808

2.c命名空间注入 : 需要在头文件中加上约束文件

导入约束 : xmlns:c="http://www.springframework.org/schema/c"


<bean id="student" class="com.kuang.pojo.Student" p:name="小明"/>

image-20210710125137648

属性注入小结

  • 本质上还是无参构造方法属性注入和有参构造方法注入
    • **无参构造方法注入:**即通过set方法注入,所以通过set方法注入时类中一定要有无参构造方法,但默认存在
    • **有参构造方法注入:**直接通过有参构造方法属性赋值注入,所以必须要重写有参构造方法;注意此时不要忘了显式添加一个无参构造方法,以适应set方法注入
  • p命名空间和c命名空间注入,本质上还是使用无参构造方法和有参构造方法注入

6.4bean的作用域

bean为每个类创建的对象,六种作用域:

image-20210711105732863

1.单例模式(Spring默认机制:每次从容器中getBean的时候,拿到的是同一个对象)

beans.xml:

<bean id="user" class="com.kuang.pojo.User" scope="singleton">

test:

public void test(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    User user1 = (User) context.getBean("user");
    User user2 = (User) context.getBean("user");
    System.out.println(user1==user2);  //true

}

2.原型模式(每次从容器中getBean的时候,都会产生一个新的对象)

beans.xml:

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

test:

System.out.println(user1==user2);  //false

3.其余的request、session、application这些都只能在web开发中使用到!

7.引用的Bean的自动装配

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean依赖,即某个对象中引用的其他对象不用显式注入,spring可以实现自动注入。

Spring中bean中引用bean有三种装配机制,分别是

  1. 在xml中显式配置;

  2. 在java中显式配置;

  3. 隐式的bean发现机制和自动装配(本节研究的)

    • 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
    • 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IOC/DI

    组件扫描和自动装配,使得显式的配置降低到最少。

    实际应用中不推荐使用自动装配xml配置,而是使用注解装配。

测试环境搭建(XML显示装配引用bean)

1.新建一个项目

2.新建两个实体类,Cat、Dog都有一个叫的方法

public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}

3.新建一个拥有者类People

package com.kuang.pojo;

import lombok.Data;


public class People {
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

4.编写Spring配置文件beans.xml**【在xml中显式配置引用的bean】**


<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="cat" class="com.kuang.pojo.Cat">bean>
    <bean id="dog" class="com.kuang.pojo.Dog">bean>
    <bean id="people" class="com.kuang.pojo.People">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="name" value="qinjiang"/>
    bean>
    
beans>

5.测试

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    People people = context.getBean("people",People.class);

    people.getCat().shout();
    people.getDog().shout();
}

结果正常输出,环境ok

7.1byName、byType自动装配

1.byName方式自动装配

  • autowire="byName"按名称自动装配

  • 测试:

    1.修改bean配置,增加一个属性autowire="byName"

    <bean id="cat" class="com.kuang.pojo.Cat">bean>
    <bean id="dog" class="com.kuang.pojo.Dog">bean>
    <bean id="people" class="com.kuang.pojo.People" autowire="byName">
        <property name="name" value="qinjiang"/>
    bean>
    

    2.再次测试,结果依旧成功输出

    3.如果将cat的id修改,和people属性名称不一致,此时按名称自动装配失败,空指针异常。

    因为byname规则实际上是按people属性的set方法找对应的bean去装配,此时找不到。

    <bean id="cat111" class="com.kuang.pojo.Cat">bean>
    
  • byName装配规则:

    1. 查找其类中所有引用的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat
    2. 去spring容器中寻找是否有此字符串名称id的对象
    3. 如果有,就取出注入,否则就报空指针异常

2.byType方式自动装配

  • autowire="byType"按类型自动装配

    使用时需要保证:同一类型的对象,在spring容器中唯一。否则会报不唯一异常。

    image-20210711143400467

  • 测试:

    1. 修改bean配置,增加一个属性autowire="byType"

    2. 测试,正常输出

    3. 此时被引用的bean的id名称无所谓了,可以去掉,因为是按照对象类型来寻找并自动装配的。

      <bean id="cat22" class="com.kuang.pojo.Cat">bean>
      <bean id="dog22" class="com.kuang.pojo.Dog">bean>
      <bean id="people" class="com.kuang.pojo.People" autowire="byType">
          <property name="name" value="qinjiang"/>
      bean>
      

7.2 使用注解实现自动装配

jdk1.5开始支持注解,spring2.5全面支持注解

准备工作:

  1. 在beans.xml配置文件中引入context文件头,注意学会修改方式配置文件

    xmlns:context="http://www.springframework.org/schema/context" 【注意开头加上context】
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    
  2. beans.xml中开启属性 注解支持

    <context:annotation-config/>
    

<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.kuang.pojo.Cat">bean>
    <bean id="dog" class="com.kuang.pojo.Dog">bean>
    <bean id="people" class="com.kuang.pojo.People">
        <property name="name" value="qinjiang"/>
    bean>
beans>

1.在实体类属性上采用@Autowired注解【推荐】

  • @Autowired是按照类型自动寻找匹配,不支持id名字匹配

    public class People {
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
    
        private String name;
    }
    
  • @Autowired(required=false)使用,false表示注解标记的对象可以为null,true不能为空必须有对象

    如在没有注册cat的bean时,people仍能成功注入,只是此时cat引用对象可以为空

    @Autowired(required = false)
    private Cat cat;
    
  • 可以辅助使用@Qualifier(value = "dog2"),进而可以通过id名字匹配

    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
    

2.在实体类属性上采用@Resource注解

  • @Resource:如有指定name属性,按照先按照指定id名称查找匹配

    @Resource(name = "dog2")
    private Dog dog;
    
  • 再进行默认的byName方式查找装配

    @Resource
    private Dog dog;
    
  • 以上都不成功,则按byType方式自动装配

@Autowired与@Resource异同:

  • @Autowired:
    • 默认按类型装配(属于spring规范)
    • 默认情况下必须要求依赖对象存在,如果要允许null值,可以设置@Autowired(required=false)
    • 可以结合@Qualifier(value = “dog2”),使用按id名称查找装配
  • @Resource:
    • 默认按名称装配
    • 名称可以通过name属性进行指定
    • 如果 没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找
    • 当找不到与名称匹配的bean时才按照类型进行装配
  • 它们作用相同,都是用注解方式注入对象,实现自动装配。但执行顺序不同:@Autowired先byType,@Resource先 byName。

8.使用注解开发注册bean和属性注入

  • 在Spring4之后,要是使用注解,必须保证aop的包的导入,导入Spring-webmvc会自动导入aop包

image-20210711160206847

  • 使用注解需要导入contex约束,在增加对注解的支持

image-20210711160615613

8.1 传统注解

1.在类中采用@Component注解取代xml中的

需要在beans.xml中配置扫描哪些包下的注解

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

2.普通属性注入采用@Value

3.引用类型属性注入采用@Autowired

  1. 类对象

    Cat:

    @Component
    public class Cat {
        public void bark(){
            System.out.println("miao~");
        }
    }
    

    User:

    //类bean注册采用注解替代
    @Component
    public class User {
        //普通属性注入
        @Value("kuangshen")
        private String name;
    
        //引用类型属性 bean注入
        @Autowired
        private Cat cat;
    }
    

    等价于原来beans.xml中:

    <bean id="cat" class="com.kuang.pojo.Cat"/>
    <bean id="user" class="com.kuang.pojo.User">
        <property name="name" value="kuangshen"/>
        <property name="cat" ref="cat"/>
    bean>
    
  2. beans.xml

    
    <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.kuang.pojo"/>
    
    beans>
    
  3. 测试

    public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
        User user = (User) context.getBean("user");
        System.out.println(user.getName());
        user.getCat().bark();
    
    }
    

@Component别名:

开发中我们通常采用MVC三层架构

为了区分在不同的层中对其实现类,在Spring中进行注册装配bean

@Component有一些别名,功能都一样:都是注册bean对象

  • @Repository:dao层
  • @Service:service层
  • @Controller:controller层

写上这些注解,就相当于将这个类交给Spring管理装配了!

4.作用域注解实现@Scope

一般在具体类前@Component下跟随书写

  • singleton:默认的,Spring会采用单例模式创建这个对象
  • prototype:原型模式,多例模式
//类bean注册采用注解替代
@Component
@Scope("singleton")
public class User {
	...
}

小结 xml与注解比较

xml与注解 实现注册bean和注入属性 比较:

  • XML可以适用于任何场景,结构清晰,维护方便。适用于复杂类
  • 注解不是自己提供的类使用不了,开发方便。适用于简单类

**xml与注解 整合开发:**推荐做法

  • xml注册管理bean
  • 注解完成属性注入
  • 使用过程中,可以不用扫描,扫描是为了类上的注解

8.2采用Java配置类 取代 beans.xml配置文件

  • JavaConfig原来是基于Spring的一个子项目,它通过Java类的方式提供Bean的定义信息

  • 在Spring4的版本,JavaConfig正式成为Spring4的核心功能

  • 采用此种配置方法,可以完全摒弃beans.xml配置文件

image-20210711170724227

  1. 编写实体类User

    仍然采用注解注册bean,和进行属性注入

    //注解实现bean注入,可以不需要,因为在MyConfig配置类中注册了bean
    @Component
    public class User {
        //属性注入
        @Value("kuangshen")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
  2. 在java的com.kuang下新建一个config的包,编写一个MyConfig配置类【关键】

    //用MyConfig的Java类取代配置文件beans.xml配置文件
    @Configuration
    //注解扫描包声明
    @ComponentScan("com.kuang.pojo")
    public class MyConfig {
    
        //用Java返回函数取代beans.xml中类的bean注册。方法名:等于bean中的id
        @Bean
        public User user(){
            return new User();
        }
    }
    
  3. 测试

    需要采用new AnnotationConfigApplicationContext(MyConfig.class);获取Spring中管理的对象

    @Test
    public void test(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = (User) context.getBean("user");
        System.out.println(user.getName());
    }
    

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

你可能感兴趣的:(spring)