Spring基础-Bean、Bean的配置与获取

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_40918961/article/details/94220594

目录

 

基本概念

Java Bean

IoC-控制翻反转

DI—依赖注入

IoC 容器

Spring 中bean 的定义、配置与使用

1.创建Bean

2.基于 XML 配置Bean

3.基于注解配置Bean

扫描二维码关注公众号,回复: 7200678 查看本文章

Spring Bean Scopes - Bean 的作用域

Spring Collections - Bean集合

Bean过滤器


基本概念

Java Bean

Java Bean是一个实现了通用规范的类,该规范主要约束有:

1. 这个类需要是public 的, 然后需要有个无参数的构造函数

        对外可以访问Bean,所有必须是public,别的类要在内部使用Bean,但又不知道你有几个构造器,所以必须有个无参构造器,这样通过反射直接就能获得对象。

2. 这个类的属性应该是private 的, 通过setXXX()和getXXX()来访问(实现属性的getter与setter)

       假若别人想用动态设置你的Bean的属性,你大可以将所有属性设置为public,但是这样显得很不规范,所以设置getter/setter规范化属性的获取与设置。

3. 这个类应该是可以序列化的, 即可以把bean的状态保存的硬盘上, 以便以后来恢复。 

        将设计好的Bean保存下来,下一次使用是直接读取信息即可。

IoC-控制翻反转

      IoC 不是一种技术,只是一种思想。传统应用程序都是由我们在类内部主动创建(new)依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,容器通过注解或者配置文件读取信息,整合对象,通过容器获取对象。

DI—依赖注入

      IoC 和 DI 是同一个概念的不同角度描述,实际上是相同的思想。

        依赖注入指组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

       所谓控制反转,依赖注入,无非就是当前的Bean需要依赖另一个Bean,可以直接new一个,如果Bean比较少,new几个也没关系,如果多了,那关系就很复杂,很难查看Bean之间的依赖关系,要简化这种关系,索性就将Bean都交给通用容器处理,依赖关系只需要在外部写清楚即可,,即配置属性,或直接使用注解。要用到Bean则直接通过容器获取。

IoC 容器

        IoC 容器就是具有依赖注入功能的容器,IoC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中 new 相关的对象,应用程序由 IoC 容器进行组装。在 Spring 中 BeanFactory 是 IoC 容器的实际代表者。

       通过配置文件,Spring IoC 容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于 xml 配置文件进行配置元数据,而且 Spring 与配置文件完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于 java 文件的、基于属性文件的配置都可以。

        Spring Ioc 容器的代表就是 org.springframework.beans 包中的 BeanFactory 接口, BeanFactory 接口提供了 IoC 容器最基本功能;而 org.springframework.context 包下的 ApplicationContext 接口扩展了 BeanFactory ,还提供了与Spring AOP 集成、国际化处理、事件传播及提供不同层次的 context 实现 (如针对 web 应用的 WebApplicationContext )。简单说, BeanFactory 提供了 IoC 容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。 

具体的:

  • XmlBeanFactory : BeanFactory 实现,提供基本的 IoC 容器功能,可以从 classpath或文件系统等获取资源;

File file = new File("fileSystemConfig.xml");
Resource resource = new FileSystemResource(file);
BeanFactory beanFactory = new XmlBeanFactory(resource);
  • ClassPathXmlApplicationContext : ApplicationContext 实现,从 classpath 获取配置文件;
  BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");
  • FileSystemXmlApplicationContext : ApplicationContext 实现 ,从文件系统获取配置文件。
  BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");

        即类的实例不通过new 来获得,而通过IoC容器获取相应的配置信息,调用容器相应方法获得。

        spring既然是个框架,所谓框架,即别人写好的一整套代码,咱们直接拿过来用,在此框架上遵循该框架的规则并利用框架规则进行方便快捷的开发,要先下载并导入spring进行开发,如果使用IDEA可自动导入.

        如果使用IDEA创建Maven工程,则要在pom.xml下配置依赖,使用alt+enter进行自动导入即可(注意更换版本号)。

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <spring.version>5.1.1.RELEASE</spring.version>

