Spring学习笔记1:IOC本质和依赖注入

文章目录

1、IOC本质

​ **控制反转loC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现loC的一种方法,**也有人认为DI只是loC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
​ 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的.
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

1.1、拓展:关于Spring Boot 和 Spring Cloud

  • Spring Boot

    • 一个快速开发的脚手架。
    • 基于SpringBoot可以快速开发单个微服务。
    • 约定大于配置
  • Spring Cloud

    • Spring Cloud是基于SpringBoot实现的

    大多数公司都使用SpringBoot快速开发,学习SpringBoot的前提,是需要完全掌握Spring及SpringMVC!承上启下的作用!

2、HelloSpring

ApplicationContext就是一个百宝箱

​ ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了:Spring把Bean放在这个容器中,在需要的时候,用getBean方法取出。

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
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--使Hspring来创建对象,在:spring这些都成为Bean
    类型变量名= new类型();
    Hello hello = new Hello();
    id =变量名
    class = new的时象:
    property相当于始对象中的属性设置一个值! |
    -->
    <bean id="hello" class="com.hjt.pojo.Hello" >
        <!--ref: 引用Spring容器中创建好的对象
        value :具体的值。基本数据类型!
        -->
        <property name="str" value="Spring"/>
    </bean>
    <bean id="UserServiceImpl2" class="com.hjt.service.UserServiceImpl">
        <property name="userdao" ref="Sqlserver"/>
    </bean>
    
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl UserServiceImpl = (com.hjt.service.UserServiceImpl) context.getBean("UserServiceImpl");
UserServiceImpl.getUser();

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());

3、IOC创建对象的方式

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

​ 必须有无参构造,不然会报错!

<bean id="hello" class="com.hjt.pojo.Hello" >
    <property name="str" value="Spring"/>
</bean>

2、使用有参构造创建方法:

public User(String name) {
    this.name = name;
    System.out.println("User的有参构造");
}
//第一种,下标赋值
    <bean id="user" class="com.hjt.pojo.User">
        <constructor-arg index="0" value="何*~"/>
    </bean>
//第二种,通过类型创建,不建议使用。
	<bean id="user" class="com.hjt.pojo.User">
		<constructor-arg type="java.lang.String" value="何*三号"/>
	</bean>
//第三种,直接通过参数名创建bean
	<bean id="user" class="com.hjt.pojo.User">
		<constructor-arg name="name" value="何*四号"/>
	</bean>

​ 只要有bean,扫描xml之后容器中所有的对象都被实例化都被创建对象了,并且只创建一个实例,new几个相同的user都是一样的。

User user = (User)context.getBean("user");
User user2 = (User)context.getBean("user");
user==user2---------->true

4、Spring配置

4.1、别名

<bean id="user" class="com.hjt.pojo.User">
    <constructor-arg name="name" value="何*四号"/>
</bean>

<alias name="user" alias="ssasasa"/>
User user = (User)context.getBean("user");
user.show();
User user1 = (User)context.getBean("ssasasa");
user1.show();
//User的有参构造
//name=何*四号
//name=何*四号

4.2、Bean的配置

id:bean的唯一标识符,也就是相当于我们学的对象名

class:bean对象所对应的全限定名:包名+类型

name:也是别名

<bean id="userT" class="com.hjt.pojo.User" name="user2,u2"><!--,和空格和;分隔都可以-->

</bean>

User user1 = (User)context.getBean("user2");
user1.show();
name=null
User user1 = (User)context.getBean("u2");
user1.show();
name=null

4.3、import

import一般用于团队开发使用,可将多个配置文件,导入合并一个。

<import resoure="bean.xml"/> 

4.4、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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  (1) (2)
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

5、DI依赖注入

5.1、构造器注入

​ 前面已经说过

5.2、Set方式注入【重点】

1.依赖注入:set注入!

​ *依赖:bean对象的创建依赖于容器!

​ *注入:bean对象中的所有属性,由容器来注入!

【环境搭建】

1.复杂类型

public class Address {
    private String addr;

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
    @Override
    public String toString() {
        return "Address{" +
            "addr='" + addr + '\'' +
            '}';
    }
}

2.真实测试对象

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

