Spring装配Bean

1 容器内部协作及简介


1.容器在启动时,读取Bean配置元数据信息(eg:实现类,属性,依赖关系以及配置行为(eg实现Bean级生命周期接口))

2.解析读取的信息将Bean构建成BeanDefinition对象并形成Bean注册表

3.容器根据BeanDefinition注册表实例化 & 组装 & 执行Bean相应的配置方法以及生命周期方法并将Bean放在Bean缓存池中

4.应用程序通过getBean(...)从容器的缓存池中获取Bean

2 属性注入

2.1 设值注入

通过setter方法注入Bean的属性或者依赖对象

使用setter注入的前提:1.Bean需要提供无参构造函数 2.Bean需要提供对应的setter方法

<bean id="beanId" class="Bean实现类">

       <property name="fieldName" value=""/><!-- 注入属性 file为基础数据类型-->

       <property name="fieldName" ref="当前容器/父容器中存在的Bean"/><!-- 注入依

赖对象 fileObject类型-->

       ......

 </bean>

采用setter注入需要主要的是:当以大写字母命名变量时,需要改变量的前两个字母为大写或者小写.

2.2 构造注入

通过构造函数注入Bean对应的属性或者依赖对象,使Bean在实例化后就得到设置,确保Bean实例化后就可以使用.

2.2.1 按类型匹配入参

<bean id="beanId" class="Bean实现类">
    <!-- 注入属性 fileType为基础数据类型-->
        <constructor-arg name="fieldName" type=”file数据类型”><value><![CDATA[;<>]]></value></constructor-arg>
        <!-- 注入依赖对象 fileType为Object类型-->
        <constructor-arg name="fieldName" ref="当前容器/父容器中存在的Bean" type=”file数据类型”></constructor-arg>
       ......
    </bean>

Bean实现类中只有一个构造函数时,<constructor-arg>的声明顺序确定了构造函数的入参顺序,所以不需要添加入参数据类型,但是在某些情况下定义多个具有相同入参的构造函数时,需要按索引匹配入参

2.2.2 按索引匹配入参

需要注意的是索引下标是从0开始


<bean id="beanId" class="Bean实现类">
    <!-- 注入属性 fileType为基础数据类型-->
        <constructor-arg name="fieldName" index=”fiel索引”><value><![CDATA[;<>]]></value></constructor-arg>
        <!-- 注入依赖对象 fileType为Object类型-->
        <constructor-arg name="fieldName" ref="当前容器/父容器中存在的Bean" index=”fiel索引”></constructor-arg>
       ......
    </bean>

构造函数是允许被重载的,此时需要联合使用两种类型匹配.

2.2.3 工厂方法注入

2.2.3.1 非静态工厂方法注入
<bean id="factoryBeanId" class="工厂方法实现类"></bean>
<bean id="beanId" factory-bean="factoryBeanId" factory-method="工厂类方法即用来生成对象的方法"></bean>
2.2.3.2 静态工厂方法注入
<bean id="beanId" factory-bean="工厂方法实现类" factory-method="工厂类方法即用来生成对象的方法"></bean>

2.3 方法注入

在单例Bean中注入prototyep Bean.单例Bean注入关联Bean的动作只发生一次.虽然关联Bean被声明为prototype,但是单例Bean保留的依然是第一次注入的Bean的实例

2.3.2 实现BeanFactoryAware接口

public class Boss implements BeanFactoryAware {
    private Car car;
    private BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public Car getCar() {
        return beanFactory.getBean("car", Car.class);
    }
}
<bean id="boss" class="com.lookup.Boss" scope="singleton"></bean>
<bean id="car" class="com.lookup.Car" scope="prototype"></bean>

单实例Bean直接获得BeanFactory实例,采用该实例直接从容器中获得prototype Bean。此中方式Spring接口侵入Bean,造成了对Bean的污染

2.3.2 采用配置

public interface ICar {
    public Car getCar();
}
public class Car {
}
public class Boss {
    private Car car;

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}
    <bean id="car" class="lookup.Car" scope="prototype"></bean>

    <bean id="boss" class="lookup.Boss" scope="singleton">
        <lookup-method name="getCar" bean="car"></lookup-method>
    </bean>

