当前位置: 代码迷 >> 综合 >> SpringBean
  详细解决方案

SpringBean

热度:85   发布时间:2023-10-27 19:31:42.0

SpringBean

  • 概述
  • BeanFactory接口
  • Bean的作用域
  • SpringBean的生命周期
  • 依赖注入的三种方式
  • 通过注解装配Bean
  • 细节

概述

控制反转:一种通过描述(在Java中可以是xml或者注解),通过第三方产生或获取特定对象的方式。由程序中通过主动new的方式创建对象,到由Spring管理对象的创建和销毁;
依赖注入:生成的对象需要的成员属性通过在xml或者注解的形式给Bean配置注入。

BeanFactory接口

Spring Ioc容器的设计主要基于BeanFactory和ApplicationContext两个接口,其中ApplicationContext是BeanFactory的子接口之一,BeanFactory提供了SpringIoc最底层的设计。

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";//返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常Object getBean(String name) throws BeansException;//通过Bean的名字匹配,并转换为给定class类型,可以是接口或父类型<T> T getBean(String name, Class<T> requiredType) throws BeansException;//返回指定类型的bean实例<T> T getBean(Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;//判断工厂中是否包含给定名称的bean定义,若有则返回trueboolean containsBean(String name);//判断给定名称的bean定义是否为单例模式,默认情况下是单例模式boolean isSingleton(String name) throws NoSuchBeanDefinitionException;//这个bean是否总是提供独立的实例boolean isPrototype(String name) throws NoSuchBeanDefinitionException;//通过Bean的名字匹配到的Bean的类型是否是指定的类型boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;//返回给定名称的bean的ClassClass<?> getType(String name) throws NoSuchBeanDefinitionException;//返回给定bean名称的所有别名 String[] getAliases(String name);
}

