Spring使用注解读取和存储对象

目录

存储 Bean 对象

前置⼯作:配置扫描路径(重要)

添加注解存储 Bean 对象 

类注解

类注解之间的关系

 Bean 命名规则

 方法注解

重命名 Bean

获取 Bean 对象(对象装配)

属性注⼊

构造⽅法注⼊ 

Setter 注⼊

三种注⼊优缺点分析

@Resource:另⼀种注⼊关键字

Bean 作⽤域和⽣命周期

作⽤域定义

 Bean 的 6 种作⽤域

singleton

prototype

request

 session

application(了解)

websocket(了解)

单例作⽤域(singleton) VS 全局作⽤域(application)

设置作⽤域

 Spring 执⾏流程和 Bean 的⽣命周期

Spring 执⾏流程

Bean ⽣命周期

 ​编辑


存储 Bean 对象

前置⼯作:配置扫描路径(重要)

注意:想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的 包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。
在 spring-config.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:content="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans htt
    p://www.springframework.org/schema/beans/spring-beans.xsd http://www.spring
framework.org/schema/context https://www.springframework.org/schema/contex
t/spring-context.xsd">
<content:component-scan base-package="com.bit.service"></content:compon
ent-scan>
</beans>
其中标红的⼀⾏为注册扫描的包,如下图所示:

也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的。

添加注解存储 Bean 对象 

想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
2. ⽅法注解:@Bean。
接下来我们分别来看。

类注解

@Controller // 将对象存储到 Spring 中
public class UserController {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
    }
}

类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

 其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”。

 Bean 命名规则

它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下:

public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        // 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                Character.isUpperCase(name.charAt(0))){
            return name;
        }
        // 否则就将⾸字⺟⼩写
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

 方法注解

public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
    }
}
在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:
@Component
public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
    }
}

重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所示:
public class Users {
    @Bean(name = {"u1"})
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
    }
}
此时我们使⽤ u1 就可以获取到 User 对象了,如下代码所示:
class App {
 public static void main(String[] args) {
 // 1.得到 spring 上下⽂
 ApplicationContext context =
 new ClassPathXmlApplicationContext("spring-config.xml");
 // 2.得到某个 bean
 User user = (User) context.getBean("u1");
 // 3.调⽤ bean ⽅法
 System.out.println(user);
 }
}
这个重命名的 name 其实是⼀个数组,⼀个 bean 可以有多个名字

获取 Bean 对象(对象装配)

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:
1. 属性注⼊
2. 构造⽅法注⼊
3. Setter 注⼊

属性注⼊

属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中。
Service 类的实现代码如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
    /**
     * 根据 ID 获取⽤户数据
     *
     * @param id
     * @return
     */
    public User getUser(Integer id) {
        // 伪代码,不连接数据库
        User user = new User();
        user.setId(id);
        user.setName("Java-" + id);
        return user;
    }
}
Controller 类的实现代码如下
import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Controller;
@Controller
public class UserController {
    // 注⼊⽅法1:属性注⼊
    @Autowired
    private UserService userService;
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}
获取 Controller 中的 getUser ⽅法:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserControllerTest {
    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        UserController userController = context.getBean(UserController.cla
                ss);
        System.out.println(userController.getUser(1).toString());
    }
}
最终结果如下:

构造⽅法注⼊ 

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:
@Controller
public class UserController2 {
    // 注⼊⽅法2:构造⽅法注⼊
    private UserService userService;

    @Autowired
    public UserController2(UserService userService) {
        this.userService = userService;
    }
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}
如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略
但是如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法,否则程序会报错

Setter 注⼊

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注
解,如下代码所示:
@Controller
public class UserController3 {
    // 注⼊⽅法3:Setter注⼊
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}

三种注⼊优缺点分析

  • 属性注⼊的优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只 有在使⽤的时候才会出现 NPE(空指针异常)。
  • 构造⽅法注⼊是 Spring 推荐的注⼊⽅式,它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这 种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点是通⽤性,在使⽤ 之前⼀定能把保证注⼊的类不为空。
  • Setter ⽅式是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法,所有 Spring 现版本已 经推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了。

@Resource:另⼀种注⼊关键字

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示:
@Controller
public class UserController {
    // 注⼊
    @Resource
    private UserService userService;
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}
@Autowired 和 @Resource 的区别
  •  出身不同:@Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
  • 使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。
  • @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注 ⼊和属性注⼊,不能⽤于构造函数注⼊。

Bean 作⽤域和⽣命周期

在 Spring 中Bean 的作⽤域默认也是 singleton 单例模式。

作⽤域定义

限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就 表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀ 个⼈读取到的就是被修改的值。

 Bean 的 6 种作⽤域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:
1. singleton:单例作⽤域
2. prototype:原型作⽤域(多例作⽤域)
3. request:请求作⽤域
4. session:回话作⽤域
5. application:全局作⽤域
6. websocket:HTTP WebSocket 作⽤域
注意后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种。

singleton

  • 官⽅说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
  • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对 象。
  • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
  • 备注:Spring默认选择该作⽤域

prototype

  • 官⽅说明:Scopes a single bean definition to any number of object instances.
  • 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例。
  • 场景:通常有状态的Bean使⽤该作⽤域

request

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:每次http请求会创建新的Bean实例,类似于prototype
  • 场景:⼀次http的请求和响应的共享Bean
  • 备注:限定SpringMVC中使⽤

 session

  • 官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http session中,定义⼀个Bean实例
  • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
  • 备注:限定SpringMVC中使⽤

application(了解)

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http servlet Context中,定义⼀个Bean实例
  • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
  • 备注:限定SpringMVC中使⽤  

websocket(了解)

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
  • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀
  • 次初始化后,直到WebSocket结束都是同⼀个Bean。
  • 备注:限定Spring WebSocket中使⽤

单例作⽤域(singleton) VS 全局作⽤域(application)

  • singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
  • singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

设置作⽤域

使⽤ @Scope 标签就可以⽤来声明 Bean 的作⽤域,⽐如设置 Bean 的作⽤域,如下代码所示:

import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Scope;
        import org.springframework.stereotype.Component;
@Component
public class Users {
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean(name = "u1")
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java"); // 【重点:名称是 Java】
        return user;
    }
}

 @Scope 标签既可以修饰⽅法也可以修饰类,@Scope 有两种设置⽅式:

1. 直接设置值:@Scope("prototype")
2. 使⽤枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

 Spring 执⾏流程和 Bean 的⽣命周期

Spring 执⾏流程

Bean ⽣命周期

1.实例化 Bean(为 Bean 分配内存空间)
2.设置属性(
Bean 注⼊和装配)
3.Bean 初始化
  • 实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、 ApplicationContextAware 的接⼝⽅法;
  • 执⾏ BeanPostProcessor 初始化前置⽅法;
  • 执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
  • 执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
  • 执⾏ BeanPostProcessor 初始化后置⽅法。
4.使⽤ Bean
5.销毁 Bean

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值