1 Bean
配置
Spring
能够看做是一个治理 Bean
的工厂,开发者须要将 Bean
配置在 XML
或者 Properties
配置文件中。理论开发中常应用 XML
的格局,其中 <bean>
中的属性或子元素如下:
id
:Bean
在BeanFactory
中的惟一标识,在代码中通过BeanFactory
获取Bean
的实例时候须要以此作为索引class
:Bean
的具体实体类,应用包名 + 类名
的模式指定scope
:指定Bean
实例的作用域<constructor-arg>
:应用构造方法注入,指定构造方法的参数,index
示意序号,ref
指定对BeanFactory
中其余Bean
的援用关系,type
指定参数类型,value
指定参数常量值<property>
:用于设置一个属性,示意应用setter
注入,name
指定属性的名字,value
指定要注入的值,ref
指定注入的某个Bean
的id
<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
应用程序上下文应用
上面具体说一下最罕用的两个:singleton
和prototype
。
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
不会治理 scope
为prototype
的销毁,所以图中没有看到调用销毁的办法。
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
:示意一个组件对象,加上了该注解就能实现主动拆卸,默认的Bean
的id
为应用小驼峰命名法的类@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
主动产生的 Bean
的id
为办法名,而不是 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
,指定须要注入的Bean
的id
,或者应用@Resouce
:
@Resource
private TestInterface testRepository1;
@Resource
private TestInterface testRepository2;
然而要留神这样默认了成员的名字就是 Bean
的id
,能够看到这里的名字是 testRepository1
与testRepository2
而不是 repository1
和repository2
。