---
# System prepended metadata

title: SPRING

---

# SPRING

## 核心概念

### Spring五大層17模組:
**A.核心容器(Core Container)**: Spring主要核心容器層
1.)Core模組:核心功能
2.)Beans模組:內有BeanFactory元件
3.)Context模組:內有ApplicationContext元件
4.)ExpressionLanguage模組(EL):延伸自JSP 2.1
**B.AOP層**
5.)AOP模組:設計AOP程式時使用
6.)Aspect模組
7.)Instrumentation模組
**C.資料存取/整合層(Data Access/Integration)**
8.)JDBC模組
9.)ORM模組:可使用O/R-mapping框架功能，如JPA、Hibernate等
10.)OXM模組
11.)JMS模組
12.)Transaction模組:專案中有交易管理時須使用到的模組
**D.Web層**
13.)Web模組
14.)Web-Servlet模組:Spring MVC會使用到的模組
15.)Web-Struts模組
16.)Web-Portlet模組
**E.Test層**
17.)Test模組

### IoC模式:
中文"控制反轉"，主要使模組都依賴於抽象，實作去依賴抽象，應用程式不依賴於容器，好處是可以降低物件之間的耦合，也可以避免應用程式依賴於容器的功能，IoC模式有兩種實現方式:**Dependency Injection**及**Service Locator**，而Spring使用的技術是Dependency Injection(DI)

### Dependency Injection(DI):
中文"依賴注入"，主要意義是透過抽象介面來注入依賴的實際物件，而DI主要有三種實現方式:
type 1 IoC: Interface injection(強侵入性，不使用)
type 2 Ioc: Setter injection (Spring鼓勵使用該種)
type 3 IoC: Constructor injection(Spring也允許使用)

### 代理機制(proxy)
在討論AOP前，要先了解AOP的前身代理機制，Java代理模式最常見的有兩種:
**靜態代理（Static proxy）與動態代理（Dynamic proxy）**
在靜態代理的實現中，代理物件與被代理的物件都必須實作同一介面，如此當有其他類別需要用到時則需要重寫代理物件
動態代理則不必為特定物件與方法撰寫特定的代理物件，主要是利用Proxy.newProxyInstance()建立代理物件，並實作InvocationHandler的代理類別，當實作InvocationHandler時必須實作invoke方法

### AOP，全名Aspect-Oriented Programming，切面導向程式設計
-有時在應用程式的主服務流程必須插入一些與主服務流程本身無關的系統考量邏輯(如Loggin登入、Transaction交易等)，而這些切入的邏輯稱之為:Cross-cutting concerns(橫切關注點)，而從中獨立出來的物件則稱之為Aspect(切面)
-AOP著重在Aspect的設計及與應用程式間的**Weave(縫合)**
-**JoinPoint**: 指服務流程中，程式執行過程的一個執行點
-**PointCut**: 為JoinPoint的集合，通常會利用PointCut Expression表示要將Aspect織入那些地方
-**Advice**: 在JoinPoint上要執行的程式碼，而每個Advice都需要一個PointCut做依據(決定在哪執行)
-**Target Object**: 指被Aspect通知的物件，由於SpringAOP使用動態代理，所以此物件為被代理物件


## 第一支spring程式
1.	產生一個Maven普通Java專案(或一般Java專案)
2.	載入需要pom檔(spring-context)
3.	寫一支類別名為HelloWorld的Java Bean
4.	Spring組態設定檔(xml版)，至少需有以下設定
```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 -->
	<bean id="hello" class="com.test.HelloWorld"/>
</beans>

```
4.	拿到Spring產生的bean
```java
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
HelloWorld bean = context.getBean("hello", HelloWorld.class);
//也可寫成  HelloWorld bean = (HelloWorld)context.getBean("hello ");
```

## 注入bean的幾個方式
在spring.xml加入bean後，

### ApplicationContext: 
利用ApplicationContext並實作指向的組態檔，再拿指定id的bean

