Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期

本文主要是介绍Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.存储Bean对象

1.1 <bean></bean>标签

<?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="user" class="User"> </bean><bean id="people" class="demo.People"></bean>
</beans>

class路径是以java为当前路径来写的 

此时只是将类配置到了配置文件中,并没有真正的放到Spring中,只有在获取上下文对象时,才会将对象放进去。

获取bean对象:

    public static void main(String[] args) {//先得到 spring 上下文对象(路径名称要和spring创建的配置文件名称相等)ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");//依赖查找->IoC的一种实现方式//通过Bean名称来获取(这里的名称要和配置文件中的id)相等,此时得到的object对象,需要强转User user=(User)context.getBean("user");//通过类型来获取bean对象(如果Spring容器中有多个该类型对象,会报错)User user1=context.getBean(User.class);//通过Bean名称和类型来获取User user2=context.getBean("user",User.class);System.out.println(user.sayHello());
//        People people=context.getBean("people",People.class);
//        System.out.println(people.sayHi());}
//User Constructor
//hello worldpublic static void main(String[] args) {//XmlBeanFactory 有横线说明被官方弃用了,不建议使用BeanFactory context=new XmlBeanFactory(new ClassPathResource("spring-config.xml"));//通过Bean名称来获取(这里的名称要和配置文件中的id)相等,此时得到的object对象,需要强转User user=(User)context.getBean("user");//通过类型来获取bean对象User user1=context.getBean(User.class);//通过Bean名称和类型来获取User user2=context.getBean("user",User.class);System.out.println(user.sayHello());//People people=context.getBean("people",People.class);//System.out.println(people.sayHi());}
//User Constructor
//People constructor
//hello world

ApplicationContext VS BeanFactory 有什么区别?

相同点:都是容器管理对象,都可以获取Bean对象。

区别:

ApplicationContext 是 BeanFactory 的子类,ApplicationContext 拥有更多的功能(对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持)

Bean 的加载机制不同:ApplicationContext 是一次加载并初始化所有的Bean对象,而BeanFactory 是需要哪个才去加载哪个(懒加载),因此更加轻量。

1.2  配置扫描路径

在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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--       <bean id="user" class="User"> </bean>-->
<!--       <bean id="people" class="demo.People"></bean>-->
<!--       配置bean的扫描根路径:只有当前目录下的类才会扫描是否添加了注解,如果添加了注解--><content:component-scan base-package="demo"></content:component-scan>
</beans>

注意:想要将对象成功的存储到Spring 中,我们需要配置一下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到Spring中

二. 通过注解添加Bean对象

想要将对象存储在Spring中,有两种注解类型可以实现:

2.1 类注解:@Controller、@Service、@Repository、@Component、@Configuration

• @Controller【控制器】校验参数的合法性(安检系统),负责接口对接。

• @Service【业务】业务组装(客服中心):会告诉我使用哪些方法,调用哪些接口,但是不具体实行。

• @Repository【数据持久层】实际业务处理(实际办理的业务):即业务层调用的方法的具体实现,操作一些和数据库相关的信息。

• @Component【组件】 工具类层(基础的工具):例如对账户加密,进行的业务不涉及数据库

• @Configuration 【配置层】配置:例如一些端口号

这五个类注解都可以使用
//@Controller
//@Service
//@Repository
//@Configuration@Component
public class People {public People(){System.out.println("People constructor");}public void sayHi(){System.out.println("hello world");}
}public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");People people=context.getBean("people",People.class);people.sayHi();}

Bean 生成名称源代码(此处是调用的jdk中的命名方法,并不是spring定义的,是Java定义的)

    public static String decapitalize(String name) {if (name == null || name.length() == 0) {return name;}//如果name长度大于1,并且前两个字符都是大写,返回name本身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);}

类注解Bean命名规则:

默认情况下,当类名的前两个字母都是大写时,名字和类名相同;当类名的前两个字母只有第一个是大写时,则名字是类名首字母小写。

若通过value设置,那么只能通过设置的类名来获取

五大类注解之间的关系

查看@Controller/@Service/@Repository/@Configuration 源码发现:

其他的四个类都是基于@Component实现的

2.2  方法注解:@Bean 

注意:@Bean 必须配合五大类注解一同使用,否则就会报错. (如果不使用五大类注解,spring就会扫描所有的类中的方法,会大大的降低效率).

public class ArticleInfo {private int id;private String title;private String Content;private LocalDateTime CreateTime;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return Content;}public void setContent(String content) {Content = content;}public LocalDateTime getCreateTime() {return CreateTime;}@Overridepublic String  toString() {return "ArticleInfo{" +"id=" + id +", title='" + title + '\'' +", Content='" + Content + '\'' +", CreateTime=" + CreateTime +'}';}public void setCreateTime(LocalDateTime createTime) {CreateTime = createTime;}
}@Controller
public class Articles {
//    可以通过以下三种方式来命名,可以命名任意个名字,若命名后,默认命名(方法名)不能再使用
//    @Bean({"arti","article"})
//    @Bean(name = {"arti","article"})@Bean(value = {"arti","article"})public ArticleInfo art(){//伪代码(真实的代码中是不允许我们去new的,因为我们IoC的思想就是控制反转)ArticleInfo articleInfo=new ArticleInfo();articleInfo.setId(1);articleInfo.setTitle("西游记");articleInfo.setContent("大闹天空");articleInfo.setCreateTime(LocalDateTime.now());return articleInfo;}
}public static void main(String[] args) {ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
//      ArticleInfo articleInfo=context.getBean("article",ArticleInfo.class);
//      ArticleInfo articleInfo=context.getBean("arti",ArticleInfo.class);ArticleInfo articleInfo=context.getBean("art",ArticleInfo.class);System.out.println(articleInfo);}

• @Bean的默认获取方式:@Bean的默认命名=方法名

2.3 @Bean的加载顺序

2.3.1 名称相同

//此处的Order中的数字越大,权重越高,优先执行
@Order(100)
@Controller
public class Articles {
//    可以通过以下三种方式来命名,可以命名任意个名字,若命名后,默认命名不能再使用
//    @Bean({"arti","article"})
//    @Bean(name = {"arti","article"})
@Bean(value = {"arti","article"})
public ArticleInfo arti(){//伪代码(真实的代码中是不允许我们去new的,因为我们IoC的思想就是控制反转)ArticleInfo art=new ArticleInfo();art.setId(2);art.setTitle("三国演义");art.setContent("温酒斩华雄");art.setCreateTime(LocalDateTime.now());return art;
}@Bean(value = {"arti","article"})public ArticleInfo art(){//伪代码(真实的代码中是不允许我们去new的,因为我们IoC的思想就是控制反转)ArticleInfo art=new ArticleInfo();art.setId(1);art.setTitle("西游记");art.setContent("大闹天空");art.setCreateTime(LocalDateTime.now());return art;}
}@Order(18)
@Component
public class Articles2 {@Bean(value = {"arti","article"})public ArticleInfo arti(){//伪代码(真实的代码中是不允许我们去new的,因为我们IoC的思想就是控制反转)ArticleInfo art=new ArticleInfo();art.setId(3);art.setTitle("红楼梦");art.setContent("我也不知道");art.setCreateTime(LocalDateTime.now());return art;}
}

在同一个类中,返回同名的相同类型的方法 Bean 时, 按照前后顺序执行

如果一个类的多个Bean使用相同的名称,那么程序执行不会报错,但是第一个Bean之后的对象不会放到容器中,也就是只有第一次创建Bean的时候会将对称的Bean 名称关联起来,后续再有相同名称Bean,容器会自动忽略 

2.3.2 名称不同 

对于同种类型的Bean,而名称不同时,仍可注入到容器中

三. 获取Bean 对象

对于之前我们获取 Bean 对象的方法,是通过依赖查找的方法,先获取上下文对象(容器对象)context,然后在通过getBean来获取对象的实例.对此,似乎还没有直接new来的快.因此我们还可以通过对象(依赖)注入的方式来获取对象的实例.

3.1 属性注入

@Repository
public class UserResposity {private int num;public int add(){System.out.println("Do UserResposity method");return num;}
}@Service
public class UserService {@AutowiredUserResposity userResposity;public int add(){System.out.println("Do UserService method.");return userResposity.add();}
}//使用测试验证对象是否被注入
class UserServiceTest {@Testpublic void test1(){//此处只是为了验证UserService中的UserResposity是否被注入了对象ApplicationContext context= new ClassPathXmlApplicationContext("spring-config.xml");UserService userService=context.getBean("userService",UserService.class);userService.add();}
}

上述代码该类型的对象在spring容器中只有一个,那若有多个呢?

public class User {private String name;public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Users{" +"name='" + name + '\'' +'}';}
}@Component
public class Users {@Bean("user1")public User user1(){User user=new User();user.setName("张三");return user;}@Bean("user2")public User user2(){User user=new User();user.setName("李四");return user;}}@Service
public class UserService2 {@AutowiredUser user;public void sayHi(){System.out.println(user.toString());}
}class UserService2Test {@Testpublic void test(){ApplicationContext context= new ClassPathXmlApplicationContext("spring-config.xml");UserService2 userService2=context.getBean("userService2",UserService2.class);userService2.sayHi();}
}//available: expected single matching bean but found 2: user1,user2

依赖注入 vs 依赖查找

依赖查找依赖Bean的名称

@Autowried 依赖注入流程:首先先根据 getType (从spring容器)获取对象,如果只获取一个,那么直接将此对象 注入到当前属性中;如果获取到多个对象,才会使用getName(根据名称)进行匹配。

 此时我们发现会报错,因为找到了两个该类型的对象。此时我们可以通过以下几种方法解决:

将属性名成改为Bean 储存在spring容器中对应的名称

    @AutowiredUser user2;User user1;

使用注解  @Qualifier

    @Autowired@Qualifier("user2")User user;

优点:使用简单

缺点:

无法注入final 修饰的属性 

只适用于IoC容器

3.2 Setter 注入

@Service
public class SetterUserService {private UserResposity userResposity;@Autowiredpublic void setRepository(UserResposity repository){this.userResposity=repository;}public void sayHi(){userResposity.add();}
}@Testpublic void test() {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");SetterUserService service = context.getBean("setterUserService", SetterUserService.class);service.sayHi();}

优点:通常Setter只Set一个属性,所以Setter注入更符合单一设计原则

缺点:

无法注入一个final 修饰的变量

setter 注入的对象可以被修改,setter 本来就是一个方法,既然是一个方法,就有可能被多次调用和修改。

3.3 构造方法注入

@Service
public class ConstructorService {//可以注入final修饰的属性private final UserResposity userResposity;
//    private UserResposity userResposity;//如果当前类中只存在一个构造方法时,@Autowired可以省略@Autowired//若容器中有多个UserResposity时,可以通过参数列表中的变量名改为容器中对应的名字public ConstructorService(UserResposity userResposity) {this.userResposity = userResposity;}public void sayHi(){userResposity.add();}}@Testpublic void test() {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");ConstructorService service = context.getBean("constructorService", ConstructorService.class);service.sayHi();}

优点:

可以注入一个final 修饰的变量

注入的对象不会被改变(构造方法只调用一次)

构造方法可以保证注入对象完全初始化

通用性更好(不依赖IoC)

四. @Resource

@Service
public class UserService2 {@Resource(name = "user2")User user;public void sayHi(){System.out.println(user.toString());}
}class UserService2Test {@Testpublic void test(){ApplicationContext context= new ClassPathXmlApplicationContext("spring-config.xml");UserService2 userService2=context.getBean("userService2",UserService2.class);userService2.sayHi();}
}

@Autowried与@Resource 的区别:

出身不同:@Resource 来自于JKD;@Autowired来自于Spring

支持参数不同:@Resource 支持的参数有很多,@Autowired只支持一个参数

使用上不同:@Resource 不支持构造方法注入;而@Autowired 支持

idea 兼容性支持不同:使用@Atowired 在idea 专业版下可能误报(required 设置为false表示该属性可不被注入,可以通过设置为false来解决)

五. Bean 作用域

案例:

@Service
public class ScopeService1 {@Resource(name = "user1")private User user;@Resource(name = "user1")private User user2;public void print(){user.setId(222);user2.setId(333);System.out.println("user1:"+user.toString());System.out.println("user2:"+user2.toString());}
}@Testpublic void Test(){ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");ScopeService1 service1=context.getBean("scopeService1",ScopeService1.class);service1.print();}

我们想要两个user属性有不同的id,但是设置完后发现两个user的id相同。

原因分析:操作以上问题的原因是因为Bean 默认情况下是单例状态,也就是所有人使用的都是同一个对象。单例模式可以很大程度上提高性能,所以在Spring中Bean的作用域默认也是 singleton单例模式

解决方案:在bean对象放入Spring 容器中时,指定作用域

    @Bean("user1")@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public User user1(){User user=new User();user.setId(1);user.setName("张三");return user;}

5.1 作用域定义 

Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,那么当其他人修改了这个值之后,那么另一个人读取到的就是被修改的值。

5.2 Bean 的6 中作用域

sigleton

• 描述:该作用域下的Bean在IoC 中只存在一个实例:获取Bean及装配Bean都是同一个对象

• 场景:通常无状态的Bean使用该作用域。无状态表示Bean对象的属性不需要更新

• 备注:Spring 默认选择该作用域

prototype

• 描述:每次对该作用域下的Bean 的请求都会创建新的实例:获取Bean及装配Bean都是新的对象

• 场景:通常有状态的Bean使用该作用域

request

描述:每次 http 请求都会创建新的Bean实例,类似于prototype

场景:一次 http 请求和响应的共享Bean

备注:限定 SpringMVC 中使用

session

描述:在一个 http session 中定义一个Bean实例

场景:用户会话的共享Bean,比如:记录一个用户的登录信息

备注:限定SpringMVC中使用

application

描述:在一个 http servlet Context 中,定义一个Bean实例

场景:Web 应用的上下文信息,比如:记录一个应用的共享信息

备注:限定在SpringMVC中使用

websocket

描述:在一个http websocket 的生命周期中,定义一个Bean实例

场景:WebSocket 的每次会话中,保存了一个Map 结构的头信息,将用来包裹客户端消息头。第一次初始化后,直到 WebSocket 结束都是同一个Bean

备注:限定Spring WebSocket 中使用

六. Spring 执行流程和 Bean 的生命周期

6.1 Spring 的执行流程

Spring 生命周期:

• 启动容器

• 读取配置进行 Bean 初始化

• 将 Bean 加入到容器中

• 装配 Bean 属性(给当前类的属性ID进行注入)

6.2 Bean 的生命周期

1. 实例化(内存空间分配)

2. 设置Bean 属性 (进行依赖注入,将依赖的Bean 赋值到当前类的属性上)

3. Bean的初始化

• 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;

• 执行 BeanPostProcessor初始化的前置方法

• 执行 @PostConsturct 初始化方法,依赖注入操作之后被执行

• 执行 BeanPostProcessor 初始化后置方法

@Component
public class BeanLifeComponent implements BeanNameAware {@Overridepublic void setBeanName(String name) {System.out.println("执行了 setBeanName 方法:"+name);}
//  初始化方法@PostConstructpublic void postConsturct(){System.out.println("执行了 postConsturct 方法");}//这个方法是和初始化方法一样的,只是这里需要在xml文件中声明public void myInit(){System.out.println("执行了 myInit 方法");}public void use(){System.out.println("使用Bean");}@PreDestroypublic void preDestroy(){System.out.println("执行了 preDestroy 方法");}
}public static void main(String[] args) {//这里不要使用ApplicationContext,因为它没有close方法ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");BeanLifeComponent beanLife=context.getBean("myBean",BeanLifeComponent.class);//使用BeanbeanLife.use();//此时的上下文对象若不关闭,是不会执行 preDestroy 方法的context.close();
}

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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--这里的init-method 的名字和我们代码中初始化方法名字要一致--><bean id="myBean" class="BeanLifeComponent" init-method="myInit"></bean>
</beans>

4. 使用Bean

5. 销毁Bean

销毁容器的各种方法,如@PreDestroy、DisposableBean 接口方法、destroy-method。

思考:为什么要先设置属性在进行初始化呢

 因为我的初始化方法在执行的过程中可能会用到我这个类中的某个属性,如果我不先进行属性的赋值,那么在执行到使用属性的这行代码时一定会报错

这篇关于Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/541907

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数