# Spring 框架 官方文件 路徑 https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/ ## SpringIOC 為什需要IOC : 在還沒有spring以前,所有服務耦合性很高,修改了一個服務會影響許多的服務,有了spring之後,透過spring 控制反轉,透過綁定介面,而非綁定物件,當服務使用到之後才進行注入,當服務需要修改的時候只需要抽換介面背後的實作,達到最少的修改 ### 分析實現 1.先寫一個UserDao接口 ``` public interface UserDao { public void getUser(); } ``` 2.再去寫Dao的實作 ``` public class UserDaoImpl implemants UserDao { @Override public void getUser() { System.out.println("獲取用戶數據"); } } ``` 3.然後再去寫UserService的接口 ``` public interface UserService{ public void getUser(); } ``` 4.最後寫Service的實現類 ``` public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); @Override public void getUser() { userDao.getUser(); } } ``` 5.測試一下 ``` @Test public void test(){ UserService service = new UserServiceImpl(); service.getUser(); } ``` 這是我們原來的方式 , 開始大家也都是這麽去寫的對吧 . 那我們現在修改一下 . 把Userdao的實現類增加一個 . ``` public class UserDaoMySqlImpl implements UserDao { @Override public void getUser() { System.out.println("MySql获取用户数据"); } } ``` 緊接著我們要去使用MySql的話 , 我們就需要去service實現類里面修改對應的實現 ``` public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoMySqlImpl(); @Override public void getUser() { userDao.getUser(); } } ``` 在假設, 我們再增加一個Userdao的實現類 . ``` public class UserDaoOracleImpl implements UserDao { @Override public void getUser() { System.out.println("Oracle获取用户数据"); } } ``` 那麽我們要使用Oracle , 又需要去service實現類里面修改對應的實現 . 假設我們的這種需求非常大 , 這種方式就根本不適用了, 甚至反人類對吧 , 每次變動 , 都需要修改大量代碼 . 這種設計的耦合性太高了, 牽一發而動全身 . 那我們如何去解決呢 ? 我們可以在需要用到他的地方 , 不去實現它 , 而是留出一個接口 , 利用set , 我們去代碼里修改下 ``` public class UserServiceImpl implements UserService { private UserDao userDao; // 利用set實現 public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void getUser() { userDao.getUser(); } } ``` 現在去我們的測試類里 , 進行測試 : ``` @Test public void test(){ UserServiceImpl service = new UserServiceImpl(); service.setUserDao( new UserDaoMySqlImpl() ); service.getUser(); //那我們現在又想用Oracle去實現呢 service.setUserDao( new UserDaoOracleImpl() ); service.getUser(); } ``` 以前所有東西都是由程序去進行控制創建 , 而現在是由我們自行控制創建對象 , 把主動權交給了調用者 . 程序不用去管怎麽創建,怎麽實現了 . 它只負責提供一個接口 . 這種思想 , 從本質上解決了問題 , 我們程序員不再去管理對象的創建了 , 更多的去關注業務的實現 . 耦合性大大降低 . 這也就是IOC的原型 ! ## Spring MVC ![](https://i.imgur.com/XLBFvcM.png) ### 简要分析执行流程 DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。 我们假設请求的url為 : http://localhost:8080/SpringMVC/hello 如上url拆分成三部分: http://localhost:8080服務器域名 SpringMVC部署在服務器上的web站點 hello表示控制器 通過分析,如上url表示为:请求位於服務器localhost:8080上的SpringMVC站點的hello控制器。 HandlerMapping為處理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根據请求url查找Handler。 HandlerExecution表示具体的Handler,其主要作用是根據url查找控制器,如上url被查找控制器为:hello。 HandlerExecution將解析後的信息傳遞给DispatcherServlet,如解析控制器映射等。 HandlerAdapter表示處理器適配器,其按照特定的规則去執行Handler。 Handler讓具體的Controller執行。 Controller將具體的執行信息返回给HandlerAdapter,如ModelAndView。 HandlerAdapter將視圖邏輯名或模型傳遞给DispatcherServlet。 DispatcherServlet调用視圖解析器(ViewResolver)来解析HandlerAdapter傳遞的邏輯视圖名。 視圖解析器將解析的邏輯视圖名傳给DispatcherServlet。 DispatcherServlet根據視圖解析器解析的視圖结果,调用具体的視圖。 最终視圖呈現给用戶。 ## 依賴注入 Dependency Injection ### set 注入 #### 範例 #### 宣告address class ``` public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } ``` #### 宣告Student class ``` public class Student { private String name; private Address address; private String[] book; private List<String> hobby; private Map<String,String> card; private String wife; private Properties info; private Set<String> games; get set .... } ``` #### beans.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="address" class="com.spring.pojo.Address"> <property name="address" value="台北市"></property> </bean> <bean id="student" class="com.spring.pojo.Student"> <!-- 普通注入 --> <property name="name" value="student1"/> <!-- bean注入 --> <property name="address" ref="address"/> <!-- 陣列注入 --> <property name="book" > <array> <value>小紅帽</value> <value>大野狼</value> <value>小紅帽奶奶</value> <value>三隻小豬</value> </array> </property> <!-- list --> <property name="hobby" > <list> <value>看電影</value> <value>寫作文</value> <value>念書</value> <value>游泳</value> </list> </property> <!-- map --> <property name="card"> <map> <entry key="健保卡" value="M12345567"/> <entry key="信用卡" value="938849950005"/> </map> </property> <!-- set --> <property name="games"> <set> <value>皇室戰爭</value> <value>小唐人</value> <value>仙劍奇俠傳</value> </set> </property> <!-- null --> <property name="wife"> <null/> </property> <!-- properties --> <property name="info"> <props> <prop key="driver">cars</prop> <prop key="sex">男</prop> </props> </property> </bean> </beans> ``` ### p xml注入 可以透過p:代替properties範例如下 ![](https://i.imgur.com/hax3ha6.png) ### 結構注入 #### java Score ``` public class Score { private String level; private int score; public Score(String level, int score) { super(); this.level = level; this.score = score; } } ``` #### beans.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="score" class="com.spring.pojo.Score"> <constructor-arg name="level" value="A" /> <constructor-arg name="score" value="100" /> </bean> </beans> ``` ### c xml注入 可以透過c:代替constructor範例如下 ![](https://i.imgur.com/DJmfO7z.png) ### bean的作用域 | Scope | Description | | -------- | -------- | | singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. | | prototype | Scopes a single bean definition to any number of object instances. | | request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. | | session | Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. | | application | Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.| | websocket | Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.| ### 測試自動裝配 #### 建立 persion 下有 dog cat 手動配置 ``` <?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="dog" class="com.spring.pojo.Dog"/> <bean id="cat" class="com.spring.pojo.Cat"/> <bean id="persion" class="com.spring.pojo.Persion" > <property name="name" value="spring-beforeautotest"/> <property name="dog" ref="dog"/> <property name="cat" ref="cat"/> </bean> </beans> ``` ### 透過自動裝配將物件自動set進去 #### by name自動裝配 ![](https://i.imgur.com/81nPB6l.png) #### by type自動裝配 ![](https://i.imgur.com/QJDNLKt.png) ### 註解配置 官方推薦不要再用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:annotation-config/> </beans> ``` #### @Autowired ![](https://i.imgur.com/HV0T0cU.png) 可以配合@Qualifier 指定對應的bean #### xml 設定 ![](https://i.imgur.com/XoazKsO.png) #### annotation設定 ![](https://i.imgur.com/r42aqDq.png) #### @Resource ## SpringAOP **為什麼要使用AOP: 在開發過程中,開發者有可能在各個方法中要求加入一些方法, 這些方法的代碼重複性很高,卻分散在各個方法中,AOP可以透過代理 幫我們集中處理這些方法** ![](https://i.imgur.com/4yDL6Xt.png) ![](https://i.imgur.com/ViE6Z6Z.png) maven 引入 ``` <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> ``` applicationContext.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="log" class="com.springAOP.log.Log" /> <bean id="userService" class="com.springAOP.service.UserService"/> <bean id="annotationPointCut" class="springAopAnnotation.AnnotationPointCut"/> <!-- 開啟自動代理 --> <aop:aspectj-autoproxy/> </beans> ``` 路徑範例 ![](https://i.imgur.com/OShKKMf.png) classname :MethodBeforeAdvice ``` package com.springAOP.log; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class Log implements MethodBeforeAdvice{ //method 使用的方法 //target 目標物件 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getClass().getName()+"的"+method.getName()+"方法"); } } ``` classname :UserService ``` package com.springAOP.service; public class UserService implements UserServiceIntf{ public void add() { System.out.println("新增"); } public void delete() { System.out.println("刪除"); } public void update() { System.out.println("修改"); } public void select() { System.out.println("查詢"); } } ``` classname :UserServiceIntf ``` package com.springAOP.service; public interface UserServiceIntf { public void add(); public void delete(); public void update(); public void select(); } ``` classname :AnnotationPointCut ``` package springAopAnnotation; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class AnnotationPointCut { @Before(value = "execution(* com.springAOP.service.UserService.*(..))") public void before(){ System.out.println("-----執行參數之前------"); } } ``` 測試 ``` package springAOP; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.springAOP.service.UserService; import com.springAOP.service.UserServiceIntf; public class Test { public static void main(String[] args) { ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml"); //接口必須是介面 UserServiceIntf userService = (UserServiceIntf)context.getBean("userService"); userService.add(); } } ``` 結果 ![](https://i.imgur.com/B3Sya3L.png)