### @Resource: 
spring.xml加入
```xml
<bean class = "org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
```
讓spring知道要接管annotation，後在宣告類別變數時加上@Resource(name=" ")拿到指定名稱的bean

### constructor injection: 
以SessionFactory為例
先在spring.xml宣告好SessionFactory後，再加入
```xml
<bean id="hello " class="com.test.HelloWorld (被注入類別)">
	<constructor-arg ref="SessionFactory" />
</bean>
```
，之後到被注入類別宣告
```java
private SessionFactory sessionFactory;
public HelloWorld(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
```
建構子注入會在一開始就加入倚賴，而屬性注入Field Injection則不會，所以可能會有意外出現nullPointException的情況發生

## important annotation
4個宣告在類別，告訴Spring受IoC控管用的annotation:
**@Component**: 代表為普通Bean元件
**@Repository**: 代表為DAO元件
**@Service**:代表為Service元件
**@Controller**:代表為Controller元件
以上4個都有value屬性，可以在後面加上(value="xxx")，當不加時，value預設的id為類別名稱第1個字改小寫，可讓component-scan掃描到
4個宣告在類別，Bean元件的有效範圍(scope):
**@Scope(scopeName="singleton")**:使用相同id去getBean()每次都會拿到同樣的物件
**@Scope(scopeName="prototype")**: 使用相同id去getBean()每次都會拿到全新物件
**@Scope(scopeName="request")**:用在Web應用程式，在同個HTTP request，用相同id去getBean()每次都會拿到同樣的物件
**@Scope(scopeName="session")**:用在Web應用程式，在同個HTTP session，用相同id去getBean()每次都會拿到同樣的物件

@PostConstruct:宣告在方法上，在DI完成後會優先執行該段初始化方法
@PreDestroy:宣告在方法上，在Bean Container關閉之前執行的方法



## Bean的生命週期:
主要有這四個階段:
**產生實體**（Instantiation）
**屬性賦值**（Populate）
**初始化**（Initialization）
**銷毀**（Destruction）

過程中可能被調用的幾個重要方法:
**postProcessBeanFactory()**: 所有的bean definitions已經被載入到bean factory之中，但還未產生實體前

**postProcessBeforeInstantiation(Class beanClass, String beanName)**：在bean產生實體之前調用
**postProcessProperties(PropertyValues pvs, Object bean, String beanName)**：在bean產生實體之後、設置屬性前調用
**postProcessAfterInstantiation(Class beanClass, String beanName)**：在bean產生實體之後調用

**setBeanName(String name)** 方法，獲取 bean 的 id 或者 name

**setBeanFactory(BeanFactory beanFactory)** 方法，獲取當前環境中的 BeanFactory

**setApplicationContext(ApplicationContext applicationContext)**，用來獲取當前環境中的 ApplicationContext。

**afterPropertiesSet()**，在屬性注入完成後調用。

**postProcessBeforeInitialization(Object bean, String beanName)**：在初始化之前調用此方法
**postProcessAfterInitialization(Object bean, String beanName)**：在初始化之後調用此方法

**destroy()方法**: 摧毀bean時使用

## BeanFactory vs. ApplicationContext
BeanFactory使用**延遲加載**，當需要bean時才產生，也是ApplictionContext的父介面
ApplictionContext則是**一次加載**，所以內存佔據較多，同時，也增加了許多高級容器功能，如MessageSource

## ApplicationContext
Spring兩大常用ApplicationContext:
**AnnotationConfigApplicationContext**: Java類別組態
**ClassPathXmlApplicationContext**: Xml格式組態
較常見使用情境:
系統內建類別:由於沒有Source code，故會使用XML組態或Java組態設定
自訂類別:由於有Source code，故會直接使用annotation組態設定

## Spring容器在web的註冊
要在Servlet拿到spring容器必須加入以下，告知組態檔的位置，而contextLoaderListener則是用來**監聽web容器啟動時觸發容器初始化事件**，監聽到這個事件後其**contextInitialized方法**就會被調用
```xml
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:application-context.xml
    </param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
```