Bean这个对象从被spring创建出来,new出来,到最后销毁的过程,是spring的基础,之前一直只是使用各种对象,对各种对象的使用的原理不了解,这次深入了解一下。
第一步就是将Bean对象new出来。放到内存里。
第二步就是在需要这个对象的地方,把刚刚new出来的对象放过去,这个就是依赖注入。需要这个对象的地方都会有注解@autowired
第三步就是执行Aware接口,如果对象实现了BeanNameAware等接口,就会调用对应的方法,传入对应的参数。
第四步:调用BeanPostProcessor.postProcessBeforeInitialization()方法,可以在Bean对象初始化之前做点什么。
第五步:对象的初始化,这一步就是要做加载缓存、连接数据库之类的操作。
第六步:Bean生成一个代理对象,记录对象的日志,监控对象的性能。
第七步:使用对象。
第八步:调用bean对象的销毁方法,释放对象占用的资源,比如关闭数据库连接,关闭线程池等等。
以上可以简化为5步,创建、属性赋值、初始化、使用、销毁。
示例代码
import org.springframework.stereotype.Component;
@Component
public class Computer {
public Computer() {
System.out.println("--- [依赖组件] HR 去采购了一台新电脑 (Computer 实例化) ---");
}
}
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class EmployeeBean implements BeanNameAware, InitializingBean, DisposableBean {
private Computer computer;
private String beanName;
// 【第1步】:实例化 (Constructor)
public EmployeeBean() {
System.out.println("第1步:[实例化] EmployeeBean 的构造方法执行,HR 招人入职,分配空工位...");
}
// 【第2步】:属性赋值 (Dependency Injection)
@Autowired
public void setComputer(Computer computer) {
this.computer = computer;
System.out.println("第2步:[属性赋值] Spring 注入 Computer,公司给员工发电脑...");
}
// 【第3步】:感知环境 (Aware接口)
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("第3步:[Aware接口] Spring 告诉打工人,你在公司的工号(BeanName)是:" + name);
}
// 【第5步】:初始化-注解方式 (@PostConstruct)
@PostConstruct
public void init() {
System.out.println("第5步:[初始化-@PostConstruct] 员工自己装 IDEA,配环境...");
}
// 【第5步】:初始化-接口方式 (InitializingBean)
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第5步:[初始化-InitializingBean] 环境配置完毕,大喊一声:老板我准备好了!");
}
// 【第7步】:业务逻辑被正常调用
public void work() {
System.out.println("第7步:[正常使用] 业务方法执行,打工人开始疯狂 996 撸代码...");
}
// 【第8步】:销毁-注解方式 (@PreDestroy)
@PreDestroy
public void preDestroy() {
System.out.println("第8步:[销毁-@PreDestroy] 公司倒闭准备停机,员工交还电脑,删除跑路...");
}
// 【第8步】:销毁-接口方式 (DisposableBean)
@Override
public void destroy() throws Exception {
System.out.println("第8步:[销毁-DisposableBean] 打工人正式离职,彻底拜拜!");
}
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
// 【第4步】:初始化前置处理
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 为了控制台干净点,我们只拦截打印 employeeBean
if ("employeeBean".equals(beanName)) {
System.out.println("第4步:[BeanPostProcessor前置] HR 岗前谈话:马上要干活了,准备好了吗?");
}
return bean; // 把 Bean 原封不动返回
}
// 【第6步】:初始化后置处理(敲黑板:AOP 的诞生之地!)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("employeeBean".equals(beanName)) {
System.out.println("第6步:[BeanPostProcessor后置] HR 观察员工:这小伙子不错,如果加了切面,就在这给他包一层【动态代理】!");
}
return bean; // 如果有AOP,这里返回的就不再是原对象,而是代理对象了!
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class LifecycleApplication {
public static void main(String[] args) {
System.out.println("========== 公司(Spring 容器) 开门营业 ==========");
// 启动 Spring 容器
ConfigurableApplicationContext context = SpringApplication.run(LifecycleApplication.class, args);
System.out.println("========== 容器启动完毕,准备接客 ==========");
// 从容器里抓出我们的打工人,让他干活
EmployeeBean employee = context.getBean(EmployeeBean.class);
employee.work();
System.out.println("========== 公司(Spring 容器) 准备关门破产 ==========");
// 优雅关闭容器,触发销毁回调
context.close();
}
}
AOP,面向切面编程
刚刚知道这个概念的时候,一头雾水,看了概念内容之后发现还挺好理解的。
其实感觉就是把常用的功能都抽离出来,单独弄一个模块。把业务流程中的不核心的内容而且要重复写的代码都统一放到一个地方去。
一般有这么几个使用的地方:
- 日志记录:一个接口调用的时候,调用之前记录一下时间,调用之后记录一下时间,每一个接口不能都写一遍这个实现,所以需要一个AOP。
- 权限校验:AOP在请求头里面检查token,就不需要在业务代码里面检查了。
- 业务管理:这个看起来挺有用的,@Transactional注解实现的mysql写入回滚其实就是AOP,在业务逻辑执行之前,AOP向mysql发送BEGIN,然后执行业务逻辑,执行成功就COMMIT,如果执行不成功,比如抛异常,那么就ROLLBACK。