SpringBean管理

Posted 不会写代码的菜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBean管理相关的知识,希望对你有一定的参考价值。

一.什么是SpringBean?

在Spring中将管理对象称为 Bean.Bean是由一个SpringIOC容器实例化,组装和管理的对象.也就是说,Bean并不是由我们程序员编写的,而是在程序运行过程中,由Spring通过反射机制生成的.

SpringBean是Spring框架在运行时管理的对象,我们编写的大多数逻辑代码都会存放在SpringBean中.

二.SpringBean管理的内容

SpringBean的管理包括:

  • 创建一个对象

  • 提供依赖项(例如其他bean,配置属性)

  • 拦截对象方法调用以及提供额外的框架功能

  • 摧毁一个对象

三.SpringBean管理的方式

SpringBean有两种管理方式:

  • 基于XML文件的配置

  • 使用注解

SpringBean属性列表:

  • id: Bean的唯一标识符,Spring容器对Bean的配置,管理都通过该属性进行.

  • name:

    Spring容器通过此属性进行配置和管理,name属性可以为Bean指定多个名称,每个名称之间使用逗号或者分号分开

  • class: 指定Bean的实现类,它必须使用类的全限定名

  • scope: 用于设定Bean示例的作用域,其属性值如下:

    • singleton: 单例的.即只创建一个对象,在Spring启动时就会创建好.

    • prototype: 原型的.每次都会创建一个对象,在获取时才会创建.

  • constructor-arg: <Bean>元素的子元素.可以使用此元素传入构造参数进行实例化

  • property: <Bean>元素的子元素.用于调用Bean实例中的setter()方法完成属性赋值,从而完成依赖注入.

    • name属性指定Bean实例中的相应属性名

    • ref属性及value属性用于指定参数值

  • ref: <constructor-arg>,<property>等元素的属性或子元素,可以用于指定Bean工厂中某个Bean’实例的引用

  • value: <constructor-arg>,<property>等元素的属性或子元素,可以用于直接给定一个常量值

  • list: 用于封装List或数组属性的依赖注入

  • set: 用于封装Set类型属性的依赖注入

  • map: 用于封装Map类型的依赖注入

  • entry:

    元素的子元素,用于设置一个键值对,其key属性指定字符串类型的键值.ref属性或value属性直接指定其值.也可以通过ref或者value子元素指定其值.

1.使用XML文件

  • 使用xml文件进行SpringBean管理的方式很直观,但是较为麻烦我们在学习初期使用此种方式进行配置,在后期开发中建议使用注解的方式进行管理

    • 代码实现:

      <?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">
       ​
           <!--把需要Spring管理的对象进行配置-->
           <bean id="admin" class="com.ffyc.springdemo.model.Admin" scope="singleton">
       ​
               <!--通过get,set方法注入-->
               <property name="id" value="1"></property>
               <property name="name" value="飞飞"></property>
       ​
               <!--通过构造方法注入-->
               <constructor-arg name="id" value="2"></constructor-arg>
               <constructor-arg name="name" value="狗熊"></constructor-arg>
           </bean>
       </beans>
    • 注:

      • 我们在使用<property>标签进行配置时,类中必须要有get个set方法.因为<property>标签是通过类中的set方式创建对象的

      • 在使用<constructor-arg>标签时,类中可以没有get,set 方法,但是必须要有构造方法.因为此方式是通过构造方法创建对象的

2.使用注解实现

在使用注解实现Bean管理之前,我们首先要进行配置,否则Spring无法帮我们找到对应的文件

Spring注解属性列表:

注解说明
@Component使用在类上,用于实例化Bean
@Service使用在service层上,用于实例化Bean
@Repository使用在dao层上,用于实例化Bean
@Autowired使用在字段或setter上,用于根据类型依赖注入;使用在字段上就不需要再写setter
@Qualifier结合@Autowired一起使用,用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行依赖注入
@Value注入普通属性
@Scope标注Bean的作用范围

什么是依赖注入:

依赖注入,是IOC的一个方面,可以理解为不用我们自己创建对象,而是只需要描述它如何被创建,spring会帮我们创建,依赖外部的注入