</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>

可以看到,在依赖中导入了spring-core与spring-context

在目录src/main/java下是整个项目的java代码,划分成多个package,每个package下多个class

在目录src/main/resources是整个项目的xml配置文件.

Spring 中bean 的定义、配置与使用

1.创建Bean

package bean;

public class Person {
    public Person(){}
    private String name;
    private int age;
    public void setName(String name){
        this.name=name;
    }
    public void setAge(int age){
        this.age=age;
    }
    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
    public void print(){
        System.out.println(name+" "+age);
    }
}

2.基于 XML 配置Bean

通过配置XML来获取该类的实例,有三种写法:一般方法、缩写方法、p schema

配置不包含其它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">
    <!--使用p schema写法时要加入xmlns:p="http://www.springframework.org/schema/p">
    <!-一般配置方法-->
    <bean id="personOne" class="bean.Person">
        <property name="name">
            <value>"小明"</value>
        </property>
        <property name="age">
            <value>15</value>
        </property>
    </bean>
    <!--缩写配置方法-->
    <bean id="personTwo" class="bean.Person">
        <property name="name" value="贵师大生" />
        <property name="age" value="21" />
    </bean>
    <!--p schema 需要时自查-->
</beans>

       每个bean都是一个实例,bean有两个标签,id表示该实例的id标识,通过ApplicationContext对象的getBean()方法传入id即可获得对应类的实例,class表示该对象的Class。每个bean可以设置property,property有三个标签,name="xxx"就是类中定义的变量名,value="xxx"就是name对应的变量的值,ref="xxx‘’就是变量对应的类中对对象的引用,用于某个类中包含其它类,该bean中的ref即引用具体的bean,详见下面的嵌套bean。

配置包含有依赖关系的bean,即bean中包含其它bean

package bean;

public class Customer {
    private Person person;
    private int id;
    public Customer(Person person){
        this.person=person;
    }
    public Customer(){}

    public void setPerson(Person person){
        this.person=person;
    }
    public Person getPerson(){
        return person;
    }
    public void setId(int id){
        this.id=id;
    }
    public int getId(){
        return id;
    }

    public void print(){
        System.out.println(person.getName()+" "+person.getAge()+" "+id);
    }
}

        Customer内部有一个Person对象,肯定无法通过配置bean中的value进行配置,有三种办法

1.通过ref

<?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="personOne" class="bean.Person">
        <property name="name">
            <value>"小明"</value>
        </property>
        <property name="age">
            <value>15</value>
        </property>
    </bean>

    <!--Customer中有一个Person变量,通过ref引用具体的Person bean-->
    <bean id="customerOne" class="bean.Customer">
        <property name="person" ref="personOne" />
        <property name="id" value="2017243202" />
    </bean>
</beans>

        使用ref则ref引用的对象不会被其它bean所引用,否则会报错

2.在bean中配置bean

在bean中的property,name为变量名,后面跟着一个bean的定义即可

<!--内部嵌套bean-->
<bean id="customerTwo" class="bean.Customer">
    <property name="person" >
        <bean class="bean.Person">
            <property name="name" value="龙马夹袋" />
            <property name="age" value="33" />
        </bean>
    </property>
    <property name="id" value="88" />
</bean>

3.通过构造函数注入

<bean id="customerThree" class="bean.Customer">
    <constructor-arg>
        <bean class="bean.Person">
            <property name="name" value="坂田达" />
            <property name="age" value="35" />
        </bean>
    </constructor-arg>
    <property name="id" value="1234567" />
</bean>

        如果通过构造函数注入,就要有相应的构造函数,构造函数的参数应该是对应的类的对象,同时,如果有构造函数,一定要有默认的构造函数,否则会报错。

bean的自动装配

   Spring 支持 5 种自动装配模式,如下:

no —— 默认情况下,不自动装配,通过 ref attribute 手动设定。
byName —— 根据 Property 的 Name 自动装配,如果一个 bean 的 name ,和另一个 bean 中的 Property 的 name 相同,则自动装配这个 bean 到 Property 中。
byType —— 根据 Property 的数据类型( Type )自动装配,如果一个 bean 的数据类型,兼容另一个 bean 中 Property 的数据类型,则自动装配。
constructor —— 根据构造函数参数的数据类型,进行 byType 模式的自动装配。
autodetect —— 如果发现默认的构造函数,用 constructor 模式,否则,用 byType 模式。
1.默认引用装配
    默认情况下,需要通过 ref 来装配 bean 。
2.通过属性名(实际上就是变量名,xml中bean的id)装配
     根据属性 Property 的名字装配 bean ,这种情况,CustomerService 设置了 autowire="byName" ,Spring 会自动寻找与属性名字 customerDAO (实际上就是变量名)相同的 bean ,找到后,通过调用 setCustomerDAO(CustomerDAO customerDAO) 将其注入属性。
如果根据 Property name 找不到对应的 bean 配置就会装配失败,运行后,CustomerService 中 customerDAO=null 。

3.通过类型(实际上就是类的类型,xml中的class)装配
        根据属性 Property 的数据类型自动装配,这种情况,CustomerService 设置了 autowire="byType" ,Spring 会自动寻找与属性类型相同的 bean ,找到后,通过调用 setCustomerDAO(CustomerDAO customerDAO) 将其注入。
        如果配置文件中有两个类型相同的 bean,将抛出 UnsatisfiedDependencyException 异常,见以下: Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: 所以,一旦选择了 byType 类型的自动装配,请确认你的配置文件中每个数据类型定义一个唯一的 bean 。
4.构造器注入
这种情况下,Spring 会寻找与参数数据类型相同的 bean ,通过构造函数 public Customer(Person person) 将其注入。

在主应用程序中获取bean

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

public class App {
    //ApplicationContext实现ClassPathXmlApplicationContext接口,读取配置文件,成为beans工厂
    private static ApplicationContext context;
    public static void main(String[] args){
        context=new ClassPathXmlApplicationContext("Beans.xml");
        Person person=(Person)context.getBean("personTwo");
        person.print();
    }
}

        可以看到,context是从类路径中获取配置的IoC容器,调用getBean方法获取bean,完成控制反转。

        在xml文件下可手动配置bean,beans下的bean就代表具体实例,属性property中的id表示所引用的class的id标识,class代表类路径,默认前缀为src/main/java,name表示类的变量名(实例字段),value表示该变量的值,以此完成bean的初始化赋值。

        在需要调用时,先读取配置文件,返回一个ApplicationContext对象,通过该对象获取一个实例,通过具体配置bean获得的实例在通过id返回对象时返回的是同一个对象,两者引用同一块内存。

3.基于注解配置Bean

       可以想到,通过xml完成信息的注入,确实解耦合,但是也挺麻烦,注解是为 Spring 容器提供 Bean 定义的信息,把 XML 定义的信息通过类注解描述出来。Spring容器三大要素:Bean 定义、 Bean 实现类以及 Spring 框架。如果采用 XML 配置,Bean 定义和 Bean 实现类本身分离,而采用注解配置,Bean 定义在 Bean 实现类上注解就可以实现。

正常手动配置一个 bean :
1.写java 类
2.写配置文件xml,配置bean
3.在主类中解析xml文件获取Ioc容器,通过容器获取bean
通过注解,可更轻松描述上述事情

 @Component

被此注解标注的类将被 Spring 容器自动识别,自动生成 Bean 定义。

package bean;

import org.springframework.stereotype.Component;

//该注解表示自动装配到Ioc容器
@Component
public class Lj {
    private String info;
    public void setInfo(String info){
        this.info=info;
    }
    public String getInfo(){
        return info;
    }
}

除此之外,Spring 有三个与 @Component 等效的注解:

  1. @Controller:对应表现层的 Bean,也就是 Action 。
  2. @Service:对应的是业务层 Bean 。
  3. @Repository:对应数据访问层 Bean 。

