标签:调用顺序 顺序 row stp before 应用 factory 业务逻辑 order
什么是Spring?
Spring框架存在的意义就是为了降低耦合度, 根据不同的代码采用不同的方式, 通过IOC来降低主业务逻辑之间的耦合度, 通过AOP来降低系统级服务(如日志、安全、事务等)和主业务逻辑之间的耦合度. 此外还提供了一个Web层的框架Spring MVC.
在介绍Spring容器之前, 我们先介绍什么是bean. 简单来说, 被Spring容器管理的对象就叫bean, 比如Controller/Action, Service, Dao.
<bean id="userControllerId" class="org.a.IOC.UserController"></bean> <bean id="userServiceId" class="org.a.IOC.UserServiceImpl"></bean> <bean id="BookDaoId" class="org.a.IOC.UserDaoImpl"></bean>
Spring容器管理着项目中所有bean对象的实例化与装配, 有两种, 分别是 BeanFactory 和 ApplicationContext. 其中 ApplicationContext 是 BeanFactory 的一个子接口, 补充了以下几个功能:
上述几个功能只需了解就行, 对于两者的区别, 我们需要记住的是:
大部分情况下我们使用的都是ApplicationContext, 这篇博客下面的内容也都是基于ApplicationContext.
配置元数据
Spring容器通过读取元数据来获取要实例化、装配的对象. 元数据有三种格式, 分别是XML文件, Java注解和Java代码.
<beans> <bean id="BookServiceId" class="org.tyshawn.service.impl.BookServiceImpl"> <property name="bookDao" ref="BookDaoId"></property> </bean> <bean id="BookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl"></bean> </beans>
上面这段代码就是基于XML文件的元数据, ApplicationContext 的两个实现类 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 用来加载XML格式的元数据. 两者的区别在于 ClassPathXmlApplicationContext 是基于类路径, 而 FileSystemXmlApplicationContext 是基于文件路径.
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); ApplicationContext context = new FileSystemXmlApplicationContext("D:\\springtest\\src\\main\\resources\\spring\\bean.xml");
ApplicationContext的实现类AnnotationConfigApplicationContext用来加载Java注解格式的元数据.
@Configuration @ComponentScan(basePackages = "org.tyshawn") public class AppConfig { } AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
IoC也称为依赖注入(dependency injection, DI). 这是一个过程, 在这个过程中, 首先通过在对象实例上设置的属性来定义bean之间的依赖关系, 然后Spring容器在创建bean时注入这些依赖项(这个注入过程也叫做装配). 依赖注入有两种, 分别是基于构造方法的注入和基于Setter方法的注入.
基于构造方法注入
public interface IBookDao { void insert(); } public class BookDaoImpl implements IBookDao { @Override public void insert() { System.out.println("add book"); } } public interface IBookService { void addBook(); } public class BookServiceImpl implements IBookService { private IBookDao bookDao; public BookServiceImpl(IBookDao bookDao) { this.bookDao = bookDao; } @Override public void addBook() { this.bookDao.insert(); } } <beans> <bean id="BookServiceId" class="org.tyshawn.service.impl.BookServiceImpl"> <constructor-arg ref="BookDaoId"/> </bean> <bean id="BookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl"></bean> </beans> public class Test{ public static void main(String[] args) throws ParseException { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); IBookService bookService = (IBookService) context.getBean("BookServiceId"); bookService.addBook(); } }
基于Setter方法的注入
public interface IBookDao { void insert(); } public class BookDaoImpl implements IBookDao { @Override public void insert() { System.out.println("add book"); } } public interface IBookService { void addBook(); } public class BookServiceImpl implements IBookService { private IBookDao bookDao; public void setBookDao(IBookDao bookDao) { this.bookDao = bookDao; } @Override public void addBook() { this.bookDao.insert(); } } <beans> <bean id="BookServiceId" class="org.tyshawn.service.impl.BookServiceImpl"> <property name="bookDao" ref="BookDaoId"></property> </bean> <bean id="BookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl"></bean> </beans> public class Test{ public static void main(String[] args) throws ParseException { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); IBookService bookService = (IBookService) context.getBean("BookServiceId"); bookService.addBook(); } }
Bean的作用域有六种, 其中后四种只支持Web应用.
作用域 | 描述 |
---|---|
singleton | 默认. bean在每一个Spring容器内只有一个实例 |
prototype | 每次从Spring容器中获取到的bean都是一个新的实例 |
request | bean在每一个 HTTP Request 中只有一个实例, 只支持Web应用 |
session | bean在每一个 HTTP Session 中只有一个实例, 只支持Web应用 |
application | bean在每一个 ServletContext 中只有一个实例, 只支持Web应用 |
websocket | bean在每一个 WebSocket 中只有一个实例, 只支持Web应用 |
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/> <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/> <bean id="loginAction" class="com.something.LoginAction" scope="request"/> <bean id="userPreferences" class="com.something.UserPreferences" scope="session"/> <bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
singleton和application的区别
(1) 在作用域为singleton时, bean在每一个Spring容器内只有一个实例, 而应用可以有多个容器.
(2) 在作用域为application时, bean在整个应用中只有一个实例.
(3) 作用域application只支持Web应用.
具有多例bean依赖的单例bean
一个bean的作用域是 singleton, 而它的属性的作用域是 prototype, 如下所示:
<bean id="BookServiceId" class="org.tyshawn.service.impl.BookServiceImpl" scope="singleton"> <constructor-arg ref="BookDaoId"/> </bean> <bean id="BookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl" scope="prototype"></bean>
我们想要的效果是, 每次获取BookServiceId时都是同一个bean, 而它的属性BookDaoId都是一个新的bean. 但这种情况是不可能的, 因为BookServiceId只会实例化, 装载一次. 要想达到我们期望的效果, 需要使用方法注入:
方法注入
Spring框架通过使用来自CGLIB库的字节码生成器来动态生成覆盖该方法的子类来实现此方法注入.
public class BookServiceImpl implements IBookService { private IBookDao bookDao; public IBookDao getBookDao() { return bookDao; } @Override public void addBook() { IBookDao bookDao = getBookDao(); System.out.println(bookDao); } } <bean id="BookServiceId" class="org.tyshawn.service.impl.BookServiceImpl" scope="singleton"> <lookup-method name="getBookDao" bean="BookDaoId"></lookup-method> </bean> <bean id="BookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl" scope="prototype"></bean> public class Test{ public static void main(String[] args) throws ParseException { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); BookServiceImpl bookService1 = (BookServiceImpl) context.getBean("BookServiceId"); BookServiceImpl bookService2 = (BookServiceImpl) context.getBean("BookServiceId"); bookService1.addBook(); bookService2.addBook(); } } org.tyshawn.dao.Impl.BookDaoImpl@6121c9d6 org.tyshawn.dao.Impl.BookDaoImpl@87f383f
生命周期回调
(1) 初始化回调
在Spring容器将bean实例化, 设置属性值之后将会执行初始化回调. 初始化回调有两种设置方式:
方式一(推荐) <bean id="exampleInitBean1" class="org.tyshawn.example.ExampleBean" init-method="init"/> public class ExampleBean { public void init() { System.out.println("do some initialization work."); } } 方式二(不推荐) <bean id="exampleInitBean2" class="org.tyshawn.example.AnotherExampleBean"/> public class AnotherExampleBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("do some initialization work."); } } public class Test{ public static void main(String[] args) throws ParseException { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); ExampleBean example1 = (ExampleBean) context.getBean("exampleInitBean1"); AnotherExampleBean example2 = (AnotherExampleBean) context.getBean("exampleInitBean2"); System.out.println(example1); System.out.println(example2); } } do some initialization work. do some initialization work. org.tyshawn.example.ExampleBean@4eb7f003 org.tyshawn.example.AnotherExampleBean@eafc191
(2) 销毁回调
当bean被销毁之前, 将会执行销毁回调. 销毁回调有两种设置方式:
方式一(推荐) <bean id="exampleDestoryBean1" class="org.tyshawn.example.ExampleBean" destroy-method="destory"/> public class ExampleBean { public void destroy() { System.out.println("do some destruction work."); } } 方式二(不推荐) <bean id="exampleDestoryBean2" class="org.tyshawn.example.AnotherExampleBean"/> public class AnotherExampleBean implements DisposableBean { @Override public void destroy() { System.out.println("do some destruction work."); } } public class Test{ public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); ExampleBean example1 = (ExampleBean) context.getBean("exampleDestoryBean1"); AnotherExampleBean example2 = (AnotherExampleBean) context.getBean("exampleDestoryBean2"); //当容器被关闭时, 容器内的bean就被销毁了 context.registerShutdownHook(); } } do some destruction work. do some destruction work.
(3) 初始化回调 / 销毁回调的两种方式同时配置
当 初始化回调 / 销毁回调的两种方式同时配置时会出现什么结果呢?
<bean id="exampleDestoryBean2" class="org.tyshawn.example.AnotherExampleBean" destroy-method="cleanup"/> public class AnotherExampleBean implements DisposableBean { @Override public void destroy() { System.out.println("do some destruction work."); } public void cleanup() { System.out.println("do some cleanup work."); } } public class Test{ public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); AnotherExampleBean example2 = (AnotherExampleBean) context.getBean("exampleDestoryBean2"); context.registerShutdownHook(); } } do some destruction work. do some cleanup work.
结果是两种方式都执行, 但 DisposableBean / InitializingBean 在前, destroy-method / init-method 在后.
(4) 启动和关闭回调
如果Spring容器中的bean实现了 Lifecycle 接口, 当Spring容器启动时, 将会调用这些bean的start()方法, 当Spring容器关闭时, 将会调用这些bean的stop()方法.
public interface Lifecycle { void start(); void stop(); boolean isRunning(); }
在很多情况下, start()方法和stop()方法的调用顺序是重要的, 如果两个bean存在依赖关系, 比如 a 依赖 b (b是a的属性), 这时 a 先调用start()方法, b 先调用stop()方法. 但如果我们不知道依赖关系, 却想让 a 在 b 之前调用start()方法, 这时我们就可以用 SmartLifecycle 接口代替 Lifecycle 接口.
public interface Phased { int getPhase(); } public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); }
SmartLifecycle接口的方法介绍:
当 isAutoStartup() 返回true, Spring容器启动时会调用bean的 start() 方法
当 isRunning() 返回true, Spring容器销毁时会调用bean的 stop(Runnable runnable) 方法
getPhase() 返回的是优先级, 当有多个bean时, 返回值大的先执行start()方法, 销毁时顺序相反. 容器内没有实现SmartLifecycle接口, 而实现了Lifecycle接口的bean返回值是0. 负数代表最高优先级.
BeanPostProcessor
如果我们想在Spring容器完成bean的初始化前后加一些定制逻辑, 我们可以向容器注册一个或多个定制BeanPostProcessor实现. 当有多个BeanPostProcessor定制时, 我们同时要实现Ordered接口.
bean的生命周期
bean在Spring容器中的生命周期是怎么样的呢?
(1) 实例化
(2) 设置属性值
(3) 调用BeanNameAware的setBeanName()方法
(4) 调用BeanFactoryAware的setBeanFactory()方法
(5) 调用ApplicationContext的setApplicationContext()方法
(6) 调用BeanPostProcessor的postProcessBeforeInitialization()方法
(7) 调用InitializingBean的afterPropertiesSet()方法
(8) 调用xml配置的初始化方法
(9) 调用BeanPostProcessor的postProcessAfterInitialization()方法
(10) 容器启动.
(11) bean可以使用了.
(12) 容器关闭.
(13) 调用DisposableBean的destory()方法
(14) 调用xml配置的销毁方法
<bean id="exampleBean" class="org.tyshawn.example.ExampleBean" init-method="init" destroy-method="dest"/> <bean class="org.tyshawn.example.CustomBeanPostProcessor"/> public class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用BeanPostProcessor的postProcessBeforeInitialization()方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("调用BeanPostProcessor的postProcessAfterInitialization()方法"); return bean; } } public class ExampleBean implements InitializingBean, DisposableBean, SmartLifecycle, BeanNameAware, BeanFactoryAware, ApplicationContextAware { private boolean isRunning = false; public void init() { System.out.println("调用xml配置的初始化方法"); } public void dest() { System.out.println("调用xml配置的销毁方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("调用InitializingBean的afterPropertiesSet()方法"); } @Override public void destroy() { System.out.println("调用DisposableBean的destory()方法"); } @Override public boolean isAutoStartup() { return true; } @Override public void stop(Runnable runnable) { runnable.run(); System.out.println("容器关闭."); } @Override public void start() { isRunning = true; System.out.println("容器启动."); } @Override public void stop() { } @Override public boolean isRunning() { return isRunning; } @Override public int getPhase() { return -1; } @Override public void setBeanName(String beanName) { 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("调用ApplicationContext的setApplicationContext()方法"); } public void sayHello() { System.out.println("bean可以使用了."); } } public class Test{ public static void main(String[] args) throws Exception { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml"); ExampleBean example = (ExampleBean) context.getBean("exampleBean"); example.sayHello(); context.registerShutdownHook(); } } 调用BeanNameAware的setBeanName()方法 调用BeanFactoryAware的setBeanFactory()方法 调用ApplicationContext的setApplicationContext()方法 调用BeanPostProcessor的postProcessBeforeInitialization()方法 调用InitializingBean的afterPropertiesSet()方法 调用xml配置的初始化方法 调用BeanPostProcessor的postProcessAfterInitialization()方法 容器启动. bean可以使用了. 容器关闭. 调用DisposableBean的destory()方法 调用xml配置的销毁方法
FactoryBean
面试中常常问到FactoryBean和BeanFactory的区别, 我们已经知道BeanFactory是一个Spring容器, 应用中所有bean的实例都存储在其中, 那FactoryBean是什么呢?
FactoryBean是一个生成bean的工厂. 一般情况下我们都是用XML文件来配置bean, 但如果我们有复杂的初始化逻辑, 相对于冗长的XML, 用Java代码可以更好地表达, 这时我们就可以创建自己的FactoryBean, 在该类中编写复杂的初始化逻辑, 然后将定制的FactoryBean插入容器中.
标签:调用顺序 顺序 row stp before 应用 factory 业务逻辑 order
原文地址:https://www.cnblogs.com/xiaoxin-IT/p/13885096.html