使用注解前的配置:

1.导入注解所需的jar包: Spring的注解功能封装在aop包中,我们只需要导入Spring aop jar包即可,由于新版Spring jar包自动导入,此处我们无需再手动导入 2.在配置文件中开启扫描注解:

 <!--开启Spring注解扫描-->
     <context:component-scan base-package="com.ffyc.springdemo"></context:component-scan>

3.创建注解对象:

3.1 给实体类添加注解

 @Component(value = "admin")
 @Scope(value = "singleton")
 public class Admin 
 ​
 

3.2 dao层

package com.ffyc.springdemo.dao;
 ​
 ​
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Repository;
 ​
 @Repository
 public class AdminDao 
 ​
     @Autowired
     JdbcTemplate jdbcTemplate;
 ​
     public void saveAdmin()
         System.out.println("保存管理员");
 
 ​

3.3 service层

package com.ffyc.springdemo.service;
 ​
 import com.ffyc.springdemo.dao.AdminDao;
 import com.ffyc.springdemo.model.Admin;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 ​
 @Service
 public class AdminService 
 ​
     @Autowired
     AdminDao adminDao;
 ​
     public void saveAdmin()
          adminDao.saveAdmin();
     
     
 

测试类:

 package com.ffyc.springdemo.test;
 ​
 import com.ffyc.springdemo.service.AdminService;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 ​
 public class Test1 
 ​
     @Test
     public  void test() 
 ​
         ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
         AdminService adminService = app.getBean("adminService", AdminService.class);
         adminService.saveAdmin();
     
 
 ​

操作结果:

 

我们可以看到使用注解的方式可以很轻松的替代原来需要在配置文件中进行的大量配置的功能.因此在后续的开发中我们也将使用注解的方式进行.但是使用注解也会有缺点,我们在下篇博客中进行分析.

四.SpringBean的生命周期

Spring中所说的bean对象,与我们new的对象是不同的,Spring中所说的bean对象是指Spring框架所创建管理的我们的对象.

从宏观上来说,bean对象的实现可以分为5个阶段:

  1. 实例化实例化 Instantiation 通过反射机制以及工厂创建出来的原始对象

  2. 属性赋值 Populate

  3. 初始化 Initialization 完成对初始对象的各种赋值,完成后就把bean对象放入容器中

  4. 将 bean 对象放入到容器中,使用

  5. 销毁 Destruction

五.Spring中的bean是线程安全的吗?

servlet对象是线程安全的吗?

不是,因为servlet对象只创建一个,多个请求访问的是同一个servlet对象,因此它不是线程安全的

Spring中的bean是否是线程安全的,需要看bean的作用于scope:

  • 如果为Singleton表示是单例的,即不是线程安全的,会使用ThreadLocal为每个请求创建一个变量副本进行操作

  • 如果为Prototype表示为原型额,是线程安全的,因为每到来一个请求就会为其创建一个bean.

bean又可以分为两种:

有状态bean和无状态bean

  • 有状态bean就是有数据存储功能,例如包含成员变量

  • 无状态bean就是没有数据存储功能,例如service层和dao层,只是方法调用.

SpringBean继续入门

一、Aware相关接口

对于应用程序来说,应该尽量减少对Sping Api的耦合程度,然而有些时候为了运用Spring所提供的一些功能,有必要让Bean了解Spring容器对其进行管理的细节信息,如让Bean知道在容器中是以那个名称被管理的,或者让Bean知道BeanFactory或者ApplicationContext的存在,也就是产让该Bean可以取得BeanFactory或者ApplicationContext的实例,如果Bean可以意识到这些对象,那么就可以在Bean的某些动作发生时,做一些如事件发布等操作。

1.1、Spring提供一些Aware接口:

beanNameAware接口:如果某个bean需要访问配置文件中本身bean的id属性,这个Bean类通过实现该接口,在依赖关系确定之后,初始化方法之前,提供回调自身的能力,从而获得本身bean的id属性,该接口提供了void setBeanName(String name)方法实现,需要指出的是该方法的name参数就是该bean的id属性,加调该setBeanName方法可以让bean获取得自身的id属性

