Spring:Bean(基本定义、作用域、各类注入、自动装配、注解)

从本质上看,Spring容器就是一个超级大工厂,Spring容器中的Bean就是该工厂的产品。Spring容器能产生那些产品,则完全取决于开发者在配置文件中的配置。

对于开发者来说,开发者使用Spring框架主要是做两件事:(1)开发Bean。(2)配置Bean。对于Spring框架来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成“依赖注入”——这就是IoC的本质。其实Spring框架的本质就是,通过XML配置来驱动Java代码,这样就可以把原本由Java代码管理的耦合关系,提取到XML配置文件中管理,这就实现了系统中的各组件的解耦,有利于后期的升级和维护。

1,Bean的基本定义

https://shao12138.blog.csdn.net/article/details/112904318#t7

2,Bean的别名

由于XML规范规定了XML ID标识符必须由字母和数字组成,且只能以字母开头,但在一些特殊的情况下(与Struts整合),必须为某些Bean指定特殊的标识名,此时就必须为控制器Bean指定别名。

指定别名有两种方式:

  • 定义<bean.../>元素时通过name属性指定别名如果需要为Bean实例指定多个别名,则可以在name属性中使用逗号、冒号或者空格分隔多个别名,后面通过任意别名即可访问该Bean实例。
  • 通过<alias.../>元素为已有的Bean指定别名:某些极端的情况下,程序无法在定义Bean时就指定所有的别名,而是需要在其他地方为一个已经存在的Bean实例指定别名,则可使用<alias.../>元素完成。该元素可指定两个元素:(1)name:将为该Bean实例指定别名。(2)alias:指定一个别名。
<bean id="person" class="Bean.Person" name="#abc,@123,abc*"></bean>

 别名的字符恨意随意,可以包含特殊字符。

<bean id="person" class="Bean.Person" ></bean>
<alias name="person" alias="ysy"/>

3,容器中Bean的作用域

当通过Spring创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域:

类型说明
singleton(默认)(常用)在Spring容器中仅存在一个实例,即Bean以单例的形式存在。当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;
prototype(常用)每次调用getBean()时,都会执行new操作,返回一个新的实例。原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;
request每次HTTP请求都会创建一个新的Bean。和prototype不同就是创建后,接下来的管理,spring依然在监听;
session同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession同一个全局的Session共享一个Bean,一般用于Portlet环境。全局的web域,类似于servlet中的application。

singleton&prototype

对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器不再跟踪实例,也不会维护Bean实例的状态。

  • 对于prototype作用域的Bean,Spring容器仅仅负责创建,当容器创建了Bean实例之后,Bean实例完全交给客户端代码管理,容器不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring都会产生一个新的实例,Spring容器无法知道它曾经创建了多少个prototype作用域的Bean,也无从知道这些propertype作用域的Bean什么时候才会被销毁。因此,Spring无法管理propertype作用域的Bean。
  • 对于singleton作用域的Bean,每次客户端代码请求时都返回同一个共享实例,客户端代码不能控制Bean的销毁Spring容器负责跟踪Bean实例的产生、销毁。Spring容器可以在创建Bean之后,进行某些通用资源申请;还可以在销毁Bean实例之前,先回收某些资源,比如数据库连接。

Java创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,就可以重复使用。

<bean id="person" class="Bean.Person" scope="singleton"></bean>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person p1 = (Person) context.getBean("person");
Person p2 = (Person) context.getBean("person");
System.out.println(p1==p2);
=========================================
true

request&session

对于request作用域,每次HTTP请求,Spring容器会根据Bean定义创建一个全新的的Bean实例。且该Bean实例仅在当前HTTP Request内有效。因此,如果程序需要,完全可以自由更改Bean实例的内部状态;其他请求所获得的Bean实例无法感觉到这种内部状态的改变,当处理请求结束时,request作用域的Bean实例将被销毁。

session作用域与request作用域完全类似,区别在于:request作用域的Bean对于每次HTTP请求有效,而session作用域的Bean则对每次HTTP Session有效。

request和session作用域只在Web应用中才有效,并且必须在Web应用中添加额外配置才会生效,为了让request和session两个作用域生效,必须将HTTP请求对象绑定到为该请求服务的线程上,这使得具有request和session作用域的Bean实例能够在后面的调用链中被访问到。

<%@ page import="org.springframework.web.context.WebApplicationContext" %>
<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %>
<%@ page import="Bean.Person" %>
<%@ page import="org.springframework.context.ApplicationContext" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <%
      WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application);
      Person p1 = (Person) ctx.getBean("person");
      Person p2 = (Person) ctx.getBean("person");
      out.print(p1==p2);
    %>
  </body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

4,配置依赖

使用Spring管理Bean:https://shao12138.blog.csdn.net/article/details/112904318#t5

依赖注入方式:https://shao12138.blog.csdn.net/article/details/112967959#t3

