Spring框架存在的意義就是爲了下降耦合度, 根據不一樣的代碼採用不一樣的方式, 經過IOC來下降主業務邏輯之間的耦合度, 經過AOP來下降系統級服務(如日誌、安全、事務等)和主業務邏輯之間的耦合度. 此外還提供了一個Web層的框架Spring MVC.git
IOC Spring最核心的模塊web
DAO Spring對訪問數據庫的支持,主要是JDBC面試
MVC Spring對MVC設計模式的支持(SSM)spring
AOP (Aspect Orientied Programing)面向切面編程數據庫
ORM 對象關係映射 Spring 和ORM框架整合 Mybatis編程
在介紹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.bash
Spring容器經過讀取元數據來獲取要實例化、裝配的對象. 元數據有三種格式, 分別是XML文件, Java註解和Java代碼. 本文只介紹XML格式和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();
}
}
複製代碼
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不是同一個
public class Test{
public static void main(String[] args) throws ParseException {
ApplicationContext context1 = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
ApplicationContext context2 = new FileSystemXmlApplicationContext("D:\\springtest\\src\\main\\resources\\spring\\bean.xml");
IBookService bookService1 = (IBookService) context1.getBean("BookServiceId");
IBookService bookService2 = (IBookService) context2.getBean("BookServiceId");
System.out.println(bookService1);
System.out.println(bookService2);
}
}
org.tyshawn.service.impl.BookServiceImpl@23faf8f2
org.tyshawn.service.impl.BookServiceImpl@2d6eabae
複製代碼
一個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. 負數表明最高優先級.
<bean id="exampleDestoryBean" class="org.tyshawn.example.ExampleBean" />
public class ExampleBean implements SmartLifecycle {
private boolean isRunning = false;
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable runnable) {
runnable.run();
System.out.println("stop runnable ...");
}
@Override
public void start() {
isRunning = true;
System.out.println("start run ...");
}
@Override
public void stop() {
System.out.println("stop run ...");
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public int getPhase() {
return -1;
}
}
public class Test{
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
ExampleBean example = (ExampleBean) context.getBean("exampleDestoryBean");
System.out.println(example);
context.registerShutdownHook();
}
}
start run ...
org.tyshawn.example.ExampleBean@1b26f7b2
stop runnable ...
複製代碼
(5)初始化回調/銷燬回調 和 啓動/關閉回調的執行順序
在瞭解了初始化回調/銷燬回調 和 啓動/關閉回調以後, 咱們確定很想知道當他們同時存在時, 他們的執行順即是怎麼樣的, 以下:
<bean id="exampleBean" class="org.tyshawn.example.ExampleBean" init-method="init" destroy-method="destroy"/>
public class ExampleBean implements SmartLifecycle{
private boolean isRunning = false;
public void init() {
System.out.println("do some init work.");
}
public void destroy() {
System.out.println("do some destruction work.");
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable runnable) {
runnable.run();
System.out.println("stop runnable ...");
}
@Override
public void start() {
isRunning = true;
System.out.println("start run ...");
}
@Override
public void stop() {
System.out.println("stop run ...");
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public int getPhase() {
return -1;
}
}
public class Test{
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
ExampleBean example1 = (ExampleBean) context.getBean("exampleBean");
context.registerShutdownHook();
}
}
do some init work.
start run ...
stop runnable ...
do some destruction work.
複製代碼
從上面的實例可知, 容器啓動時先執行初始化回調, 再執行啓動回調. 容器關閉時, 先執行關閉回調, 再執行銷燬回調.
Spring提供了一系列以Aware結尾的接口, 翻譯爲XXX可感知, 做用是若是某個bean須要Spring提供一些特定的依賴, 就實現對應的XXXAware接口. 下面是咱們經常使用三個Aware接口:
接口 | 做用 |
---|---|
BeanNameAware | 提供聲明bean時的id |
BeanFactoryAware | 提供BeanFactory容器 |
ApplicationContextAware | 提供ApplicationContext容器 |
(1) BeanNameAware
<bean id="exampleDestoryBean" class="org.tyshawn.example.ExampleBean" />
public class ExampleBean implements BeanNameAware{
@Override
public void setBeanName(String beanName) {
System.out.println("beanName: " + beanName);
}
}
public class Test{
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
ExampleBean example = (ExampleBean) context.getBean("exampleDestoryBean");
}
}
beanName: exampleDestoryBean
複製代碼
(2) BeanFactoryAware
<bean id="exampleBean" class="org.tyshawn.example.ExampleBean" />
public class AnotherExampleBean implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContext: " + applicationContext);
}
}
public class Test{
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
ExampleBean example = (ExampleBean) context.getBean("exampleBean");
}
}
BeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@2344fc66: defining beans [exampleBean]; root of factory hierarchy
複製代碼
(3) ApplicationContextAware
<bean id="exampleBean" class="org.tyshawn.example.AnotherExampleBean" />
public class AnotherExampleBean implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContext: " + applicationContext);
}
}
public class Test{
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
AnotherExampleBean example = (AnotherExampleBean) context.getBean("exampleBean");
}
}
ApplicationContext: org.springframework.context.support.ClassPathXmlApplicationContext@573fd745: startup date [Thu Apr 18 18:59:43 CST 2019]; root of context hierarchy
複製代碼
BeanPostProcessor
若是咱們想在Spring容器完成bean的初始化先後加一些定製邏輯, 咱們能夠向容器註冊一個或多個定製BeanPostProcessor實現. 當有多個BeanPostProcessor定製時, 咱們同時要實現Ordered接口.
<bean id="exampleBean" class="org.tyshawn.example.AnotherExampleBean" init-method="init"/>
<bean class="org.tyshawn.example.CustomBeanPostProcessor1"/>
<bean class="org.tyshawn.example.CustomBeanPostProcessor2"/>
public class CustomBeanPostProcessor1 implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean執行init()方法以前的定製邏輯1.");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean執行init()方法以後的定製邏輯1.");
return bean;
}
@Override
public int getOrder() {
return 2;
}
}
public class CustomBeanPostProcessor2 implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean執行init()方法以前的定製邏輯2.");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean執行init()方法以後的定製邏輯2.");
return bean;
}
@Override
public int getOrder() {
return 1;
}
}
public class AnotherExampleBean {
public void init() {
System.out.println("init() ...");
}
}
public class Test{
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
AnotherExampleBean example = (AnotherExampleBean) context.getBean("exampleBean");
}
}
bean執行init()方法以前的定製邏輯2.
bean執行init()方法以前的定製邏輯1.
init() ...
bean執行init()方法以後的定製邏輯2.
bean執行init()方法以後的定製邏輯1.
複製代碼
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和BeanFactory的區別, 咱們已經知道BeanFactory是一個Spring容器, 應用中全部bean的實例都存儲在其中, 那FactoryBean是什麼呢?
FactoryBean是一個生成bean的工廠. 通常狀況下咱們都是用XML文件來配置bean, 但若是咱們有複雜的初始化邏輯, 相對於冗長的XML, 用Java代碼能夠更好地表達, 這時咱們就能夠建立本身的FactoryBean, 在該類中編寫複雜的初始化邏輯, 而後將定製的FactoryBean插入容器中.
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
複製代碼
FactoryBean接口的方法介紹:
<bean id="tool" class="org.tyshawn.example.ToolFactory">
<property name="toolName" value="iphone xs"/>
</bean>
public class Tool {
private String name;
public Tool(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ToolFactory implements FactoryBean<Tool> {
private String toolName;
@Override
public Tool getObject() throws Exception {
return new Tool(toolName);
}
@Override
public Class<?> getObjectType() {
return Tool.class;
}
@Override
public boolean isSingleton() {
return false;
}
public String getToolName() {
return toolName;
}
public void setToolName(String toolName) {
this.toolName = toolName;
}
}
public class Test{
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/bean.xml");
Tool tool = (Tool) context.getBean("tool");
System.out.println(tool.getName());
}
}
iphone xs
複製代碼
(1) 容器配置
@ComponentScan用來配置類掃描路徑.
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
}
複製代碼
等同於XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
複製代碼
(2) 聲明bean
在基於XML的配置中, 下面這行代碼聲明瞭一個bean.
<bean id="bookDaoId" class="org.tyshawn.dao.Impl.BookDaoImpl"></bean>
複製代碼
在基於註解的容器配置中, 咱們能夠經過如下註解聲明一個bean.
@Autowired 爲按類型注入屬性, 若是注入屬性有多個時, 須要經過 @Qualifier 指定名稱. @Resource = @Autowired + @Qualifier.
@Configuration
@ComponentScan(basePackages = "org.tyshawn")
public class AppConfig {
}
@Repository("bookDao")
public class BookDaoImpl implements IBookDao {
@Override
public void insert() {
System.out.println("add book");
}
}
@Service("bookService")
public class BookServiceImpl implements IBookService {
// @Autowired
// @Qualifier(value = "bookDao")
@Resource(name = "bookDao")
private IBookDao bookDao;
@Override
public void addBook() {
bookDao.insert();
}
}
public class Test{
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
IBookService bookService = context.getBean("bookService", BookServiceImpl.class);
bookService.addBook();
}
}
add book
複製代碼
(3) 做用域
@Configuration
@ComponentScan(basePackages = "org.tyshawn")
public class AppConfig {
}
@Component("exampleBean")
@Scope("prototype")
public class ExampleBean {
}
public class Test{
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ExampleBean exampleBean1 = context.getBean("exampleBean", ExampleBean.class);
ExampleBean exampleBean2 = context.getBean("exampleBean", ExampleBean.class);
System.out.println(exampleBean1);
System.out.println(exampleBean2);
}
}
org.tyshawn.example.ExampleBean@64c87930
org.tyshawn.example.ExampleBean@400cff1a
複製代碼
(4) 生命週期回調
@Configuration
@ComponentScan(basePackages = "org.tyshawn")
public class AppConfig {
}
@Component("exampleBean")
public class ExampleBean {
@PostConstruct
public void init() {
System.out.println("初始化方法");
}
@PreDestroy
public void destroy() {
System.out.println("銷燬方法");
}
}
public class Test{
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ExampleBean exampleBean = context.getBean("exampleBean", ExampleBean.class);
System.out.println(exampleBean);
context.registerShutdownHook();
}
}
初始化方法
org.tyshawn.example.ExampleBean@5d47c63f
銷燬方法
複製代碼
關於Spring的介紹到此結束, 對於新手來講這些內容足夠咱們瞭解Spring是個什麼技術, 也足夠咱們去應付面試官了. 若是想要更深層次的瞭解Spring, 能夠去看官方文檔, 閱讀源碼.
來源:t.im/kzfp
做者的開源項目推薦:
關注公衆號回覆開源項目便可獲取