没有接口的实现,动态创建子类或者实现类。

lookup-method的签名需要符合如下标准:<public | protected> [abstract] <return-type> methodName(arguments);

注入方法比较

注入方式

优点

缺点

构造注入

1.避免重要属性没有设置而获得无用的bean

2.减少setter方法

3.更好的封装,变面外界修改重要属性

1.重要属性可选时,需要提供null

2.构造函数重载需要考虑匹配问题

3.自类需要应用父类复杂的构造函数

4.参数过多时,构造函数签名复杂

设值注入

1.灵活

2.解决循环依赖

1.重要属性无法在实例化时被设置

2.外界可以通过setter修改重要属性

工厂方法注入

1.简化配置,工厂方法创建对象是通过构造方法

或者setter对对象进行初始化

1.随时间的推移工厂类会增加,导致系统类增多,增加系统复杂性

2.构造函数重载时,需要修改工厂类

lookup注入

允许singleton Bean关联prototype Bean

如果prototype是有状态的,此时该中注入的最好的选择

1.需要提供额外的接口或者抽象类

2.需要实现Bean生命级接口,造成对Bean的污染

4 BeanBean之间的关系

4.1 引用同一个容器中的bean

<bean id="beanId" class="Bean实现类">
        <constructor-arg name="fieldName" ref="当前容器中存在的Bean"></constructor-arg>
       ......
    </bean>

4.2 引用父容器中的Bean

<bean id="beanId" class="Bean实现类">
        <constructor-arg name="fieldName">
             <ref parent=”父容器中存在的Bean”/>
        </constructor-arg>
       ......
    </bean>

如何建立父子容器关系,在创建子容器是将已经存在的容器对象以参数的形式传给子容器,那么已存在容器就是子容器的父容器

Public abstract class AbstractApplicationContext 
extends DefaultResourceLoader 
implements ConfigurableApplicationContext, DisposableBean {
    public AbstractApplicationContext(ApplicationContext parent)
}

5 内部beannull以及集合的注入

5.1 内部bean注入

内部Bean和匿名内部内类似,只允许当前Bean是,而不允许容器中其他的Bean使用

<bean id="beanId" class="Bean实现类">
<property name=”field名称”>
    <bean class="内部Bean实现类">
        ......
    </bean>
</property>       
</bean>

5.2 null注入

<bean id="beanId" class="Bean实现类">
<property name=”field名称”><null></null></property>   
</bean>

5.3 集合注入

5.3.1 List

<bean id="beanId" class="Bean实现类">
<property name=”field名称”>
    <list value-type="指定存入list的默认Java类型" merge=”....”>
        <value></value>
        <ref bean=”容器中存在的Bean”></ref>
    </list>
</property>   
</bean>

5.3.2 Set

<bean id="beanId" class="Bean实现类">
<property name=”field名称”>
    <set value-type="指定存入set的默认Java类型" merge=”....”>
        <value></value>
        <ref bean=”容器中存在的Bean”></ref>
    </set>
</property>   
</bean>

5.3.3 Map

<bean id="beanId" class="Bean实现类">
<property name=”field名称”>
    <map value-type="指定存入map value的默认Java类型" key-type="指定存入map key的默认Java类型" merge=”....”>
        <entry>
            <key><value>key</value></key>
            <value>value</value>
         </entry>
         <entry>
             <key><ref bean="已经存在的Bean被设置为Key"></ref></key>
             <ref bean="已经存在的Bean被设置为value"></ref>
          </entry>
     </map>
</property>   
</bean>

5.3.4 Properties

没有ref子标签,所以引用已经存在的Bean.属于配置属性文件,所有必要引用已经存在的Bean

<bean id="beanId" class="Bean实现类">
<property name=”field名称”>
    <props value-type=”指定存入Properties value的默认Java类型” merge=”....”>
        <prop key="key">value</prop>
    </props>
</property>   
</bean>

