classA{ AInterface a; A(){} //一個方法 AMethod() { a = new AInterfaceImp(); } } // 當AInterface有一個新的實現但願使用,那麼須要改變classA的代碼 // 構造器注入 classA{ AInterface a; A(AInterface a){ this.a = a; } } // setter注入 classA{ AInterface a; A(){} public void setA(AInterface a){ this.a = a; } }
Core模塊提供了框架的基本部分,包括IoC和依賴注入功能。java
SpEL模塊提供了一種強大的表達式語言,用於在運行時查詢和操做對象圖。web
Spring容器是Spring Framework的核心。 容器將建立對象,將它們鏈接在一塊兒,配置它們,並管理從建立到銷燬的整個生命週期。 Spring容器使用DI來管理組成應用程序的組件。spring
容器經過讀取提供的配置元數據來獲取有關要實例化,配置和組裝的對象的指令。 配置元數據能夠由XML,Java註釋或Java代碼表示。Spring IoC容器利用Java POJO類和配置元數據來生成徹底配置和可執行的系統或應用程序。數據庫
Spring提供兩種不一樣的容器編程
No | 容器和描述 |
---|---|
1 | 這是提供DI基本支持的最簡單容器,由org.springframework.beans.factory.BeanFactory接口定義。 BeanFactory和相關的接口,如BeanFactoryAware,InitializingBean,DisposableBean,仍然存在於Spring中,目的是向後兼容與Spring集成的大量第三方框架 |
2 | 此容器添加了更多特定於企業的功能,例如從屬性文件解析文本消息的功能以及將應用程序事件發佈到感興趣的事件偵聽器的功能。 此容器由org.springframework.context.ApplicationContext接口定義。 |
注:ApplicationContext包含BeanFactory的全部功能。設計模式
構成應用程序主幹並由Spring IoC容器管理的對象稱爲bean。 bean是一個由Spring IoC容器實例化,組裝和管理的對象。 這些bean是使用您提供給容器的配置元數據建立的。數組
No | 配置和描述 |
---|---|
1 | class 此屬性是必需的,並指定建立的bean類型 |
2 | name 此屬性惟一地指定bean標識符 |
3 | scope 指定bean的做用範圍,默認爲singleton、prototype..... |
4 | constructor-arg 用於注入依賴項 |
5 | properties 用於注入依賴項 |
6 | autowiring mode 用於注入依賴項 |
7 | lazy-initialization mode 在第一次請求時建立bean實例,不是在啓動時建立 |
8 | initialization method 在容器設置了bean以後全部必要屬性以後調用的回調。 |
9 | destruction method 當包含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-3.0.xsd"> <!-- 簡單類定義 --> <bean id = "..." class = "..."> <!-- 這個bean的協做者和配置就在這裏 --> </bean> <!-- 懶加載模式 --> <bean id = "..." class = "..." lazy-init = "true"> <!-- 這個bean的協做者和配置就在這裏 --> </bean> <!-- 配置初始化方法 --> <bean id = "..." class = "..." init-method = "..."> <!-- 這個bean的協做者和配置就在這裏 --> </bean> <!-- 配置銷燬方法 --> <bean id = "..." class = "..." destroy-method = "..."> <!-- 這個bean的協做者和配置就在這裏 --> </bean> </beans>
No | 做用域與描述 |
---|---|
1 | singlton 單例(默認) |
2 | prototype 多例 |
3 | request HTTP請求。(web) |
4 | session HTTP會話。(web) |
5 | global-session 全局HTTP會話(web) |
@Component public class SingletonBean { private String message; public void setMessage(String msg) { this.message = msg; } public void print() { System.out.println(message); } } @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } public static <T> T getBean(String beanName) { return (T) context.getBean(beanName); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); SingletonBean singletonBean = SpringContextUtil.getBean("singletonBean"); singletonBean.setMessage("singleton"); singletonBean.print(); // singleton SingletonBean singletonBean2 = SpringContextUtil.getBean("singletonBean"); singletonBean2.print();// singleton } }
@Component @Scope("prototype") public class PrototypeBean { private String message; public void setMessage(String msg) { this.message = msg; } public void print() { System.out.println(message); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); PrototypeBean prototypeBean = SpringContextUtil.getBean("prototypeBean"); prototypeBean.setMessage("prototype"); prototypeBean.print();// prototype PrototypeBean prototypeBean2 = SpringContextUtil.getBean("prototypeBean"); prototypeBean2.print();// null } }
Spring bean的生命週期很容易理解。 實例化bean時,可能須要執行一些初始化以使其進入可用狀態。 相似地,當再也不須要bean並將其從容器中移除時,可能須要進行一些清理。緩存
@Component public class LifeCycleBean implements InitializingBean,DisposableBean { @Value("${lifecycle.message}") private String message; // 構造方法後message纔有值 public void print() { System.out.println(message); } // 1 public LifeCycleBean() { System.out.println("Constructor"); } // 2 @PostConstruct public void init(){ System.out.println("@PostConstruct"); } // 3 @Override public void afterPropertiesSet() throws Exception { System.out.println("implements InitializingBean"); } // 4 @PreDestroy public void preDestory(){ System.out.println("@PreDestroy"); } // 5 @Override public void destroy() throws Exception { System.out.println("implements DisposableBean"); } }
BeanPostProcessor接口定義了您能夠實現的回調方法,以提供您本身的實例化邏輯,依賴關係解析邏輯等。您還能夠在Spring容器經過插入一個或多個實例化,配置和初始化bean以後實現一些自定義邏輯 BeanPostProcessor實現。安全
您能夠配置多個BeanPostProcessor接口,您能夠經過設置訂單屬性來控制這些BeanPostProcessor接口的執行順序,前提是BeanPostProcessor實現了Ordered接口。服務器
BeanPostProcessors在bean(或對象)實例上運行,這意味着Spring IoC容器實例化一個bean實例,而後BeanPostProcessor接口完成它們的工做。
ApplicationContext自動檢測使用BeanPostProcessor接口的實現定義的任何bean,並將這些bean註冊爲後處理器,而後在建立bean時由容器適當調用。
// 注意該類做用於全部的Spring Bean @Component public class BeanPostProcessors implements BeanPostProcessor,Ordered { // 2 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessBeforeInitialization " + beanName); return bean; } // 5 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessAfterInitialization " + beanName); return bean; } @Override public int getOrder() { return 1; } } @Component public class LifeCycleBean implements InitializingBean,DisposableBean { @Value("${lifecycle.message}") private String message; // 構造方法後message纔有值 public void print() { System.out.println(message); } // 1 public LifeCycleBean() { System.out.println("Constructor"); } // 3 @PostConstruct public void init(){ System.out.println("@PostConstruct"); } //4 @Override public void afterPropertiesSet() throws Exception { System.out.println("implements InitializingBean"); } // 6 @PreDestroy public void preDestory(){ System.out.println("@PreDestroy"); } // 7 @Override public void destroy() throws Exception { System.out.println("implements DisposableBean"); } }
實現Ordered並設定順序值
@Component public class BeanPostProcessors2 implements BeanPostProcessor,Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessBeforeInitialization2 " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("lifeCycleBean")) System.out.println("postProcessAfterInitialization2 " + beanName); return bean; } @Override public int getOrder() { return 2; } }
bean定義能夠包含許多配置信息,包括構造函數參數,屬性值和特定於容器的信息,例如初始化方法,靜態工廠方法名稱等。
子類bean定義從父定義繼承配置數據。子定義能夠根據須要覆蓋某些值或添加其餘值。
Spring Bean定義繼承與Java類繼承無關,但繼承概念是相同的。 您能夠將父bean定義定義爲模板,其餘子bean能夠從父bean繼承所需的配置。
使用基於XML的配置元數據時,可使用parent屬性指定子bean定義,並將父bean指定爲此屬性的值。
<!-- app-conf-1.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="animal" class="com.concretepage.Animal" init-method="initA"> <property name="name" value="Hathi"/> <property name="age" value="20"/> </bean> <bean id="elephant" class="com.concretepage.Elephant" parent="animal" init-method="initE"> <property name="age" value="30"/> <property name="location" value="Varanasi"/> </bean> </beans> <!-- 注意目前不知道如何使用註解替代:parent 和 abstract -->
public class Animal { private String name; private Integer age; public void initA() { System.out.println("Inside initA()"); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Elephant extends Animal { private String location; public void initE() { System.out.println("Inside initE()"); } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } } public class SpringDemo { public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-conf-1.xml"); Elephant elephant=(Elephant)context.getBean("elephant"); System.out.println(elephant.getLocation()); // Varanasi System.out.println(elephant.getName()); // Hathi System.out.println(elephant.getAge()); // 30 context.registerShutdownHook(); } }
public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } public void setSpellChecker(SpellChecker spellChecker) { this.spellChecker = spellChecker; } } @Configuration @Import(ConfigA.class) // 注入 public class Config{ // 方式1:構造器注入 @Bean public TextEditor textEditor(){ return new TextEditor(spellChecker()); } // 方式2:setter注入 @Scope("prototype") @Bean(initMethod = "init", destroyMethod = "cleanup") public TextEditor textEditor(){ TextEditor textEditor = new TextEditor(); textEditor.setSpellChecker(spellChecker()); return textEditor; } @Bean public SpellChecker spellChecker(){ return new SpellChecker(); } }
// 默認第一種方式最簡單,無需寫構造器和setter方法 @Component public class TextEditor { @Autowired // 方式一 private SpellChecker spellChecker; @Autowired // 方式二 @Qualifier("spellChecker") // 根據名稱指定 public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } // 方式二 JSR-250 @Resource(name = "spellChecker") public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker; } @Autowired // 方式三 public void setSpellChecker(SpellChecker spellChecker) { this.spellChecker = spellChecker; } }
// 數組 @Value("${name.list}") private String[] names; // 列表 @Value("#{T(java.util.Arrays).asList('${name.list:a,b,c}')}") private List<String> list; // 列表 @Value("${name.list}") private List<String> names; // Set @Value("${name.list}") private Set<String> names; // LinkedHashSet // map @Value("#{${valuesMap}}") private Map<String, Integer> valuesMap; // map.get(key):key不存在時報錯 @Value("#{${valuesMap}.key1}") private Integer valuesMapKey1; // 安全方式不會報錯,設置爲null(默認值) @Value("#{${valuesMap}['unknownKey']}") private Integer unknownMapKey; // 設置默認值 @Value("#{${unknownMap : {key1: '1', key2: '2'}}}") private Map<String, Integer> unknownMap; @Value("#{${valuesMap}['unknownKey'] ?: 5}") private Integer unknownMapKeyWithDefaultValue; // 過濾 @Value("#{${valuesMap}.?[value>'1']}") private Map<String, Integer> valuesMapFiltered; @Value("#{systemProperties}") private Map<String, String> systemPropertiesMap; // application.properties name.list=d,e,f valuesMap={key1: '1', key2: '2', key3: '3'}
您已經在全部章節中看到Spring的核心是ApplicationContext,它管理bean的完整生命週期。 ApplicationContext在加載bean時發佈某些類型的事件。 例如,在啓動上下文時發佈ContextStartedEvent,並在上下文中止時發佈ContextStoppedEvent。
ApplicationContext中的事件處理是經過ApplicationEvent類和ApplicationListener接口提供的。 所以,若是bean實現了ApplicationListener,那麼每次將ApplicationEvent發佈到ApplicationContext時,都會通知該bean。
No | Spring內置事件 |
---|---|
1 | ContextRefreshedEvent 在初始化或刷新ApplicationContext時發佈此事件。這也可使用ConfigurableApplicationContext接口上的refresh()方法引起。 |
2 | ContextStartedEvent 使用ConfigurableApplicationContext接口上的start()方法啓動ApplicationContext時,將發佈此事件。您能夠輪詢數據庫,也能夠在收到此事件後從新啓動任何已中止的應用程序。 |
3 | ContextStoppedEvent 使用ConfigurableApplicationContext接口上的stop()方法中止ApplicationContext時,將發佈此事件。收到此活動後,您能夠進行必要的清理工做。 |
4 | ContextClosedEvent 使用ConfigurableApplicationContext接口上的close()方法關閉ApplicationContext時,將發佈此事件。封閉的環境達到其生命的終點;它沒法刷新或從新啓動。 |
5 | RequestHandledEvent 這是一個特定於Web的事件,告訴全部bean已經爲HTTP請求提供服務。 |
public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); context.start(); // 發出ContextStartedEvent事件 context.stop(); // 發出ContextStoppedEvent事件 } @Component public class SpringEventListener { // 1 @EventListener public void contextRefreshedEvent(ContextRefreshedEvent event) { System.out.println("ContextRefreshedEvent "+event); } // 2 @EventListener public void contextStartedEvent (ContextStartedEvent event) { System.out.println("ContextStartedEvent "+event); } // 3 @EventListener public void contextStoppedEvent (ContextStoppedEvent event) { System.out.println("ContextStoppedEvent "+event); } // 4 @EventListener public void contextClosedEvent (ContextClosedEvent event) { System.out.println("ContextClosedEvent "+event); } }
public class CustomEvent extends ApplicationEvent { public CustomEvent(Object source) { super(source); } public String toString(){ return "My Custom Event"; } } @Component public class CustomPublisher implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } public void publish() { CustomEvent ce = new CustomEvent(this); publisher.publishEvent(ce); } } // 方式1 @Component public class CustomEventHandler implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { System.out.println(event); } } // 方式2 @Component public class CustomEventListener { @EventListener public void listen(CustomEvent customEvent) { System.out.println(customEvent); } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); CustomPublisher customPublisher = (CustomPublisher) context.getBean("customPublisher"); customPublisher.publish(); } }
面向方面編程須要將程序邏輯分解爲稱爲橫切關注點的不一樣部分。 跨越應用程序多個點的功能稱爲跨領域問題,這些跨領域問題在概念上與應用程序的業務邏輯分離。 有許多常見的好例子,如日誌記錄,審計,聲明式事務,安全性,緩存等。
OOP中模塊化的關鍵單元是類,而在AOP中,模塊化單元是方面。 依賴注入能夠幫助您將應用程序對象相互分離,AOP能夠幫助您將交叉問題與它們所影響的對象分離。 AOP就像Perl,.NET,Java等編程語言中的觸發器。
Spring AOP模塊提供攔截器來攔截應用程序。例如,執行方法時,能夠在方法執行以前或以後添加額外的功能
術語 | 描述 |
---|---|
Aspect 切面 | 切面是通知和切點的結合。例如,一個日誌模塊爲了記錄日誌將被 AOP 方面調用。應用程序能夠擁有任意數量的方面,這取決於需求。 |
Join point 鏈接點 | 程序執行過程當中的某個特定的點,或者說特定的時候。 |
Advice 通知 | 在 Spring AOP 中,有前置通知、後置通知、異常通知、最終通知、環繞通知 5 種。 |
Pointcut | 切點是用來匹配定位鏈接點的。一組一個或多個鏈接點,通知應該被執行。 |
Weaving 織入 | 切面應用到鏈接點中 |
Introduction 引用 | 引用容許你添加新方法或屬性到現有的類中。 |
Target 目標類 | 被一個或者多個方面所通知的對象,這個對象永遠是一個被代理對象。 |
通知 | 描述 |
---|---|
前置通知 | 在一個方法執行以前,執行通知。 |
後置通知 | 在一個方法執行以後,不考慮其結果,執行通知。 |
返回後通知 | 在一個方法執行以後,只有在方法成功完成時,才能執行通知。 |
拋出異常後通知 | 在一個方法執行以後,只有在方法退出拋出異常時,才能執行通知。 |
環繞通知 | 在建議方法調用以前和以後,執行通知。 |
// Aspect @Component @Aspect public class TimeLoggingAspect { // Advice @Around("execution(* com.littleevil.autowiredtest.aop.UserService.*(..))") // Pointcut public Object userAdvice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("@Around: Before calculation-" + new Date()); Object result = joinPoint.proceed(); System.out.println("@Around: After calculation-" + new Date()); return result; } } // Target @Service public class UserService { public Integer multiply(int a, int b){ int res = a*b; System.out.println(a+ "*" + b +"= " + res); return res; } } @SpringBootApplication public class MainApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args); UserService userService = (UserService) context.getBean("userService"); Integer res = userService.multiply(1, 3); System.out.println(res); } }