zoukankan      html  css  js  c++  java
  • Spring 学习笔记(三):Spring Bean

    1 Bean配置

    Spring可以看做是一个管理Bean的工厂,开发者需要将Bean配置在XML或者Properties配置文件中。实际开发中常使用XML的格式,其中<bean>中的属性或子元素如下:

    • idBeanBeanFactory中的唯一标识,在代码中通过BeanFactory获取Bean的实例时候需要以此作为索引
    • classBean的具体实体类,使用包名+类名的形式指定
    • scope:指定Bean实例的作用域
    • <constructor-arg>:使用构造方法注入,指定构造方法的参数,index表示序号,ref指定对BeanFactory中其他Bean的引用关系,type指定参数类型,value指定参数常量值
    • <property>:用于设置一个属性,表示使用setter注入,name指定属性的名字,value指定要注入的值,ref指定注入的某个Beanid
    • <list>:用于封装List或者数组类型的依赖注入
    • <map>:封装Map类型的依赖注入
    • <set>:封装Set类型的依赖注入
    • <entry><map>的子元素,用于设置一个键值对

    2 Bean实例化

    Spring实例化Bean有三种方式:

    • 构造方法实例化
    • 静态工厂实例化
    • 实例工厂实例化

    下面进行简单的演示。

    2.1 构造方法实例化

    Spring可以调用Bean对应的类的无参构造方法进行实例化,比如:

    public class TestBean {
        public TestBean()
        {
            System.out.println("构造方法实例化");
        }
    }
    

    配置文件如下:

    <bean id="testBean" class="TestBean"/>
    

    则会调用无参构造方法初始化。

    其实就是只写一个<bean>就可以了,默认的话会调用无参构造方法初始化。

    2.2 静态工厂实例化

    静态工厂实例化需要在工厂类中配置一个静态方法来创建Bean,并添加factory-method元素,首先创建工厂类:

    public class TestBeanFactory {
        private static final TestBean testBean = new TestBean();
        public static TestBean getInstance()
        {
            return testBean;
        }
    }
    

    接着配置文件通过class指定该工厂类,通过factory-method指定获取实例的方法:

    <bean id="testBeanFactory" class="TestBeanFactory" factory-method="getInstance"/>
    

    这样就可以通过id获取了:

    TestBean test = (TestBean) context.getBean("testBeanFactory");
    

    2.3 实例工厂实例化

    实例工厂实例化与静态工厂实例化类似,不过是非静态方法,然后加上一个factory-bean元素,同样首先创建工厂类:

    public class TestBeanFactory {
        public TestBean getInstance()
        {
            return new TestBean();
        }
    }
    

    在配置文件需要添加两个Bean,一个指定工厂类,一个指定使用哪一个工厂类以及使用工厂类的哪一个方法:

    <bean id="factory" class="TestBeanFactory" /> <!--指定工厂类-->
    <bean id="testBeanFactory" factory-bean="factory" factory-method="getInstance" /> <!--指定工厂Bean以及哪一个工厂方法-->
    

    获取:

    TestBean test = (TestBean) context.getBean("testBeanFactory");
    

    3 Bean作用域

    3.1 分类

    <bean>中的scope可以指定的作用域如下:

    • singleton:默认作用域,在Spring容器只有一个Bean实例
    • prototype:每次获取Bean都会返回一个新的实例
    • request:在一次HTTP请求中只返回一个Bean实例,不同HTTP请求返回不同的Bean实例,仅在Spring Web应用程序上下文使用
    • session:在一个HTTP Session中,容器将返回同一个Bean实例,仅在Spring Web应用程序上下文中使用
    • application:为每个ServletContext对象创建一个实例,即同一个应用共享一个Bean实例,仅在Spring Web应用程序上下文使用
    • websocket:为每个WebSocket对象创建一个Bean实例,仅在Spring Web应用程序上下文使用

    下面具体说一下最常用的两个:singletonprototype

    3.2 singleton

    scope设置为singleton时,Spring IoC仅生成和管理一个Bean实例,使用id/name获取Bean实例时,IoC容器返回共享的Bean实例。设置方式如下:

    <bean id="testBean" class="TestBean"/>
    <bean id="testBean" class="TestBean" scope="singleton"/>
    

    因为这是默认的作用域,设置的话IDE也智能提示是多余的:

    在这里插入图片描述

    所以通过不需要加上scope,测试例子:

    TestBean test1 = (TestBean) context.getBean("testBean");
    TestBean test2 = (TestBean) context.getBean("testBean");
    System.out.println(test1 == test2);
    

    输入的结果为True

    3.3 prototype

    每次获取Bean时都会创建一个新的实例,例子如下:

    <bean id="testBean" class="TestBean" scope="prototype"/>
    
    TestBean test1 = (TestBean) context.getBean("testBean");
    TestBean test2 = (TestBean) context.getBean("testBean");
    System.out.println(test1 == test2);
    

    测试结果为False

    4 Bean生命周期

    Spring可以管理作用域为singleton的生命周期,在此作用域下Spring能精确知道Bean何时被创建,何时初始化完成以及何时被摧毁。Bean的整个生命周期如下:

    • 实例化Bean
    • 进行依赖注入
    • 如果Bean实现了BeanNameAware,调用setBeanName
    • 如果Bean实现了BeanFactoryAware,调用setBeanFactory
    • 如果Bean实现了ApplicationContextAware,调用setApplicationContext
    • 如果Bean实现了BeanPostProcessor,调用postProcessBeforeInitialization
    • 如果Bean实现了InitializingBean,调用afterPropertiesSet
    • 如果配置文件配置了init-method属性,调用该方法
    • 如果实现了BeanPostProcessor,调用postProcessAfterInitialization,注意接口与上面的相同但是方法不一样
    • 不需要时进入销毁阶段
    • 如果Bean实现了DisposableBean,调用destroy
    • 如果配置文件配置了destroy-method,调用该方法

    下面用代码进行演示:

    public class TestBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean {
        public TestBean()
        {
            System.out.println("调用构造方法");
        }
    
        @Override
        public void setBeanName(String s) {
            System.out.println("调用BeanNameAware的setBeanName");
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.out.println("调用BeanFactoryAware的setBeanFactory");
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("调用ApplicationContextAware的setApplicationContext");
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("调用BeanPostProcessor的postProcessBeforeInitialization");
            return null;
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("调用InitializingBean的afterPropertiesSet");
        }
    
        public void initMethod()
        {
            System.out.println("调用XML配置的init-method");
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("调用BeanPostProcessor的postProcessAfterInitialization");
            return null;
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("调用DisposableBean的destroy");
        }
    
        public void destroyMethod()
        {
            System.out.println("调用XML配置的destroy-method");
        }
    }
    

    配置文件如下,指定了init-method以及destroy-method

    <bean id="testBean" class="TestBean" init-method="initMethod" destroy-method="destroyMethod"/>
    

    测试:

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
        TestBean test = (TestBean) context.getBean("testBean");
        ((BeanDefinitionRegistry) context.getBeanFactory()).removeBeanDefinition("testBean");
    }
    

    输出如下:

    在这里插入图片描述

    如果没有最后一行的手动删除Bean定义是不会看见最后两行的输出的,另外,这里没有调用BeanPostProcessor接口的两个方法,如果把scope改为prototype,输出如下:

    在这里插入图片描述

    可以看到首先对Bean进行一次初始化,并且再次生成一个新的实例,而且调用了BeanPostProcessor的两个方法。但是需要注意Spring不会管理scopeprototype的销毁,所以图中没有看到调用销毁的方法。

    5 Bean装配方式

    Spring支持以下两种装配方式:

    • 基于XML装配
    • 基于注解装配
    • 显式Bean装配

    Bean的装配方式也就是Bean的依赖注入方式,下面分别进行阐述。

    5.1 基于XML装配

    基于XML装配也就是在XML文件中指定使用构造方法注入或者setter注入,比如:

    public class TestBean {
        private final List<String> stringList;
        private String s;
    
        public TestBean(List<String> stringList) {
            this.stringList = stringList;
        }
    
        public void setS(String s)
        {
            this.s = s;
        }
    
        @Override
        public String toString() {
            return stringList.toString() + "
    " + s + "
    ";
        }
    }
    

    Bean有一个带参数的构造方法以及一个setter,接着在XML中指定相应的值即可:

    <bean id="testBean" class="TestBean">
        <constructor-arg index="0">
            <list>
                <value>1</value>
                <value>2</value>
            </list>
        </constructor-arg>
        <property name="s" value="444" />
    </bean>
    

    测试:

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    System.out.println(context.getBean("testBean"));
    

    5.2 基于注解装配

    尽管XML方式可以简单地装配Bean,但是一旦Bean过多就会造成XML文件过于庞大,不方便以后的升级和维护,因此推荐使用基于注解的装配方式,先来看一下常用的注解:

    • @Autowired:自动装配,默认按照Bean的类型进行装配,这是Spring的注解
    • @Resource:与@Autowired类似,但是是按名称进行装配,当找不到与名称匹配的Bean时才按照类型进行装配,这是JDK的注解
    • @Qualifier:与@Autowired配合使用,因为@Autowired默认按Bean类型进行装配,使用@Qualifier可以按名称进行装配
    • @Bean:方法上的注解,用于产生一个Bean,然后交由Spring管理
    • @Component:表示一个组件对象,加上了该注解就能实现自动装配,默认的Beanid为使用小驼峰命名法的类
    • @Repository/@Service/@Controller:实际上是@Component的别名,只不过是专门用于持久层/业务层/控制层的,从源码可以看出三个注解的定义除了名字不一样其他都一致,并且都是@Component的别名:

    在这里插入图片描述

    官方文档也提到相比起使用@Component,使用@Repository/@Service/@Controller在持久层/业务层/控制层更加合适,而不是统一使用@Component

    在这里插入图片描述

    5.3 注解使用示例

    5.3.1 @Bean

    @Bean示例如下:

    public class TestBean implements BeanNameAware{
        @Override
        public void setBeanName(String s) {
            System.out.println("setBeanName");
        }
    }
    
    @Configuration
    public class Config {
        @Bean
        public TestBean getBean()
        {
            return new TestBean();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
            context.getBean("getBean");
        }
    }
    

    注意通过@Bean自动产生的Beanid为方法名,而不是Bean的类名的小驼峰形式。

    5.3.2 其他

    @Autowired/@Resource/@Qualifier/@Repository/@Service/@Controller综合示例,首先创建如下包以及文件:

    在这里插入图片描述

    @Controller
    public class TestController {
        @Resource
        private TestService service;
    
        public void save()
        {
            System.out.println("controller save");
            service.save();
        }
    }
    
    @Service
    public class TestService {
        @Autowired
        @Qualifier("testRepository1")
        private TestInterface repository1;
    
        @Autowired
        @Qualifier("testRepository2")
        private TestInterface repository2;
        public void save()
        {
            System.out.println("service save");
            repository1.save();
            repository2.save();
        }
    }
    
    @Repository
    public class TestRepository1 implements TestInterface{
        @Override
        public void save() {
            System.out.println("repository1 save");
        }
    }
    
    @Repository
    public class TestRepository2 implements TestInterface{
        @Override
        public void save() {
            System.out.println("repository2 save");
        }
    }
    
    public interface TestInterface {
        void save();
    }
    
    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            ((TestController)context.getBean("testController")).save();
        }
    }
    

    配置文件:

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

    TestService中,使用了@Qualifier

    @Autowired
    @Qualifier("testRepository1")
    private TestInterface repository1;
    
    @Autowired
    @Qualifier("testRepository2")
    private TestInterface repository2;
    

    因为TestInterface有两个实现类,@Autowired不知道是选择TestRepository1还是TestRepository2,因此需要加上@Qualifier,指定需要注入的Beanid,或者使用@Resouce

    @Resource
    private TestInterface testRepository1;
    
    @Resource
    private TestInterface testRepository2;
    

    但是要注意这样默认了成员的名字就是Beanid,可以看到这里的名字是testRepository1testRepository2而不是repository1repository2

  • 相关阅读:
    webpack基本使用
    vue-路由-显示名称
    vue-父组件和路由
    vue-路由
    vue-父子组件和ref
    vue-组件
    go-面向对象编程(上)
    JavaScript的历史
    vue-列表动画
    钩子函数实现小球弹落
  • 原文地址:https://www.cnblogs.com/6b7b5fc3/p/13639576.html
Copyright © 2011-2022 走看看