@Autowired 

        自动注入信息(形成xml中的bean配置),即配置依赖关系,可以写在字段上,或者方法上。@Autowired 默认按类型装配,默认情况下要求依赖对象必须存在,如果要允许 null 值,可以设置它的 required 属性为 false。

package bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Lsl {
    private String info;

    //该注解表示该字段信息自动扫描并装入Ioc容器,表示Lsl依赖于Lj
    @Autowired
    private Lj lj;

    public void setInfo(String info) {
        this.info = info;
    }
    public void setLj(Lj lj){
        this.lj=lj;
    }
    public Lj getLj(){
        return lj;
    }
    public void print(){
        System.out.println(info+lj.getInfo());
    }
}

@Qualifier 注解

这个注解通常和@Autowired 一起使用,用于解决Bean的歧义性问题。
有一个接口Interface Hello
有两个类Hello1,Hello2分别实现了该接口,配置文件中的id分别为hello1,hello2
另一个类声明了Hello hello,使用@Autowired 自动注入,但不知道使用Hello1还是Hello2,于是使用@Qualifier进行标明
具体来看一下:

有接口wf

package bean;

public interface wf {
     void setInfo(String info);
     String getInfo();
}

有两个class实现了wf

package bean;

import org.springframework.stereotype.Component;

//该注解表示自动装配到Ioc容器
@Component
public class Lf implements wf{
    private String info;
    public void setInfo(String info){
        this.info=info;
    }

    public String getInfo(){
        return getInfo();
    }
}
package bean;

import org.springframework.stereotype.Component;

//该注解表示自动装配到Ioc容器
@Component
public class Lj implements wf{
    private String info;
    public void setInfo(String info){
        this.info=info;
    }

    public String getInfo(){
        return info;
    }
}

而在Lsl下有:

package bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Lsl {
    private String info;

    //该注解表示将字段信息自动扫描并装入Ioc容器
    @Autowired
    //lj和lf都实现了wf接口,那么,该wf到底指的是哪一个具体实现呢,使用@Qualifer
    @Qualifier("lj")
    private wf lj;


    public void setInfo(String info) {
        this.info = info;
    }
    public wf getWf(){
        return lj;
    }

    public void print(){
        System.out.println(info+lj.getInfo());
    }
}

具体实现:

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

public class GetBeanByAnnotation {
    private static ApplicationContext context;
    public static void main(String[] args){
        context=new ClassPathXmlApplicationContext("Annotation.xml");
        //获取bean后,可通过setter方法注入你想要的
        Lsl lsl=(Lsl)context.getBean("lsl");
        lsl.setInfo("lsl");
        lsl.getWf().setInfo("lj");
        lsl.print();
    }
}

结果为lsllj

使用注解进行配置大致如此,当然仍需配置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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <context:component-scan base-package="bean"/>

</beans>

<context:component-scan base-package="bean"/>就代表自动扫描并装入Ioc容器。

@Configuration

通过使用注解 @Configuration 告诉 Spring ,这个 Class 是 Spring 的核心配置文件,并且通过使用注解 @Bean 定义 bean ,举例说明:

package com.spring.java_config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    //相当于在xml中声明一个Bean的id,但没有配置属性,属性自己调用setter设置
    @Bean(name="animal")
    public IAnimal getAnimal(){
        return new Dog();
    }
}

App.java 内容:

package com.spring.java_config;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {

    private static ApplicationContext context;

    public static void main(String[] args) {
        context = new AnnotationConfigApplicationContext(AppConfig.class);
        IAnimal obj = (IAnimal) context.getBean("animal");
        obj.makeSound();

    }
}

在 ApplicationContext.xml 文件中只需要添加:

<bean id="animal" class="com.spring.java_conf">

 即通过注解完成bean定义

@PostConstruct注解
声明方法,标明获取bean时一定初始化执行该方法

@PreDestroy 注解
声明方法,标明销毁bean时一定执行该方法

        使用注解声明Bean方便很多,但是xml下可以直接配置Bean的属性,使用注解无法配置,只能先获取Bean再通过setter方法进行设置。