如果当前的工厂没有发现Bean实例,那么会去查找父类工厂。当前工厂中的Bean实例支持覆盖父类工厂中相同名字的Bean实例。
Spring的依赖注入就是通过实现beanFactory。
BeanFactory的官方解释:

		 ...一堆废话In contrast to the methods in {@link ListableBeanFactory}, all of theoperations in this interface will also check parent factories if this is a{@link HierarchicalBeanFactory}. If a bean is not found in this factory instance,the immediate parent factory will be asked. Beans in this factory instanceare supposed to override beans of the same name in any parent factory.Bean factory implementations should support the standard bean lifecycle interfacesas far as possible. The full set of initialization methods and their standard order is:BeanNameAware's {@code setBeanName}BeanClassLoaderAware's {@code setBeanClassLoader}BeanFactoryAware's {@code setBeanFactory}EnvironmentAware's {@code setEnvironment}EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}ResourceLoaderAware's {@code setResourceLoader}(only applicable when running in an application context)ApplicationEventPublisherAware's {@code setApplicationEventPublisher}(only applicable when running in an application context)MessageSourceAware's {@code setMessageSource}(only applicable when running in an application context)ApplicationContextAware's {@code setApplicationContext}(only applicable when running in an application context)ServletContextAware's {@code setServletContext}(only applicable when running in a web application context){@code postProcessBeforeInitialization} methods of BeanPostProcessorsInitializingBean's {@code afterPropertiesSet}a custom init-method definition{@code postProcessAfterInitialization} methods of BeanPostProcessorsOn shutdown of a bean factory, the following lifecycle methods apply:{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessorsDisposableBean's {@code destroy}a custom destroy-method definition

Bean的作用域

Spring提供了4种作用域,它会根据情况决定是否生成新的对象。
单例(singleton):它是默认选项,整个应用中Spring只生成一个Bean的实例;
原型(prototype):每次注入或者通过SpringIoc容器获取Bean时,Spring都会为它创建一个新实例;
会话(session) :一个Session中,Spring只会创建一个实例;
请求(request) :一次请求中Spring会创建一个实例,不同的请求会创建不同的实例。

SpringBean的生命周期

在这里插入图片描述

Demo

/*** 针对每一个springIOC容器中的bean,它自己不会执行这两个方法*/
public class BeanPostProcessorImpl implements BeanPostProcessor {
    @Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "开始实例化");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("【" + bean.getClass().getSimpleName() + "】对象" + beanName + "实例化完成");return bean;}
}
/*** 针对Spring Ioc容器*/
public class DisposableBeanImpl implements DisposableBean {
    @Overridepublic void destroy() throws Exception {
    System.out.println("调用DisposableBean的destroy方法");}
}
public class JuiceMake implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
    public JuiceMake(){
    System.out.println("无参构造函数");}public JuiceMake(String s){
    System.out.println("有参构造函数");}@Overridepublic void setBeanName(String name) {
    System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanNameAware接口的setBeanName方法");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanFactoryAware接口的setBeanFactory方法");}//要求springIoc容器实现ApplicationCoontext接口@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("【" + this.getClass().getSimpleName() + "】调用ApplicationContextAware接口的setApplicationContext方法");}//BeanPostProcessor的postProcessBeforeInitialization方法@PostConstructpublic void postConstruct() {
    System.out.println("postConstruct");}@Overridepublic void afterPropertiesSet() throws Exception {
    System.out.println("【" + this.getClass().getSimpleName() + "】调用InitializingBean接口的afterPropertiesSet方法");}//自定义初始化方法public void init() {
    System.out.println("【" + this.getClass().getSimpleName() + "】执行自定义初始化方法");}//BeanPostProcessor的postProcessAfterInitialization方法//使用这个Bean@PreDestroypublic void preDestroy() {
    System.out.println("preDestroy");}//DisposableBean的destroy方法//自定义销毁方法public void destroy() {
    System.out.println("【" + this.getClass().getSimpleName() + "】执行自定义销毁方法");}private String beverageShop = null;private Source source = null;public String getBeverageShop() {
    return beverageShop;}public void setBeverageShop(String beverageShop) {
    System.out.println("设置属性beverageShop");this.beverageShop = beverageShop;}public Source getSource() {
    return source;}public void setSource(Source source) {
    System.out.println("设置属性source");this.source = source;}public String makeJuice() {
    String juice = "这是一杯由" + beverageShop + "饮品店,提供的" + source.getSize() + source.getSugar() + source.getFruit();return juice;}
}
	<bean id="beanPostProcessor" class="spring.springIOC.BeanPostProcessorImpl"/><bean id="disposableBean" class="spring.springIOC.DisposableBeanImpl"/><!--Spring扫描Source类之后也可以用注解注入属性(@Autowired,@Resource)--><bean id="source" class="spring.springIOC.Source"><property name="fruit" value="橙汁"/><property name="sugar" value="少糖"/><property name="size" value="大杯"/></bean><!--init方法执行前先设置属性--><!--实例化时执行默认的无参构造函数,修饰符无影响--><bean id="juiceMake" class="spring.springIOC.JuiceMake" init-method="init" destroy-method="destroy" ><property name="source" ref="source"/><property name="beverageShop" value="贡茶"/></bean>
public class IocMain {
    public static void main(String[] args) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:config-iocSpring.xml");JuiceMake juiceMake = ctx.getBean("juiceMake",JuiceMake.class);System.out.println(juiceMake.makeJuice());ctx.close();}
}
//output:
【DisposableBeanImpl】对象disposableBean开始实例化
【DisposableBeanImpl】对象disposableBean实例化完成
【Source】对象source开始实例化
【Source】对象source实例化完成
无参构造函数
设置属性source
设置属性beverageShop
【JuiceMake】调用BeanNameAware接口的setBeanName方法
【JuiceMake】调用BeanFactoryAware接口的setBeanFactory方法
【JuiceMake】调用ApplicationContextAware接口的setApplicationContext方法
【JuiceMake】对象juiceMake开始实例化postConstruct
【JuiceMake】调用InitializingBean接口的afterPropertiesSet方法
【JuiceMake】执行自定义初始化方法
【JuiceMake】对象juiceMake实例化完成
这是一杯由贡茶饮品店,提供的大杯少糖橙汁
五月 21, 2019 10:58:17 上午 org.springframework.context.support.AbstractApplicationContext doClosepreDestroy
【JuiceMake】执行自定义销毁方法
调用DisposableBean的destroy方法

源码路径:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean

当配置了多个BeanPostProcessor接口的实现类时,有顺序影响。
源码路径:org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors
PriorityOrdered>Ordered>None。
不同接口实现时,高优先级的BeanPostProcessor实现类会作用于低优先级的初始化过程。
int getOrder();方法的返回值越大,优先级越低。