5,设置普通属性值(value)

创建配置文件:https://shao12138.blog.csdn.net/article/details/112904318#t7

6,配置合作者Bean(ref)

如果需要为Bean设置的属性值是容器中的另一个Bean实例,则应该使用ref属性。使用ref属性时可指定一个bean属性。

<bean id="person" class="Bean.Person" scope="singleton">
    <property name="car" ref="myCar"/>
</bean>
<bean name="myCar" class="Bean.Car"/>

7,使用自动装配注入合作者Bean

Spring能自动装配Bean与Bean之间的依赖关系,即无须使用ref显式指定依赖Bean,而是由Spring容器检查XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean。

自动装配可以减少配置文件的工作量,但降低了依赖关系的透明性和清晰性。对于大型应用,不建议使用自动装配。虽然使用自动装配可以减少配置文件的工作量,但大大降低了依赖关系的清晰性和透明性,依赖关系的装配依赖于源文件的属性名或属性类型,导致Bean与Bean之间的耦合降低到代码层次,不利于高层次解耦。

  • Spring的自动装配可通过<beans.../>元素的default-autowire属性指定,该属性对配置文件中所有的Bean起作用。
  • 也可以通过<bean.../>元素的autowire属性指定,该属性只对该Bean其作用。

在某些情况下,程序希望将某些Bean排除在自动装配之外,不作为Spring自动装配策略的候选者,此时可设置autowire-candidate属性,通过为<bean.../>设置autowire-candidate="false",即可将该Bean排除在自动装配之外,容器在查找时将不考虑该Bean。另外,还可以通过在<beans.../>元素中指定default-autowire-candidates属性将一批Bean排除在自动装配之外。default-autowire-candidates属性的值允许使用模式字符串,例如指定default-autowire-candidates="*abc",则所有以"abc"结尾的Bean都将被排除在自动装配之外。不仅如此,该属性甚至可以指定多个模式字符串,这样所有匹配任一模式字符串的Bean都将被排除在自动装配之外。

autowire、default-autowire属性可以接受如下值:

  • no(默认):不使用自动装配。Bean依赖必须通过ref元素定义。在较大的部署环境中不鼓励改变这个配置,显示配置着能够得到更清晰的依赖关系。
  • byName:根据setter方法名进行自动装配。Spring容器查找容器中的全部Bean,找出其id与setter方法名去掉set前缀,并小写首字母后与同名的Bean来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。
  • byType:根据setter方法的形参类型来自动装配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这个这样的Bean,就抛出一个异常;如果没找到这样的Bean,则什么都没有发生,setter方法不会被调用。
  • constructor:与byType类型,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器参数类型匹配的Bean,则会抛出一个异常。
  • autodetect:Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。如果找到一个默认的构造函数,那么就会应用byType策略。

注意:如果使用自动装配依赖,又使用ref显示指定依赖时,则显式指定的依赖覆盖自动装配依赖。

byName规则:setter方法的方法名与Bean的id进行匹配,假如Bean A的实现类包含setB()方法,而Spring的配置文件恰好包含id为b的Bean,则Spring容器会将b实例注入Bean A中。如果容器中没有名字匹配的Bean,Spring则不会做任何事情。

<?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="person" class="Bean.Person" scope="singleton" autowire="byName">
    </bean>
    <bean name="car" class="Bean.car">
        <property name="name" value="奔驰"/>
    </bean>
</beans>
package Bean;

public class car {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
======================================
package Bean;
public class Person {
    private car car;

    public car getCar() {
        return car;
    }

    public void setCar(car car) {
        this.car = car;
    }
}
package Action;
import Bean.Person;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
    public static void main(String[] args) throws BeansException {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person p1 = (Person) context.getBean("person");
        System.out.println(p1.getCar().getName());
    }
}

byType规则:byteType规则是根据setter方法的参数类型与Bean的类型进行匹配。假如A实例由setB(B b)方法,而Spring的配置文件中恰好有一个类型为B的Bean实例,容器为A注入类型匹配的Bean实例,如果容器中没有类型为B的实例,Spring不会调用setB()方法;但如果容器中包含多余一个的B实例,程序将会抛出异常。

<bean id="person" class="Bean.Person" scope="singleton" autowire="byType">

8,内部Bean(bean)