Spring Bean Scopes - Bean 的作用域

spring 中,Bean 的作用域决定了从 Spring 容器中返回的 Bean 实例的类型。在 Spring 中,支持以下 5 种类型的作用域:

  1. singleton — 单例模式,由 IOC 容器返回一个唯一的 bean 实例。
  2. prototype — 原型模式,被请求时,每次返回一个新的 bean 实例。
  3. request — 每个 HTTP Request 请求返回一个唯一的 Bean 实例。
  4. session — 每个 HTTP Session 返回一个唯一的 Bean 实例。
  5. globalSession — Http Session 全局 Bean 实例。

大多数情况下只需要处理 Spring 的核心作用域 — 单例模式( singleton )和原型模式( prototype ),默认情况下,作用域是单例模式。

使用模式只需要在对应的xml文件中配置bean时加入scope属性即可

例:该bean使用原型模式<bean id="Customer class="com.spring.customer.Customer" scope="prototype"/>

Spring Collections - Bean集合

要获取Bean的集合,就要使用Collections,使用时也需要进行相应的配置,主要类型有List,Set,Map,Properties等

配置bean时要显示添加,即增加property标签,具体格式一般为

<property name="集合变量名">

    <集合类型(list/set等)>

       <value>索引或与其特性有关的数据</value>

          <bean>与相应集合绑定的bean信息配置</bean>

       </集合类型>

</property>

例:

  1. <bean id="customerBean" class="com.spring.collections.Customer">
  2. <!-- java.util.List -->
  3. <property name="lists">//lists为在Customer中声明过的List的变量名
  4. <list>
  5. <value>1</value><!-- List 属性既可以通过 <value> 注入字符串,也可以通过 <ref> 注入容器中其他的 Bean-->
  6. <ref bean="personBean" />
  7. <value>2</value>
  8. <bean class="com.spring.collections.Person">
  9. <property name="name" value="List" />
  10. <property name="address" value="nmsl" />
  11. <property name="age" value="25" />
  12. </bean>
  13. </list>
  14. </property

 Spring Bean 的生命周期

  • Bean的建立, 由BeanFactory读取Bean定义文件,并生成各个实例
  • Setter注入,执行Bean的属性依赖注入
  • BeanNameAware的setBeanName(), 如果实现该接口,则执行其setBeanName方法
  • BeanFactoryAware的setBeanFactory(),如果实现该接口,则执行其setBeanFactory方法
  • BeanPostProcessor的processBeforeInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processBeforeInitialization()方法
  • InitializingBean的afterPropertiesSet(),如果实现了该接口,则执行其afterPropertiesSet()方法
  • Bean定义文件中定义init-method
  • BeanPostProcessors的processAfterInitialization(),如果有关联的processor,则在Bean初始化之前都会执行这个实例的processAfterInitialization()方法
  • DisposableBean的destroy(),在容器关闭时,如果Bean类实现了该接口,则执行它的destroy()方法 -Bean定义文件中定义destroy-method,在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法

        如果使用ApplicationContext来维护一个Bean的生命周期,则基本上与上边的流程相同,只不过在执行BeanNameAware的setBeanName()后,若有Bean类实现了org.springframework.context.ApplicationContextAware接口,则执行其setApplicationContext()方法,然后再进行BeanPostProcessors的processBeforeInitialization() 实际上,ApplicationContext除了向BeanFactory那样维护容器外,还提供了更加丰富的框架功能,如Bean的消息,事件处理机制等。

Bean过滤器

即标签<context:include-filter>与<context:exclude-filter>

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

        <context:include-filter type="regex" 
                       expression="com.spring.services.*Service.*" />
        <context:exclude-filter type="regex" 
            expression="com.spring.dao.*DAO.*" />        

    </context:component-scan>
使用类正则的方式批量将class注入容器(或避免被注入容器)。

猜你喜欢

转载自blog.csdn.net/qq_40918961/article/details/94220594