BeanFactoryAware接口:实现了BeanFactoryAware接口的bean,可以直接通过beanfactory来访问spring的容器,当该bean被容器创建以后,会有一个相应的beanfactory的实例引用,该 接口有一个方法void setBeanFactory(BeanFactory beanFactory)方法通过这个方法的参数创建它的BeanFactory实例,实现了BeanFactoryAware接口,就可以让Bean拥有访问Spring容器的能力。缺点:导致代码与spring的api耦合在一起,这种方式不推荐。

ApplicationContextAware接口:在Bean类被初始化后,将会被注入applicationContext实例,该接口有一个方法,setApplicationContext(ApplicationContext context),使用其参数context用来创建它的applicationContext实例,缺点:导致代码与spring的api耦合在一起,这种方式不推荐。

1.2、beanNameAware接口:

package com.pb.entity;

import org.springframework.beans.factory.BeanNameAware;
/*
 * 实体类实现init方法和BeanNameAware接口
 */
public class Hello implements BeanNameAware{

    @Override
    public void setBeanName(String arg0) {
        System.out.println("回调setBeanName方法  id属性是"+arg0);
        
    }
    public void init(){
        System.out.println("正在执行初始化方法init");
    }

}

 applicationContext.xml

<bean id="hello" class="com.pb.entity.Hello" init-method="init"></bean>

  测试类:

package com.pb.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.pb.entity.Hello;

public class HelloTest {

    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello=context.getBean("hello",Hello.class);

    }

}

  结果:

回调setBeanName方法  id属性是hello
正在执行初始化方法init

  

1.3、BeanFactoryAware接口:  

package com.pb.entity;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
/*
 * 实体类实现init方法和BeanNameAware接口
 */
public class Hello implements BeanNameAware,BeanFactoryAware{
    private BeanFactory bf;
    @Override
    public void setBeanName(String arg0) {
        System.out.println("回调setBeanName方法  id属性是"+arg0);
        
    }
    public void init(){
        System.out.println("正在执行初始化方法init");
    }
    
    /*
     * 重写setBeanFactory方法
     * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
     */
    @Override
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        this.bf=arg0;
        
    }
    public BeanFactory getBf() {
        return bf;
    }


}

  

配置文件不变

测试类:

package com.pb.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.pb.entity.Hello;

public class HelloTest {

    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello=context.getBean("hello",Hello.class);
        System.out.println("得到beanFactory对象 "+hello.getBf());

    }

}

  结果:

回调setBeanName方法  id属性是hello
正在执行初始化方法init
得到beanFactory对象 org.springframework.beans.factory.support.DefaultListableBeanFactory@3dc0bb: defining beans [hello]; root of factory hierarchy

  
1.4、ApplicationContextAware接口:

package com.pb.entity;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/*
 * 实体类实现init方法和BeanNameAware接口
 */
public class Hello implements BeanNameAware,BeanFactoryAware,ApplicationContextAware{
    private BeanFactory bf;
    private ApplicationContext context;
    @Override
    public void setBeanName(String arg0) {
        System.out.println("回调setBeanName方法  id属性是"+arg0);
        
    }
    public void init(){
        System.out.println("正在执行初始化方法init");
    }
    
    /*
     * 重写setBeanFactory方法
     * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
     */
    @Override
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        this.bf=arg0;
        
    }
    public BeanFactory getBf() {
        return bf;
    }
    @Override
    public void setApplicationContext(ApplicationContext arg0)
            throws BeansException {
        this.context=arg0;
        
    }
    public ApplicationContext getContext() {
        return context;
    }



}

  

配置文件不变

测试类

package com.pb.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.pb.entity.Hello;

public class HelloTest {

    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello=context.getBean("hello",Hello.class);
        System.out.println("得到beanFactory对象 "+hello.getBf());
        System.out.println("得到的applicationContext对象:"+hello.getContext());

    }

}

  结果:

回调setBeanName方法  id属性是hello
正在执行初始化方法init
得到beanFactory对象 org.springframework.beans.factory.support.DefaultListableBeanFactory@3dc0bb: defining beans [hello]; root of factory hierarchy
得到的applicationContext对象:org.springframework.context.support.ClassPathXmlApplicationContext@1d04653: startup date [Wed Apr 08 00:43:06 CST 2015]; root of context hierarchy

  

二、BeanPostProcessor类 和BeanFactoryPostProcessor

对容器中的Bean进行处理

实现BeanPostProcessor接口Bean后处理器

public Object postProcessAfterInitialization(Object arg0, String arg1);在bean初始化之后的操作

public Object postProcessBeforeInitialization(Object arg0, String arg1);在bean初始化之前的操作

 第一个参数初始化的Bean第二个参数是Bean 实例的名称

容器后处理器在容器实例化结束后,对容器进行额外的处理

必须实现BeanFactoryPostProcessor接口,

public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)

Spring撮提供了很多后窗口处理器如:

PropertyPlaceholderConfigurer:属性占位符配置器

PropertyOverrideConfigurer:另外一种属性占位符配置器

2种的区别在与后面一种具有覆盖的性质

2.1、PropertyPlaceholderConfigurer

是spring内建PropertyEdito,它是beanFactoryPostProcess的实现类

作用:读取properties配置文件

通过该实现类可以将spring的配置文件某些属性值配置到properties文件中,从而只要悠 properties的文件,spring的配置文件即可进行相应变动。

使用1.PropertyPlaceholderConfigure修改某个部分的属性时,不需要打开Spring配置文件,从而保证不会将新的错误引入到spring的配置文件中。

优点:可以从主xml配置文件中分离出部分的配置信息,

  可以支持多个配置文件,可以将配置文件 分割成多个配置文件,从而降低修改配置文件的风险,

 

2.2、PropertyOverrideConfigurer

会覆盖掉XML文件中的配置信息,以.properties属性文件中的配置为主

如果没有配置PropertyOverrideConfigurer则使用XML中的配置信息

属性格式:

beanName.property=value

其中beanName是springxml配置方便就近中的bean id属性,value是属性的值,对于多个properties文件,通过locations属性来进行指定

三、自定义属性编辑器

应用场景:

类型无法识别,如日期等

实现

继承PropertyEditorSupport

重写setAsText()方法

package com.pb.entity;

import java.util.Date;

public class AppDate {
    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
    
    

}

  自定义编辑器

package com.pb.entity;

import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class CustomerProperty extends PropertyEditorSupport {
    private String format;

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat sdf=new SimpleDateFormat(format);
        //super.setAsText(text);
        try {
            //转换对象,能过setValue方法重新赋值
            this.setValue(sdf.parse(text));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    public String getFormat() {
        return format;
    }

    public void setFormat(String format) {
        this.format = format;
    }
    
    
}

  applicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    <!--定义bean的类为自定义编辑器 -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <!-- 属性 -->
        <property name="customEditors">
            <!-- map -->
            <map>
                <!-- key为日期 -->
                <entry key="java.util.Date">
                    <!--配置map的value -->
                    <bean class="com.pb.entity.CustomerProperty">
                        <!-- 配置属性 -->
                        <property name="format" value="yyyy-MM-dd"></property>
                    </bean>
                </entry>
            </map>
        </property>
    </bean>
    <!--配置AppDate的bean  -->
<bean id="appDate" class="com.pb.entity.AppDate">
<property name="date">
<value>2014-4-8</value>
</property>
</bean>
</beans>

  测试类:

package com.pb.demo;

import java.text.SimpleDateFormat;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.pb.entity.AppDate;

public class Demo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        AppDate appDate=context.getBean("appDate",AppDate.class);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(sdf.format(appDate.getDate()));

    }

}

  

 

以上是关于SpringBean管理的主要内容,如果未能解决你的问题,请参考以下文章

springbean补充:关于bean的属性

SpringBean管理

快速切入:Spring框架核心概念总览

面试刷题30:SpringBean的生命周期?

jmockit 中文网 springbean

Spring Bean