如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用内部Bean(嵌套Bean)(内部 Bean 不能使用在任何其他地方

把<bean.../>配置成<property.../>或<constructor-args.../>的子元素,那么该<bean.../>元素配置的Bean仅仅作为setter注入、构造注入的参数。内部 Bean 声明直接包含在 <property> 或 <constructor-arg> 元素里, 由于容器不能获取嵌套的Bean, 不需要设置任何 id 或 name 属性。

使用嵌套Bean与使用ref引用容器中的另一个Bean的本质是一样的。Spring框架的本质就是通过XML配置文件来驱动Java代码的,当程序需要调用setter方法或有参的构造器时,程序总需要传入参数值,随着参数类型的不同,当然Spring配置文件也要发生改变。

(1)形参类型是基本类型、String、日期等,直接使用value指定直接量值即可。

(2)形参类型是符合类(如Person、Dog),就需要传入一个Java对象作为实参,于是有三种方式:

  • 使用ref引用一个容器中已配置的Bean(Java对象)。
  • 使用<bean.../>元素配置一个嵌套Bean(Java对象)。
  • 使用自动装配。
<?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="person" class="Bean.Person" scope="singleton">
        <property name="car">
            <bean class="Bean.car">
                <property name="name" value="宝马"/>
            </bean>
        </property>
    </bean>
</beans>

内部Bean提高了程序的内聚性,但降低了程序的灵活性。只要在完全确定无须通过Spring容器访问某个Bean实例时,才考虑使用嵌套Bean来配置该Bean。

9,注入集合类型/组合类型

https://shao12138.blog.csdn.net/article/details/90286230

10,SpringBea的Bean和Java Bean

bean与Java Bean的不同:

  • 写法不同:Java Bean必须遵守特定的规范,必须给每个属性提供对应的getter和setter方法,而Bean只需提供setter方法。
  • 用处不同:Bean是Java实例、Java组件,JavaBean通常作为DTO(数据传输对象)来封装值对象,来传递参数
  • 生命周期不同:JavaBean作为值对象传递,不接受任何容器管理其生命周期。Bean由Spring管理生命周期。

虽然Spring对Bean没有特殊要求,但建议满足下面的几个原则:

  • 尽量为每个Bean实现类提供无参数的构造器。
  • 接受构造注入的Bean,则应提供对应的、带参数的构造函数。
  • 接受设值注入的Bean,则应提供对应的setter方法,并不要求提供对应的getter方法。

例子:数据库连接Bean(提前引入jar包

<?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="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <!--指定连接数据库的驱动-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!--指定连接数据库URL-->
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/test"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
        <property name="maxPoolSize" value="200"/>
        <property name="minPoolSize" value="2"/>
        <property name="initialPoolSize" value="2"/>
        <property name="maxIdleTime" value="200"/>
    </bean>
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource ds = context.getBean("dataSource", DataSource.class);
Connection connection = ds.getConnection();
//......

11,Bean的注解配置

Spring为不喜欢XML的人提供了另外一种选择,就是对Java类进行配置。

@Configuration
public class AppConfig {
    @Bean(name = "person")
    public Person person(){
        Person person = new Person();
        person.setCar(new car("奔驰"));
        return person;
    }
}
  • @Configuration:用于定义一个Java配置类。
  • @Bean:用于定义一个方法,该方法返回值为容器的一个Bean。
  • @Value:用于定义一个字段,并为该字段配置一个值。
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person ds = context.getBean("person", Person.class);

注意:一旦使用了Annotation配置Spring容器中的Bean,那么就需要使用下列方法来创建Spring容器ApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class)

除此之外还有如下常用的Annotation:

  • @Import:导入其他Java配置类。
  • @Scope:定义对应Bean的生命域。
  • @Lazy:是否需要延迟初始化。
  • @DependsOn:初始化该方法对应的Bean之前初始化指定的Bean。

如果以XML配置为主,需要让XML配置能加载Java类。只需要在XML配置中增加如下代码:<context:annotation-config/>注:需要引入支持context的DTD。

如果以Java类配置为主,需要让Java配置类能加载XML配置。需要借助于@ImportResource注解,例如:@ImportResource("classpath:/beans.xml")。

实际上,Spring提供@Configuration和@Bean并不是为了完全取代XML配置,只希望作为XML配置的一种补充。对于Spring框架的用户来说,Spring配置文件的“急剧膨胀”是一个让人头痛的点,因此Spring框架从2.0版本开始不断地寻找对配置文件“减肥”的各种方法。

但由于注解引入时间较晚,因此在一些特殊功能的支持上,注解不如XML配置强大。在目前的大多数项目中,要么完全使用XML配置方式管理Bean的配置,要么使用以注解为主、XML配置为辅的方式管理Bean的配置,要想完全放弃XML配合还是比较困难的。 

之所以会出现两者共存的情况,主要归结为三个原因:

  • 目前绝大多数采用Spring进行开发项目,几乎都是基于XML配置方式的,Spring引入注解的同时,必须保证注解能够与XML配置和谐共存,这是前提。
  • 由于注解引入的时间较晚,因此功能也没有发展多年的XML配置强大,对于复杂的配置,注解很难独当一面,在一段时间内仍需要XML配置的配合才能解决问题。
  • Spring的Bean配置方式与Spring核心模块之间是解耦的,因此,改变配置方式对Spring框架自身是透明的。Spring可以通过使用Bean后处理器非常方便的增加对注解的支持,这在技术上是非常容易实现的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值