依赖注入的三种方式

Spring通过反射技术实现。Spring的依赖注入就是通过实现beanFactory
挺详细的:https://www.cnblogs.com/xiaoxi/p/5865330.html

通过注解装配Bean

https://blog.csdn.net/u013412772/article/details/73741710

@Component只能注解在类上,只能将类作为一个Bean。@Bean是一个方法级别上的注解,会将方法的返回对象作为Spring的Bean,存放在Ioc容器中,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。

@Target({
    ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    //一个字符串数组,允许配置多个BeanName,默认情况下Bean的名字和方法名相同,设置name属性可以指定名字,也可以取别名String[] name() default {
    };//属性被注入的方式,by name or by type,default noAutowire autowire() default Autowire.NO;//自定义初始化方法String initMethod() default "";//自定义销毁方法String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

Demo

@Configuration		//相当于xml配置的<beans></beans>
public class ConfigBean {
    @Resource	//先根据属性名匹配,如果找不到再按类型匹配;如果设置了name属性,那就只会按照名字查找.private Source source;@Bean(initMethod = "init2")public JuiceMake juiceMake2(){
    JuiceMake juiceMake2 = new JuiceMake();juiceMake2.setBeverageShop("贡茶");juiceMake2.setSource(source);return juiceMake2;}//也可以在内部使用@Configuration,它会自动装配,不需要使用@Import(xxConfiguration.class)@Configurationstatic class InnerStaticClass {
    @BeanSource getSource() {
    Source source = new Source();source.setFruit("椰汁");return source;}}
}

可以使用AnnotationConfigApplicationContext上下文加载,例如:

ApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);  //扫描加载@Configuration的文件
Object object = context.getBean("configBean.InnerStaticClass");//获取内部类的Bean

但这时需要注意Spring只加载了ConfigBean,其中的Source属性并不在Spring容器中,因此Source属性的依赖注入(DI)会失效,可以这样解决:在ConfigBean上加上一个注解@ImportResource(location = {"classpath:config-iocSpring.xml"}),这样xml里定义的Bean就会被加载进来。

细节

  1. 定义相同id的Bean
    实体:
public class Hello {
    private String name;public void setName(String name) {
    this.name = name;}public String getName() {
    return name;}
}

main.xml:

<bean id="hello" class="springBean.Hello"><property name="name" value="ljh"/></bean><import resource="follow.xml"/>

follow.xml:

<bean id="hello" class="springBean.Hello"><property name="name" value="lyf"/></bean>

测试类:

public class Test {
    public static void main(String[] arsg) {
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:main.xml");Hello hello = (Hello) context.getBean("hello");System.out.println(hello.getName());}
}
//output:
lyf

相同id的bean不允许在一个xml配置文件中配置,但是可以在不同文件中配置,即便存在父子关系。从输出结果可以看出,后定义的Bean会覆盖之前定义的。这是因为spring ioc容器在加载bean的过程中,类DefaultListableBeanFactory会对id相同的bean进行处理:后加载的配置文件的bean,覆盖先加载的配置文件的bean。DefaultListableBeanFactory类中,有个属性allowBeanDefinitionOverriding,默认值为true,该值就是用来指定出现两个bean的id相同的情况下,如何进行处理。如果该值为false,则不会进行覆盖,而是抛出异常。
2.类上加了@Component注解,同时又在配置文件中配置了Bean,如果设置的id不同,那么Spring容器会产生两个相同类型的Bean;如果设置的id相同,那么在XML中配置的Bean会覆盖通过注解配置的Bean。
3.通过XML配置Bean的属性时,可以搭配@Autowired等注解一起配置,<property />和注解可以一起用。
4.一个属性使用注解和XML同时配置,优先XML配置。
5.通过XML的方式可以设置Bean的静态属性,但是通过@Autowired注解的形式不行。XML里设置属性可以调用静态的setNameXX方法。
6.XML里配置的属性引用可以是通过注解配置生成的Bean。

@Component
public class A {}public class C {private static A a;public void setA(A a) {C.a = a;}
}<bean id = "b" class = "C"><property name = "a" ref = "a"/>
</bean>

参考书籍:JavaEE互联网轻量级框架整合开发

  相关解决方案