其中的value-type & key-type &merge都是可选的.

5.4 集合合并

 <bean id="super beanId" class="Bean实现类">
         <property name="field名称">
            <set>
                <ref bean="beanId"></ref>
            </set>
        </property>
</bean>
<bean id="beanId" parent="super beanId">
         <property name="field名称">
            <set merge=“true”>
                <ref bean="beanId"></ref>
            </set>
        </property>
 </bean>

在合并操作时候需要指定Beanparent节点并且是针对同一个field的操作,如果声明了value-type,那么数据类型也需要一致.

6 属性级联

class Student {
    //需要持有非空实例,并提供对应的get方法,作用是获得对象实例
    private Teacher teacher = new Teacher();
    private String teacherName;
    //必须提供get方法以便获取实例
    public Teacher getTeacher() {
        return teacher;
    }
}
class Teacher {
    private String name;
    //必须提供set方法
    public void setName(String name) {
        this.name = name;
    }
}
  <bean id="student" class="com.bean.lifecycle.entery.Student">
        <property name="teacher.name" value="AAA"></property>
    </bean>

7 bean自动装配类型

装配类型

说明

byName

根据bean的名称自动装配,

在容器中根据名字查找和属性名字一致的Bean进行装配

Student定义了名为teacher变量并提供了对应的setter,

如果容器中有一个Bean且名字为teacher,

那么就将teacher自动装配给Studentteacher变量

byType

根据bean类型自动装配

在容器中根据类型查找和指定属性类型一致的Bean进行装配,

如果容器中存在多个bean就会抛出异常,否则不抛出异常且属性不会被装配

如果不希望这样可以i使用dependency-check=object”强制让容器抛出异常

Student定义了类型为Teacher的属性,

如果容器中有一个Bean且类型为Teacher,

那么就将其自动装配给StudentTeacher属性

constructor

byType的方式类似,不同之处在于它应用于构造器参数。

如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常

Autodetect

通过bean类的自省机制来决定是使用constructor

还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式

default

如果bean没有显示声明autowrie属性,

那么容器将采用beans根标签中default-autowrie配置类型来装载bean

default-autowrie为全局装配类型配置

on

不使用自动装配,必须通过ref元素指定依赖,默认设置

在实际应用中一般不会在xml中启用自动装配,一般都会采用注解的方式,该方式下采用的byType类型的装配 。byName byType在使用的过程中必须保证bean能够初始化,否则的话会出现bug(属性为默认值,只是实例化没有初始化)如果有默认的无参数的构造器就不需要多余的配置,如果有带有参数的构造器,那在bean的配置中必须配置初始化参数或者在bean中添加无参数的构造器

8 方法替换

可以使用某个Bean的方法替换另外一个Bean的方法,需要实现MethodReplacer接口


public class Boss implements MethodReplacer{
    @Override
    public Object reimplement(Object o, Method method, Object[] objects) throws Throwable {
        return o;
    }
}
<bean id="bean1" class="lookup.Boss" scope="singleton">
        <replaced-method name="methodName" replacer="bean2"></replaced-method>
    </bean>

采用bean2实现了MethodReplacer接口,配置文件的含义是采用bean2 reimplement方法中的逻辑替换bean1methodName中对应的逻辑

9 BeanBean之间的关系

9.1 继承

java的继承一样,子Bean可以继承父Bean的行为,也可以修改父Bean的行为

    <bean id="boss" class="lookup.Boss" abstract="true">
        <property name="" value=""></property>
        <property name="" value=""></property>
    </bean>

    <bean id="boss2" class="lookup.Boss" parent="boss">
        <property name="" value=""></property>
        <property name="" value=""></property>
    </bean>

9.2 依赖

<bean id="car" class="lookup.Car" scope="prototype"></bean>

<bean id="boss" class="lookup.Boss" depends-on="car"></bean>

9.3 引用

    <bean id="car" class="lookup.Car" scope="prototype"></bean>

    <bean id="boss" class="lookup.Boss">
        <property name="car" ref="car"></property>
    </bean>

