Spring学习笔记-02 Bean的作用域、自动装配、注解开发

1.Bean的作用域

当Spring IOC容器创建Bean实例的时候,可以为Bean指定作用域,包括singleton(单例模式,默认选项,整个项目只有一个)、prototype(原型模式)、request(Http请求)、session(会话)、global-session(全局会话)[后三者仅在web工程中可以生效]。

(1)Singleton

整个Spring IOC容器中只会存在一个对应于bean id的实体类,一般适用于Service、DAO这些。

    <bean id="dog" class="com.zt.entity.Dog" scope="singleton">
        <constructor-arg index="0" value="汪汪"/>
        <constructor-arg index="1" value="2"/>
    </bean>

默认就是Singleton,也可以显示写出scope。

值得注意的是,我们所说的单例模式下唯一,指的是使用相同bean id获取得到的实体类是同一个,但是如果两个bean类型相同但是bean id不同,那么他们还是两个不同的实体类。

[注]:在java中,引用类型的==比较的是对象存储的内存地址。

    <bean id="dog" class="com.zt.entity.Dog">
        <constructor-arg index="0" value="汪汪"/>
        <constructor-arg index="1" value="2"/>
    </bean>
    
    <bean id="dog2" class="com.zt.entity.Dog">
        <constructor-arg index="0" value="汪汪"/>
        <constructor-arg index="1" value="2"/>
    </bean>

(2)Prototype

原型模式,每次使用IOC容器获取的bean都不是同一个,哪怕他们的bean id相同。

    <bean id="dog" class="com.zt.entity.Dog" scope="prototype">
        <constructor-arg index="0" value="汪汪"/>
        <constructor-arg index="1" value="2"/>
    </bean>

(3)Request

在一次Http请求中,一个bean id只对应一个实例;可以这么理解,在每个请求内部,bean是单例模式(仅限于同一个bean id)。

(4)Session

在一个会话中,一个bean id只对应一个实例。

(5)global session

全局Session(暂不了解,了解后补充)。

2.自动装配

Spring提供了自动装配功能,只需简单配置,我们甚至连注入都不需要自己配置。

Autowire关键字:用于指定Spring容器按照什么规则帮助我们进行注入。

byName:当存在一个bean的id与等待注入的某个实体类的某个属性名相同时,就可以注入

byType:当存在一个bean的类型与等待注入的某个实体类的某个属性的类型相同时,就可以注入

如果在容器中注册的Bean具有相同类型(或者说都符合代注入的属性的类别,包括子父类、接口),那么使用byType就会报错。

当然,我们可以对这个属性进行手动注入,Spring检测到该属性被注入值之后,也就不会帮我们自动注入了。

3.注解开发

配置文件有一个很明显的缺陷:配置文件中要书写大量的规则,当系统的集成度变高时,大量配置文件糅杂会导致项目混乱,所以现在很多地方我们都使用注解的方式替代配置文件,这可以大大简化配置文件甚至去掉配置文件。但这并不代表配置文件的写法已经过时,因为配置文件有着注解无法达到的灵活性,之前我们在Mybatis中,有<sql>配合<include>提取重复SQL,还可以定制个性化的resultMap,这些是注解无法做到的,现在的普遍做法是必要灵活性的地方在配置文件中编写,其他的尽量用注解替代。

注解要想生效,首先它不同于配置文件,配置文件可以直接指明如何配置,哪些地方有配置;但是注解必须实现经过一边扫描才能知道。还记得吗?在Mybatis中当我们使用注解开发后,需要将mapper的注册改为class并指向DAO。

1. 自动装配注解的使用

1. 为了使用注解,我们必须引入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">


</beans>

2. 配置扫描器

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

3. 在需要被装配的类的对应属性上增加Autowired注解

    @Autowired
    private Stu stu;

4. 测试

有一个bean id与待注入的属性同名:

没有有一个bean id与待注入的属性同名,且存在相同类型的bean:

当然,Spring也提供了Qualifier注解配合Autowried使用,可以通过bean id指定bean进行注入,这样也便于程序检查。

但是!Java本身提供了Resource注解,也可以实现自动装配。

详解区别与联系:

联系:@Autowired和@Resouce都可以用来自动装配,都可以写在属性字段上或者setter上。

区别:@Autowired默认按照类型装匹配,默认情况下,要被注入的对象必须存在,不允许为null,也可以通过设置其属性required = false允许装配null。搭配@Qualifier注解可以使用按名称装配。

@Resource默认按照名称装配,名称通过name属性指定。没有指定name时,如果注解在字段上,默认按照字段名进行按名查找;如果注解在setter上,则按照setter方法对应的属性名查找。当找不到名称一致的情况下,才会按照类型装配。但是,如果已经指定了name,那么就只会按照name装配。也就是name -> byName -> byType。

2. 组件注解

当我们引入了注解之后,可以不用在配置文件中配置bean,而是用组件注解告知Spring哪些类是需要被Spring容器管理为bean的。

@Component:可以声明那些类需要作为bean被Spring管理;@Component + value替代<bean id = "" class = ""></bean>

可以使用@Resource装配。

也可以使用@Autowired+@Qualifier装配。

1)使用@Value进行基本类型注入。

2)使用@Autowired+@Qualifier自动装配引用类型。

3)数组、list、map等复杂对象并不太容易直接使用注解进行注入,这也凸显了配置文件的灵活性优势。

3. 分层开发注解 

实际上,在项目开发中,我们会将项目分层:Controllr - Service - DAO

@Repository:用于标记DAO层。

@Service:用于标记Service层。

@Controller:用于标记Controller层。

拥有以上注解的类,当被扫描器扫描后,也会被Spring容器当作Bean管理。

4. 配置类(我们可以用Java配置类替代配置文件)

@Bean:传递容器内已有的对象作为参数,并将返回的对象保存在容器中进行管理。

@Configurable:声明当前类是一个配置类。

当我们使用注解进行配置时,可能我们需要引用jar包里的类,这时我们是没有办法直接在那些类中修改的,要获得它的对象并保存在容器中,只能使用以上注解。举一个例子,猫吃老鼠,老鼠作为猫的食物,现在我们不能修改猫和老鼠,但是我们要返回一个拥有老鼠作为食物的猫。

Mouse.java

package com.zt.config;

public class Mouse {
    private String name;

    public Mouse() {
    }

    public Mouse(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

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

Cat.java

package com.zt.config;

public class Cat {
    private String name;
    private Mouse mouse;

    public Cat() {
    }

    public Cat(String name, Mouse mouse) {
        this.name = name;
        this.mouse = mouse;
    }

    public String getName() {
        return name;
    }

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

    public Mouse getMouse() {
        return mouse;
    }

    public void setMouse(Mouse mouse) {
        this.mouse = mouse;
    }

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

TestConfig.java

package com.zt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConfig {

    @Bean
    public Mouse mouse(){
        return new Mouse("jerry");
    }

    @Bean
    public Cat cat(Mouse mouse){
        return new Cat("Tom",mouse);
    }
}

测试:

import com.zt.config.Cat;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.zt.config.TestConfig;
public class TestClassConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        Cat cat = context.getBean(Cat.class);
        System.out.println(cat);
    }
}

聊一聊注解与xml配置文件:在许多年前,业界普遍认为使用配置文件可以完成程序的解耦,并且,由于我们的项目最终是打包成class+配置文件的方式,因此直接修改源代码是非常不现实的事情,因此可以通过修改配置文件的方式来完成对项目的修改(如更换连接池等);但是配置文件的方式会导致整个项目被大量的配置文件淹没,因此现在的主流是去配置文件化,也就是对于一些几乎固定不动的使用注解,对于一些可能经常变化的抑或是灵活性较高的,依旧使用配置文件,这样就简化了配置文件。

猜你喜欢

转载自blog.csdn.net/qq_39304630/article/details/112368667