3.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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="address" class="com.atctbu.hjt.Address">
        	<property name="addr" value="重庆"/>
    	</bean>
    	<bean id="student" class="com.atctbu.hjt.Student">

   		<!--第一种,普通值注入,value-->
        <property name="name" value="何*"/>

        <!--第二种,Bean注入,因为是引用型,所以是ref-->
        <property name="address" ref="address"/>

        <!--第三种,数组注入-->
        <property name="books">
            <array>
                <value>Java从入门到入土</value>
                <value>Web从入门到入土</value>
                <value>Spring从入门到入土</value>
            </array>
        </property>

        <!--第四种,List注入-->
        <property name="hobbys">
            <list>
                <value>敲代码</value>
                <value>听歌</value>
                <value>看电影</value>
            </list>
        </property>

        <!--第四种,Map注入-->
        <property name="card">
            <map>
                <entry key="身份证" value="123456789"/>
                <entry key="银行卡" value="987654321"/>
            </map>
        </property>

        <!--第五种,Set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
                <value>CS</value>
            </set>
        </property>

        <!--第六种,null值注入-->
        <property name="wife">
            <null/>
        </property>

        <!--第七种,properties注入
        key=value
        -->
        <property name="info">
            <props>
                <prop key="姓名">何*</prop>
                <prop key="学号">2018131224</prop>
                <prop key="性别">男</prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
</beans>

4.测试类

public class Mytest {
    public static void main(String[] args) {
        ApplicationContext content = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student)content.getBean("student");
        System.out.println(student.getName());
        System.out.println(student.toString());
    }
}

/*何*
Student{name='何*', 
address=Address{addr='重庆'}, 
books=[Java从入门到入土, Web从入门到入土, Spring从入门到入土], 
hobbys=[敲代码, 听歌, 看电影], 
card={身份证=123456789, 银行卡=987654321}, 
games=[LOL, CF, CS], wife='null', 
info={学号=2018131224, 性别=男, password=123456, 姓名=何*, username=root}}
*/

5.3、拓展方式注入

1.p命名空间注入:xmlns:p=“http://www.springframework.org/schema/p”

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
	<!--p命令注入可以直接注入属性的值:property-->
    <bean id="user" class="com.atctbu.hjt.User" p:name="何*" p:age="21"/>

</beans>
@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user",User.class);
    System.out.println(user);
}

User{name='何*', age=21}

2.c命名空间注入:xmlns:c=“http://www.springframework.org/schema/c”

<!--p命令注入可以直接注入属性的值:property-->
<bean id="user" class="com.atctbu.hjt.User" p:name="何*" p:age="21"/>
<!--c命令注入可以通过构造器(有参构造)注入属性的值:construct-args-->
<bean id="user2" class="com.atctbu.hjt.User" c:name="张三" c:age="18"/>
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user",User.class);
    User user2 = context.getBean("user2",User.class);
    System.out.println(user);
    System.out.println(user2);
}
User{name='何*', age=21}
User{name='张三', age=18}

5.4、Bean作用域

​ 创建 bean 定义时,将创建一个配方来创建该 bean 定义所定义的类的实际实例。 bean 定义是配方的想法很重要,因为它意味着与类一样,您可以从一个配方中创建许多对象实例。

​ 您不仅可以控制要插入到从特定 bean 定义创建的对象中的各种依赖项和配置值,还可以控制从特定 bean 定义创建的对象的范围。这种方法功能强大且灵活,因为您可以选择通过配置创建的对象的范围,而不必在 Java 类级别上烘烤对象的范围。可以将 Bean 定义为部署在多个范围之一中。 Spring 框架支持六个范围,其中只有在使用网络感知ApplicationContext时才可用。您也可以创建[自定义范围]。

