乐趣区

关于spring: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() + "\n" + s + "\n";
    }
}

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

退出移动版