---
# System prepended metadata

title: 0518 Spring 動態網站開發  (重點是降低依賴)

---

# 0518 Spring 動態網站開發  (重點是降低依賴)

==spring 的功用是降低依賴，降低物件間的依賴，只是把依賴轉移出去==

```
//jsp存在這，但是大小寫不能改，會找不到，  誰找不到?

C:\Action\workspace\.metadata\.plugins
\org.eclipse.wst.server.core\tmp0
\work\Catalina\localhost\HibernateWebProject
```

logprovider被依賴(dao明星，明星自己可以做事)

誰new 表示誰依賴另一個人，他靠另一個人做事(service助理)
叫別人做事表示依賴類別


---

幫你new的方式
1. 呼叫setter di
2. constructor 

再透過@Autowired自動搜尋

---

AOP機制，在不更動原來程式碼之下，透過spring框架特性，間接把程式碼嵌入進去

出問題不會影響到原來的程式碼，只是把功能刪除

---

![](https://hackmd.io/_uploads/r1tjerEr3.png)

javax是tomcat 9.0版本以前

hibernate 和mybatis都是透過ORM

SSH: Spring + Struts(MVC)+ Hibernate

 Struts 以前習慣用.do當進入點
 
 新的SSH: SSH: Spring + SpringMVC+ Hibernate
 
 SpringBoot(內崁入tomcat +hibernate)
 
 i18 處理時區跟編碼
 
 spring framework讓你不用再用servlet，只要用單純java

i18n處理時間/編碼/資料連結、轉換，AOP

https://spring.io/projects/spring-framework

查全世界熱門的前10大程式
不要學錯

學錯後悔終生

![](https://hackmd.io/_uploads/HJDyWHNrn.png)

可以活5-10年

---

message queue
sender->line server(queue)->接收者

---

firebase 訊息推撥

---

![](https://hackmd.io/_uploads/B1hXbr4H2.png)

Google Maps JavaScript API
openstreetmap api 免費

老師會建議使用OpenStreetApi
google map api 要收費

map 可以用在美食地圖、經銷商地點、服務地點，維修地點，點進去後發通知

---

攔截器就像是fliter


---

透過IoC把依賴轉移到組態設定程式上

---

透過公司/專案特性來決定回答，合作還是獨立作業，先熟悉公司文化，再被派駐到外地

---

DAO不應該依賴service
因為想換service就換
Ioc 轉移依賴 P.14
高層模組不應該依賴低層


![](https://hackmd.io/_uploads/HyVd-HVSn.png)


![](https://hackmd.io/_uploads/HJXqbBESn.png)


路徑，jsp會存在那，但是大小寫不能改
為啥不能改? server會找不到
```
jsp檔案產生的地方

//plugins下面，wrok 下面的地方
C:\Action\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\HibernateWebProject
```



利用建構子的方式controller DI
用Set方式給的就是set DI



https://1drv.ms/f/s!Ans47I9-Pkiv9X3Nsy0R5XKL8ile

![](https://hackmd.io/_uploads/Sy5xHBNr2.png)

![](https://hackmd.io/_uploads/BJFWSrEH2.png)

為啥要裝這個?  組態設定

@ + xml
Java組態
key.value


![](https://hackmd.io/_uploads/r1xmXSSNS2.png)

new java project 
之後轉換成maven project

![](https://hackmd.io/_uploads/BkHVBrNBn.png)
1.4 改成17
專案右鍵/properties/project facts

![](https://hackmd.io/_uploads/Sk8SHrNB3.png)
bean 標籤就等於直接幫你建立好一個物件

![](https://hackmd.io/_uploads/B1PUSH4S3.png)

打CPX快速提示ClassPathXmlApplicationContext

用工具建fliter會跟@webfliter衝突，只能用其中一個

![](https://hackmd.io/_uploads/SJfKBBEHn.png)

2.  透過這行  
LogProvider logprovider = (LogProvider)context.getBean("logProvider");
getBean間接取得物件


ClassPath   Xml   ApplicationContext
這三個分別是什麼?


![](https://hackmd.io/_uploads/rk0TSHEB2.png)

![](https://hackmd.io/_uploads/H1WyISEH2.png)

![](https://hackmd.io/_uploads/S1I1Ir4B2.png)

上課常用的API

spring 6.0 版本是分界線，  要馬就用5.0版本以下

xml 發展成熟、集中管理， 以前這些是優點，

之後改用annotation各別寫

在之後完全捨去xml

1. ...代表不知道個數，但都是同型別
2.逗點隔開就代表陣列
3. ...代表不帶參數也對


可以代用多個組態設定檔來讀取文件

![](https://hackmd.io/_uploads/B1K-LHNH3.png)


p.61 根據你檔案放的位置去決定你("")的路徑

放在src就直接寫檔案名


![](https://hackmd.io/_uploads/Skg7LrEHh.png)
改xml 註解顏色


![](https://hackmd.io/_uploads/SJjNIHVBh.png) 
p.23

String 只存唯一且不相同的一份


![](https://hackmd.io/_uploads/By3LLrNH3.png)
顏色差別



![](https://hackmd.io/_uploads/rJsDUBVBn.png)


ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 是 Spring Framework 提供的一個應用程式上下文(Application Context)實現之一。

它是 Spring 的一個類別，用於從 classpath 中載入 XML 格式的配置文件，並創建並管理相應的 bean。這個類別可以解析 XML 配置文件，並根據配置文件中的定義創建相應的 bean 物件。

以下是使用 ClassPathXmlApplicationContext 的基本步驟：

在專案的 classpath 中準備一個 XML 格式的 Spring 配置文件。
創建一個 ClassPathXmlApplicationContext 物件，並將配置文件的路徑作為參數傳遞給它的建構子。
使用 getBean() 方法從 ClassPathXmlApplicationContext 中獲取需要的 bean 物件，並進行後續操作。
這樣，你就可以使用 ClassPathXmlApplicationContext 載入和管理 Spring 配置文件中定義的 bean，並在應用程式中使用它們。

---

string0：類別為 java.lang.String 的 bean。這個 bean 的 ID 是 "string0"，同時也指定了 "string1" 和 "string2" 作為別名。這個 bean 使用了建構子依賴注入(Constructor Dependency Injection)，並將字串 "Hello,amigo!!!" 傳遞給建構子。

哪裡有依賴注入?  
>將字串 "Hello,amigo!!!"  傳給 string0 和"string1" 和 "string2"

property name="loginDao" 參考到 
private LoginDao loginDao; //loginDao 屬性名稱

ref="loginDao" 參考到 bean id="loginDao"


---

```
 <bean id="loginService2" class="tw.leonchen.model.LoginService">
  <property name="loginDao" ref="loginDao"></property>
 </bean>
 ```
 剛寫上面這個幹嘛?
 
 現在才用到
 
 
 ![](https://hackmd.io/_uploads/HkaRUSNH3.png)

以後黑底用黃色的字註解好了，看得比較舒服
紅色框，黃色顏色文字

![](https://hackmd.io/_uploads/HyGbDH4r2.png)

箭頭是把框改成線去拉的

---

<!-- 利用屬性，呼叫setter方法 -->
![](https://hackmd.io/_uploads/ryAMwHVB3.png)



利用建構子給值，照index順序把值帶進去
![](https://hackmd.io/_uploads/SJnQvSVr3.png)


index順序哪來的ㄚ

![](https://hackmd.io/_uploads/Hkc4wHNr2.png)
![](https://hackmd.io/_uploads/Sy1SPHEH3.png)
應該是把值帶進去index


![](https://hackmd.io/_uploads/rkorDH4Sn.png)


---

getBean預設回傳Object

利用static block來初始化

HashMap  value 是TruckBean， 所以map.put 時候new 一個 TruckBean把值放進去


![](https://hackmd.io/_uploads/S1oEHYESn.png)

開始要記老師的順序

bean -> 然後啥 ->去組態設定檔註冊

![](https://hackmd.io/_uploads/r1XFSKNrh.png)

先建TruckBean -> TruckBeanStaticFactory ->xml設置bean

TruckBeanStaticFactory 這個要幹嘛阿

xml設置bean 這個後面要備註我的解釋

![](https://hackmd.io/_uploads/By6iBt4r3.png)


在xml設定好value  帶入factory-method

TruckBeanStaticFactory
當呼叫getTruckBean 方法時， 會去static帶入key 得到 TruckBean的key和value


![](https://hackmd.io/_uploads/B1zarYEH2.png)

![](https://hackmd.io/_uploads/SJOpBF4Bn.png)


![](https://hackmd.io/_uploads/SJfCSYNH2.png)

![](https://hackmd.io/_uploads/r1HJUYVr2.png)

static {
    map.put(1, new TruckBean(3, "nissan"));
    map.put(2, new TruckBean(4, "toyota"));
}

可以在裡面new喔?  直接new 建構子，把值帶進去

![](https://hackmd.io/_uploads/HklZUtNS2.png)


@Autowired 會自動搜尋


![](https://hackmd.io/_uploads/Ske3bItNB3.png)


在class上面加對應的annotation
分別是bean /dao /service
等於自動幫你註冊

![](https://hackmd.io/_uploads/SkizIKEr3.png)


---

true 代表一定要有，一定要找到他，沒找到會發生exception

![](https://hackmd.io/_uploads/S1gX5LFErh.png)

nullable代表允許空值出現

![](https://hackmd.io/_uploads/SJTcLFVrh.png)


@Autowired(required = true)@Nullable 

這個註解組合的效果是，當 @Autowired(required = true) 被應用時，Spring 將自動尋找相應的 bean 並注入到變數中。如果找不到相應的 bean，或者找到多個相容的 bean，且沒有其他方式可以解決注入衝突，則將拋出異常。如果同時使用了 @Nullable，則該依賴項可以為 null，而不會拋出異常。



---

因為worker bean 有再cfg設定，
所以不用加@repository

![](https://hackmd.io/_uploads/BkSpIKVS3.png)

javabean最好要有不帶參數的建構子，以避免程式發生錯誤的困擾

利用qualifier去切換你要搜尋的worker

@Autowired @Qualifier("") 寫在屬性上會比較簡單 (setter di)

相較於constructor di，@Qualifier寫在參數前面，寫法比較複雜一點

![](https://hackmd.io/_uploads/ryPJPYVB2.png)

![](https://hackmd.io/_uploads/H1J8wtEHn.png)


---

bug Buster蟲蟲剋星

寫在建構子，要設定全域

![](https://hackmd.io/_uploads/Sk-Rg5HB3.png)

![](https://hackmd.io/_uploads/Sk20ecHrh.png)

就設置一個東西能找到檔案位置 (p.121)

![](https://hackmd.io/_uploads/Sy4MZcSH3.png)

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertiesFactoryBean.html


 class="org.springframework.beans.factory.config.PropertiesFactoryBean">
 
我們哪來這個class

library提供的

沒有建立這個class也可以使用

那我怎麼知道這個從哪來

PropertiesFactoryBean 是一個 Spring Framework 提供的類別

---


他剛剛說甚麼用地一種寫法就好

p127頁


用()內是class的寫法

她說她會示範第一種

常用就是前兩種

![](https://hackmd.io/_uploads/Hyy0-cHBn.png)


![](https://hackmd.io/_uploads/HkeWG5rrn.png)


右邊應該是說id
因為變成自訂
就不是他原本方法名

![](https://hackmd.io/_uploads/BJk7G5SSh.png)


![](https://hackmd.io/_uploads/ryWVz9HS2.png)


![](https://hackmd.io/_uploads/BkuVG9rH3.png)


init方法會在乎叫建構子之後還有XX之後執行

destroy方法必須在 scope =singleton才能執行

![](https://hackmd.io/_uploads/H1mIM9rrh.png)

變數名.hashCode() 能測試兩物件是不是同一個東西


java 和mysql連線的maven dependency
讓java 和mysql建立連線

![](https://hackmd.io/_uploads/S1PuN5SB3.png)

https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.33
```
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>


```


新增四個jar
![](https://hackmd.io/_uploads/SkS5E5rBh.png)

jdbc都要自己塞值

---

api
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/DriverManagerDataSource.html

![](https://hackmd.io/_uploads/ryBuR-qSn.png)


![](https://hackmd.io/_uploads/r1aORbqBh.png)

ResultSet 物件 rs 包含從資料庫獲取的結果集。

getInt(1) 方法從 rs 獲取結果集中的第一個欄位的整數值。

House 物件 hBean 是一個 House 類別的實例，它具有 houseid 屬性。

setHouseid(rs.getInt(1)) 方法將從結果集中獲取的整數值設定為 hBean 的 houseid 屬性。

這樣，程式碼的執行將從結果集中取得第一個欄位的整數值，並將該值設定為 hBean 物件的 houseid 屬性值。這樣，hBean 物件就具有了從資料庫中獲取的房屋ID的值，以便後續使用。

---

service 呼叫dao時候會拋出sql Exception

![](https://hackmd.io/_uploads/rJf1fM9Hh.png)


## JdbcTemplate
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html


setValue是針對設定問號用的
extractData是對取資料用的

//直接new 直接override
//new PreparedStatementSetter 或是new ResultSetExtractor時
//add umimplemented method，會產生一個override的存取資料的方法

![](https://hackmd.io/_uploads/S12zfzqS3.png)

不叫那名稱就另外指定位置和名稱


![](https://hackmd.io/_uploads/SJqXMMqSh.png)

![](https://hackmd.io/_uploads/Byg4MM9rh.png)


![](https://hackmd.io/_uploads/SkUNGGqSn.png)

如果不小心刪除web.xml可以透過這方法新增

專案右鍵/javaEE tools/

---

不能直接把java專案的pom.xml直接丟到web專案的web.xml
會出問題

---

session.beginTransaction();
session.getTransaction().commit();
factory.close();
都不用加這些ㄇ

他說透過filter 還是listener去管，
那些一樣要加
切換畫面都會開交易關交易

我們的程式碼不就都沒加?
原本/*
也可以自己定義指定路徑


---

![](https://hackmd.io/_uploads/r1aFMMqHn.png)
設定改


![](https://hackmd.io/_uploads/Bkqcff9Bn.png)

設定改之後要確認版本
```
  <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
```


![](https://hackmd.io/_uploads/HJx2MM5Hh.png)

設定檔放在web-inf下


https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/package-summary.html

![](https://hackmd.io/_uploads/Hylx7fcSn.png)
value 是server裡面context.xml的name
前面+java:comp/env/

web.xml

![](https://hackmd.io/_uploads/SJO-mz5r2.png)


jndi
![](https://hackmd.io/_uploads/SJB7XM9B3.png)

config只有加這段


![](https://hackmd.io/_uploads/rygSmfqB3.png)

怎突然就註解上面兩個連線ㄌ

因為變成Web

原本是直接讀取，現在變成間接讀取server裡面的context.xml檔案
原本是datasource.DriverManagerDataSource改成jndi
一個連線改成蓋橋，讀取連線池


---

![](https://hackmd.io/_uploads/H1TIXGcBh.png)


null 不能去get HouseId 和 Housename 所以會拋出exception
這個是說甚麼
就是在講那個try catch
p.171


new servlet 時候
1.更改urlpattern新增.do
2.勾選init


![](https://hackmd.io/_uploads/H1wCXfqHh.png)
![](https://hackmd.io/_uploads/r1GkEzcrh.png)

---

![](https://hackmd.io/_uploads/B1JxNM5Bn.png)
怎麼會印出這句啊

因為妳其他專案放一起

---

要改寫的是不帶參數的建構子 init
不然servlet會無法執行到doget dopost

不要改寫帶servletconfig參數的init()

p.174 用第二種方法，取代xml

![](https://hackmd.io/_uploads/BJAQ4fqHn.png)

![](https://hackmd.io/_uploads/S1N44McH2.png)

怎麼又講到hibernateㄌ
因為目前要結合spring 和hibernate了


---


![](https://hackmd.io/_uploads/r162NG5S2.png)
dataSource 改寫成hibernate 寫法，簡潔很多


![](https://hackmd.io/_uploads/HJJkBM5Bh.png)
取代cfg.xml裡面的mapping class


https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/orm/hibernate5/LocalSessionFactoryBean.html

(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jndi/package-summary.html)

jndi


int 的i 代表更新幾筆

![](https://hackmd.io/_uploads/SkZ68MqHn.png)

![](https://hackmd.io/_uploads/SkupIfqBn.png)

都是老師講的嗎

他說明明沒做甚麼也會跑開交易或關交易 就是因為選擇過濾/* 所有

實際上要過濾的應該只有servlet

指定有用到的servlet檔案嗎

對啊

作會員的人應該都會幫大家指定

直接在/* 那裏加


,分隔全組用到的servlet

有用到filter 功能的servlet在標住在()內

![](https://hackmd.io/_uploads/r1VgwfqSn.png)


---


servlet 的init () 空的
filter init () 放FilterConfig conifg


getCurrentSession 

commit和rollback時會關閉連線


![](https://hackmd.io/_uploads/rkxEwzqH2.png)

![](https://hackmd.io/_uploads/H14rPz9rn.png)


```
<!-- spring 的scan -->
<!-- 讀取annotation設定的標籤 -->
<context:annotation-config />
<context:component-scan base-package="tw.leonchen" />
<!-- 寫上一層，確保都搜尋的到 -->
```

```
//hibernate 的scan
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
	   <property name="dataSource" ref="dataSource"/>
	   <property name="packagesToScan" value="tw.leonchen"/>
	   <property name="hibernateProperties">
	      <props>
	      	<!-- 註解來切換資料庫 -->
	         <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 
	        <!-- <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>-->
	         <prop key="hibernate.show_sql">true</prop>
	         <prop key="hibernate.format_sql">true</prop>
	         <prop key="hibernate.current_session_context_class">thread</prop>
	      </props>
	   </property>
	</bean>
	

```

jdbc不會再console印出sql語法
hibernate會印出

![](https://hackmd.io/_uploads/Hy-kKf9rh.png)


JNDI那是spring的，要用getBean同時就會連線

剛才是測試hibernate的

JNDI那是spring的，要用getBean同時就會連線

都有用到

原本是用jdbc連線目前用jndi


![](https://hackmd.io/_uploads/SywZsfqS2.png)

ref


為啥要換JNDI 啊?

web都要用連線池

---

只要網址出現整個Package name就是沒有mapping或是annotation @(/Servlet)


jdbc 只有單一個連線
每次都要new


@WebServlet("/DemoHouseJndiServlet.play")
這是urlpattern

![](https://hackmd.io/_uploads/SJTBozcSn.png)

![](https://hackmd.io/_uploads/rJBIiMcrn.png)

![](https://hackmd.io/_uploads/Syi8jzcB2.png)


https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.9.19


```
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.19</version>
    <scope>runtime</scope>
</dependency>

```

https://mvnrepository.com/artifact/aopalliance/aopalliance/1.0



![](https://hackmd.io/_uploads/rJkcoG5Sh.png)


主要功能
Hibernate - 簡化JDBC的工作
Spring - 降低依賴, 只要bean註冊好, 去找ID就好

多筆刪除可以寫迴圈抓多個ID
List去裝