Scope Description
singleton (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype 将单个 bean 定义的作用域限定为任意数量的对象实例。
request 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session 将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application 将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket 将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。

**1.单例模式(Spring默认机制)**单线程使用得多

<bean id="user2" class="com.atctbu.hjt.User" c:name="张三" c:age="18" scope="singleton"/>
User user = context.getBean("user",User.class);
User user2 = context.getBean("user2",User.class);
System.out.println(user==user2);
true

**2.原型模式:每次从容器中get 的时候都会产生一个新对象!**多线程使用得多

<bean id="user2" class="com.atctbu.hjt.User" c:name="张三" c:age="18" scope="prototype"/>
User user = context.getBean("user",User.class);
User user2 = context.getBean("user2",User.class);
System.out.println(user==user2);
false

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

6、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式!

  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种装配的方式

1.在xml中显示配置

2.在java中显示配置

3.隐式的自动装配bean【最重要的】

6.1、测试

1.环境搭建:一个人有两个宠物:

<?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="cat" class="com.atctbu.hjt.Cat"/>
    <bean id="dog" class="com.atctbu.hjt.Dog"/>

    <bean id="people" class="com.atctbu.hjt.People">
        <property name="name" value="何*"/>
<!--        引用型:ref-->
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
</beans>
public class People {
    private Cat cat;
    private Dog dog;
    private String name;
public class mytest {
    @Test
    public void test1(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");

        People people = Context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();
    }
}
miao~
wang~

6.2、ByName和ByType自动装配

**byName:**会自动在容器上下文寻找,和自己对象set方法后面的值对应的beanid

必须保证所有bean 的id唯一,并且这个bean需要和自动注入的属性的set方法一致!

    <bean id="cat" class="com.atctbu.hjt.Cat"/>
    <bean id="dog" class="com.atctbu.hjt.Dog"/>
<!--    byName:会自动在容器上下文寻找,和自己对象set方法后面的值对应的beanid-->
    <bean id="people" class="com.atctbu.hjt.People" autowire="byName">
        <property name="name" value="何*"/>
    </bean>
还是能找到显示出
miao~
wang~
--------------------------------------------------------------------------
    <bean id="cat" class="com.atctbu.hjt.Cat"/>
    <bean id="dog222" class="com.atctbu.hjt.Dog"/>
<!--    byName:会自动在容器上下文寻找,和自己对象set方法后面的值对应的beanid-->
    <bean id="people" class="com.atctbu.hjt.People" autowire="byName">
        <property name="name" value="何*"/>
    </bean>
狗的名字被改了,就找不到了,报空指针异常

**byType:**必须保证类型全局唯一,才能自动装配

必须保证所有bean 的class唯一,并且这个bean需要和自动注入的属性的类型一致!

    <bean id="cat" class="com.atctbu.hjt.Cat"/>
    <bean id="dog222" class="com.atctbu.hjt.Dog"/>
<!--byType:会自动在容器上下文寻找,和自己对象属性类型相同的bean-->
    <bean id="people" class="com.atctbu.hjt.People" autowire="byType">
        <property name="name" value="何*"/>
    </bean>
狗的名字被改了,也能找到显示出,甚至不用命名id也可以找到
<bean class="com.atctbu.hjt.Cat"/>
<bean class="com.atctbu.hjt.Dog"/>
miao~
wang~

6.3、使用注解自动装配

JDK1.5支持注解:Spring2.5就开始支持注解了

要使用注解须知:

  • 导入约束;xmlns:context=“http://www.springframework.org/schema/context”

  • 配置注解的支持;<context:annotation-config/>

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       <!--导入context-->
       xmlns:context="http://www.springframework.org/schema/context"
		<!--导入aop-->
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
		<!--导入context-->
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
		<!--导入aop-->
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="cat" class="com.atctbu.hjt.Cat"/>
    <bean id="dog222" class="com.atctbu.hjt.Dog"/>
    <bean id="people" class="com.atctbu.hjt.People"/>
	<!--开启注解支持-->
    <context:annotation-config/>

</beans>

6.3.1、@Autowaired、@Qualifier(value="")、@Resource、@Nullable

​ 前提是名字相同,自动导入(名字不同好像也可以)

​ 直接在属性上使用即可!也可以在set方式上使用!

​ 使用Autowired我们可以不用编写Set方法了,前提是你在这个自动装配的属性在IOC容器中存在,且符合名字byname!

@Autowaired(required = false)//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空。

@Qualifier(value=“dog222”) 当使用@Autowired自动装配的环境比较复杂(有多个猫猫狗狗的时候),自动装配无法通过一个注解【@Autowired】完成的时候,使用@Qualifier(value=“dog222”),去配合Autowired的使用,给它指定一个实现的值(bean对象)

@Nullable:字段标记了这个注解,说明这个字段可以为null;

@Resource:先通过名字查找,名字找不到根据类型查找,都找不到就报错,(有多个猫猫狗狗好像不行)@Resource(name = “cat2”),可以指定找哪个

小结:

@Resource和@ Autowired的区别:

● 都是用来自动装配的,都可以放在属性字段上
● @ Autowired通过byType的方式实现,而且必须要求这个对象存在!
● @ Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!

● 执行顺序不同:@ Autowired通过byType的方式实现。

public class People {
    @Resource(name = "cat2")
    private Cat cat;
    @Autowired
    Qualifier(value="dog222")
    private Dog dog;
    private String name;
    
public class mytest {
    @Test
    public void test1(){
        ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");

        People people = Context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();
    }
}
//能显示出结果
miao~
wang~

上一篇:2021-03-22


下一篇:(转)Sqoop中文手册