10 Bean的作用域

作用域

说明

singleton

在容器中Bean只存在一个实例,即Bean是以单例模式存在

prototype

每次请求容器时,都会返回一个新Bean,请求操作类似于new的操作

request

只在一次请求中有作用

session

只在一次会话中有作用

globalsession

全局Session共享一个Bean,类似于application的作用域

singleton Bean引用单例Bean

    <bean id="car" class="lookup.Car" scope="request">
        <aop:scoped-proxy/>
    </bean>

    <bean id="boss" class="lookup.Boss">
        <property name="car" ref="car"></property>
    </bean>

car的作用域是requestboss的作用域是singleton,也就说当http请求过来时候都会创建一个car Bean。采用代理后spring可以上的boss引用到对应请求的car。而此时注入boss中的car已经不是原来的car而是car的动态代理对象。该动态代理对象是Car的自类或者实现类,此时Spring在动态代理中加入了当前boss Bean需要注入的是那个请求中的car Bean的判断逻辑

11 整合多个配置文件

在大型系统中可能存在多个xml配置文件,在容器启动时可以通过通过String数组指定这些配置文件,如果要在配置文件层面实现不同配置文件中的Bean互相引用需要使用<import>标签,通过该标签引入后,多个配置文件中的Bean相当于同时配置在同一个文件中,此时就可以在配置文件层面实现互相引用

<import resource="其他配置文件路径"></import>

12 基于注解配置Bean的自动装配

12.1 扫描注解定义的Bean

<context:component-scan base-package="package" resource-pattern="*.class">
        <context:include-filter type="" expression="package | package.classNamepattern"></context:include-filter>
        <context:exclude-filter type="" expression="package | package.classNamepattern"></context:exclude-filter>
</context:component-scan>

component-scan含有一个默认的属性use-default-filters,其默认值为true(use-default-filters=true),如果该属性为默认值那么会扫描@Component@Service@Controller@Repository标注的Classeg:假设只需要扫描@Service,此时如果该属性等于true,那么也会扫描其他的组件(@Component@Controller@Repository).如果要实现该需求需要将use-default-filters的属性值修改为false

base-package定义了自动扫描package路径,也就是说需要加载哪些package,支持匹配模式。

