<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
在早期的时候,我们都是使用bean.xml的方式创建一个bean对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.dmg.User" id="user">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
</beans>
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);
}
}
但是当我们使用注解的时候可以这样使用,注入到ioc容器中
@Configuration表示这是一个配置类,对应bean.xml
@Bean 表示 <bean class="com.dmg.User" id="user">
这里的user2就是bean id
@Configuration
public class TestConfig {
@Bean
public User user2(){
User user=new User();
user.setAge(66);
user.setName("李四");
return user;
}
}
AnnotationConfigApplicationContext 表示注解配置应用上下文
使用了@Configuration这个注解,我们都可以访问到
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestConfig.class);
User user = (User) applicationContext.getBean("user2");
System.out.println(user);
}
}
我们也可以在@Bean(这里该改名字)
我们可以通过 applicationContext.getBeanDefinitionNames()来获取容器中的bean定义名称信息
AnnotationConfigApplicationContext能返回ApplicationContext是因为
最终继承的ApplicationContext,所以可以返回ApplicationContext
我们的容器中的user对象默认是单实例的,来看下场景
可以看到2次获取的时候都是true,这就是单实例
我们把applicationContext.getBean注释掉
在箭头位置加入一行数据
可以看到在单实例作用域下,先给容器加载数据,所以在容器中获取bean的时候直接从容器拿
我们在看下多实例,使用@Scope("prototype")
我们启动的时候,并没有看到打印的那一行内容,只有加载完毕的信息
我们把获取bean的注释放开
可以看到,获取一次bean,打印一次,结果为false,这就是多实例
我们可以在单实例环境下设置@Lazy 延时加载
等获取bean的时候,才创建对象
可以看到,获取bean也只打印一次,返回true
我们来看下@Bean手动指定初始化和销毁的方法
手动指定bean的生命周期
public class Sp {
public Sp(){
System.out.println("对象创建了");
}
public void init(){
System.out.println("对象初始化了");
}
public void destory(){
System.out.println("对象销毁了");
}
}
在initMethod写上初始化的方法
在destroyMethod写上销毁的方法
@Configuration
public class TestConfig {
@Bean(initMethod = "init",destroyMethod = "destory")
public Sp sp(){
return new Sp();
}
}
单实例环境下,容器启动的时候会创建对象,
容器销毁的时候,会销毁对象
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestConfig.class);
System.out.println("容器创建完成了");
applicationContext.close();
}
}
我们在看下多实例的场景
设置@Scope("prototype")
可以看到并没去创建对象,只有在获取bean的时候,才创建对象
销毁的时候,容器也不会去销毁这个对象
我们还可以通过实现InitializingBean, DisposableBean
来写属性赋值之后的方法,销毁的方法
public class Sp implements InitializingBean, DisposableBean {
private String name;
public Sp(){
System.out.println("对象创建了");
}
public void setName(String name) {
System.out.println("属性赋值");
this.name = name;
}
public void afterPropertiesSet() throws Exception {
System.out.println("属性赋值之后执行");
}
public void destroy() throws Exception {
System.out.println("对象销毁了");
}
}
@Configuration
public class TestConfig {
@Bean
public Sp sp(){
Sp sp=new Sp();
sp.setName("aaa");
return sp;
}
}
我们还可以使用@PostConstruct 来设置对象创建完成并且属性赋值之后,进行初始化
使用@PreDestroy 在容器销毁bean之前执行此方法
public class Sp {
private String name;
public Sp(){
System.out.println("对象创建了");
}
public void setName(String name) {
System.out.println("属性赋值");
this.name = name;
}
@PostConstruct
public void aa(){
System.out.println("对象创建之后,并且属性赋值之后执行");
}
@PreDestroy
public void bb(){
System.out.println("容器销毁对象之前执行");
}
}
我们还可以实现BeanPostProcessor 后置bean处理器,来进行初始化之前的操作,初始化之后的操作
public class Linux implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行:"+beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行:"+beanName);
return bean;
}
}
@Configuration
public class TestConfig {
@Bean
public Sp sp(){
Sp sp=new Sp();
sp.setName("aaa");
return sp;
}
@Bean
public Linux linux(){
return new Linux();
}
}
我们来看下源码分析,在postProcessBeforeInitialization方法打个断点
执行之后会先进入applyBeanPostProcessorsBeforeInitialization 执行bean后置处理器之前初始化方法
在这个result就拿到了bean的信息
然后再进入applyBeanPostProcessorsAfterInitialization
执行bean后置处理器之后初始化方法
等方法执行到postProcessAfterInitialization这里的时候,就会进入
我们linux类的postProcessAfterInitialization方法
初始化之前的操作和初始化之后的操作,都是在initializeBean这个方法里面去执行的
我们也可以实现ApplicationContextAware接口,在创建完对象,并且属性赋值之后,
使用应用程序上下文
public class Sp implements ApplicationContextAware {
private ApplicationContext applicationContext;
private String name;
public Sp(){
System.out.println("对象创建了");
}
public void setName(String name) {
System.out.println("属性赋值");
this.name = name;
}
@PostConstruct
public void aa(){
System.out.println("对象创建之后,并且属性赋值之后执行");
}
@PreDestroy
public void bb(){
System.out.println("容器销毁对象之前执行");
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
System.out.println("在容器中使用应用上下文");
}
}
进入源码ApplicationContextAwareProcessor类
可以看到他也是实现的BeanPostProcessor
我们进入invokeAwareInterfaces方法
可以看到当这个bean是实现了ApplicationContextAware
那么会进入对应的类去执行setApplicationContext方法
这样就能拿到应用程序上下文了,然后在其他方法就可以使用这个了
我们使用@PostConstruct和@PreDestroy这2个注解能执行
就是因为他的BeanPostProcessor类的postProcessBeforeInitialization方法
我们进入源码InitDestroyAnnotationBeanPostProcessor类
可以看到,在这里就能拿到初始化和销毁的方法,然后执行之后就进入了
对应的注解的方法