resource-pattern定义了在base-package路径需要加载的class,其默认值是**/*.class。即base-package路径下所有的class

context:include-filter含义是需要饱含expression定义中包含的class

context:exclude-filter含义是不包含expression定义中包含的class

这两个标签结合base-package起到了过滤的作用

type属性取值

说明

示例

示例说明

annotation

通过采用目标类是否标注了某个特定Annotation进行过滤

a.b.XXAnnotation

所有标注了XXAnnotation的类

assignable

通过采用目标类是否扩展(继承/实现)了某个特定类进行过滤

a.b.XXService

所有扩展了XXService的类

aspectj

通过采用类名匹配和目标类是否扩展(继承/实现)了某个特定类进行过滤

a.b.*Service+

Service结尾的类以及扩展

了扩展了Service结尾的类的类

regex

通过采用正则表达式进行过滤

a.b.c.*

a.b.c package包下所有的类

custom

采用自定的filter规则扫描类,必须要实现TypeFilter接口

  

@Component | Service | Controller | Repository (value = "beanName")

public class Boss{}

其中value指定了当前类对应的bean的名称,除了Component外还提供了一更为细致化的注解,这些注解标注了该类本身的用途

@Service:用于对Service类进行标注

@Controller:用于对Controller类进行标注

@Repository:用于对DAO类进行标注

13 采用注解装配Bean

@Lazy //延迟注解
@Service(value = "serviceName")
@Order(value = 1)//指定组件的加载顺序
@Scope (value = "scope") //Beand的作用范围
public class Boss implements MethodReplacer{

    private Car bossCar;

    @Lazy
    private final Car bus;

    @Autowired(required = false)
    public Boss(Car bossCar, @Qualifier("bus") Car bus, List<Car> cars) {
        this.bossCar = bossCar;
        this.bus = bus;
        this.cars = cars;
    }

    @Autowired
    @Qualifier("car")
    public void setCar(Car car) {
        this.bossCar = car;
    }

    @Autowired
    //将容器中所有类型为Car的Bean注入集合中
    private final List<Car> cars;

    @Resource("myCar")
    private Car myCar;

    @PostConstruct //和init-method属性含义一样,init-method只能配置一个mothod,采用注解可以配置多个mothod
    public void init() {}
    
    @PreDestroy //和destory-method属性含义一样,destory-method只能配置一个method,采用注解可以配置多个mothod
    public void destoryMethod() {}

AutowiredResource注解都能实现Bean的装配,不同的是前者默认使用的是byType方式后者默认使用的是byName方式,采用byType的方式,如果容器中存在多个类型相同的Bean会抛出异常,此时采用@Autowired(required=false)来解决上述问题,@Autowired@Qualifier("...")可以将注入方式修改为byName,其中@Qualifier("...")括号中的参数即为该beanName的名称,如果要实施延迟依赖注入,@Lazy注解必须同时标注在属性以及目标Bean上,否则延迟注入无效

14 基于JAVA类配置Bean

@Configuration //将一个类定义为Bean
@Import(Boss.class) //引入外部Bean,该处可以是数组{class1.class,class2.class},可以直接访问引入的Bean
@ImportResource("resourcePath") //引入配置文件,该处可以是数组{resourePath1,resourePath2},可以直接访问引入的Bean
public class Car {
    
    @Bean(name = "Boss") //定义Bean并提供Bean实例化逻辑以及名称
    @Scope(value = "prototype")
    public Boss getBoss() {
        return new Boss();
        
    } 
}
基于JAVA类配置的Bean采用如下两种方式中的一种启动Spring容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Car.class);
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext();
acac.register(class1.class);
acac.register(class2.class);

acac.refresh();

15 动态向容器中注册Bean

DefaultListableBeanFactory默认实现了ConfigurableListableBeanFactory接口提供了可扩展配置的功能,只需要获得该类实例就可以动态的注册Bean到容器中。为了实现该功能需要实现工厂后置处理器接口BeanFactoryPostProcess

public class DynamicRegistrationBean implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
     
        //1.将ConfigurableListableBeanFactory转换为DefaultListableBeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        
        //2.创建Bean定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Car.class);
        
        //3.设置Bean属性,引用已经存在的Bean
        beanDefinitionBuilder.addPropertyReference("name","beanName");
        
        //4.注册Bean
        defaultListableBeanFactory.registerBeanDefinition("car", beanDefinitionBuilder.getRawBeanDefinition());
        
        //5.直接注册Bean实例
        defaultListableBeanFactory.registerSingleton("bean", new Object());
    }
}

16 FectoryBean

实例化过程复杂,导致配置繁琐。此时可以实现该接口实现客制化的实例化过程

T getObject():返回实例化后的Bean,如果singleton返回为true。则实例后被放入Bean缓存池中

boolean isSingleton():判断Bean是否为单实例Bean

Class<?> getObjectTyep():返回Bean类型

public class BikeFactory implements FactoryBean<Bike> {

    private String bikeInitInfors;

    public void setBikeInitInfors(String bikeInitInfors) {
        this.bikeInitInfors = bikeInitInfors;
    }

    @Override
    public Bike getObject() throws Exception {
        String[] args = this.bikeInitInfors.split(";");
        Bike bike = new Bike();
        bike.setter
        return bike;
    }

    @Override
    public Class<?> getObjectType() {
        return Bike.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
public class Bike {

    //properties

    //setter

    //getter
}
    <bean id="bikeFactory" class="lookup.BikeFactory">
        <property name="bikeInitInfors"><value>a;b;c;d;e</value></property>
    </bean>

    <bean id="bike" class="lookup.Bike"></bean>
通过getBean("bike")时,Spring通过反射机制发现BikeFactory实现了FactoryBean接口,此时Spring会调用getObject方法并返回结果。如果想要获得FactoryBean对象可以采用getBean("&bike").
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值