SSM框架學習之Spring淺談
Spring介紹
1、什麼是Spring?
百度:
Spring是JavaEE程式設計領域的一個輕量級開源框架,該框架由一個叫Rod Johnson的程式設計師在 2002年最早提出並隨後建立,是為了解決企業級程式設計開發中的複雜性,實現敏捷開發的應用型框架 。Spring是一個開源容器框架,它整合各型別的工具,通過核心的Bean factory實現了底層的類的例項化和生命週期的管理。
補充:想要具體瞭解學習Spring,建議多逛逛Spring官網,不一定要全部看懂,一開始只是混個臉熟也有利於後面進一步的學習。
2、我們常說的Spring指什麼?
如果我們開啟Spring官網,開啟project會發現:Spring就像是一個大的容器型框架,下面還有比較熟知的常見專案和框架,例如:Springboot,SpringCloud,SpringData等等。其中“SpringFramework”的Core technologies(核心技術),會發現一些比較常見的關鍵詞:DI(IOC)和AOP,就是常說的控制反轉和麵向切面程式設計。所以,我們常說的“Spring”大部分情況下是指“SpringFramework”。
Springframework核心技術
IOC(控制反轉):
一般程式的設計思想是從Controller/Servlet層到Service層再到Dao/Mapper層,如果對某個下游的類進行更改名字,或者進行更換,則整個流程都需要進行相應的更改,所以程式碼的耦合性太高,牽一髮而動全身,所以為了解決這個問題設計出了IOC(控制反轉)的思想:把建立下游類的功能交給一個“類似工廠”(BeanFactory)的物件進行管理(工廠思想+反射)。
1 public class MyController { 2 public static void main(String[] args) {Controller3 MyBeanFactory myBeanFactory = new MyBeanFactory(); 4 SpringDao springDao = (SpringDao) myBeanFactory.getBean(); 5 springDao.test(); 6 } 7 8 }
1 public class MyBeanFactory { 2 public Object getBean() { 3 Object bean = null; 4 try {BeanFactory5 bean = Class.forName("com.emo.dao.SpringDao").newInstance(); 6 } catch (InstantiationException e) { 7 e.printStackTrace(); 8 } catch (IllegalAccessException e) { 9 e.printStackTrace(); 10 } catch (ClassNotFoundException e) { 11 e.printStackTrace(); 12 } 13 return bean; 14 } 15 }
1 public class SpringDao { 2 public void test() { 3 System.out.println("SpringDao被呼叫!"); 4 } 5 }Dao
優點:不需要每次更改Dao層資料都需要整個流程都進行更改,只需要修改BeanFactory中的“com.emo.dao.SpringDao”。
缺點:還是需要進行手動修改程式碼。
工廠思想還是需要改程式碼,於是在這個基礎上加上配置檔案(ApplicationContext.xml)進行改造,這樣只需要修改配置檔案基本不需要修改程式碼。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <!-- bean definitions here --> 8 <bean id="springDao" class="com.emo.dao.SpringDao"></bean> 9 </beans>ApplicationContext.xml
public class SpringDao { public void test() { System.out.println("SpringDao被呼叫!"); } }Dao
1 public class MyController { 2 public static void main(String[] args) { 3 //spring配置方式,建立spring工廠,載入spring配置檔案 4 ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); 5 //從spring工廠中獲取物件,通過bean的id/name 6 SpringDao springDao = (SpringDao) ac.getBean("springDao"); 7 springDao.test(); 8 } 9 }Controller
注意:記得要匯入SpringFramework依賴
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactId>spring-context</artifactId> 4 <version>5.1.7.RELEASE</version> 5 </dependency>
DI(依賴注入):
如何通過呼叫Service,繼而呼叫Dao層?為了解決這個問題,就有了依賴注入,把Dao成當作Service層的“屬性”或者“元素”,注入到配置檔案中。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <!-- bean definitions here --> 8 <bean id="springDao" class="com.emo.dao.SpringDao"></bean> 9 <bean id ="springService" class="com.emo.service.SpringService"> 10 <!-- 注入物件 --> 11 <!-- property 根據類中的setter方法進行屬性注入 --> 12 <!-- name:setter方法的字尾小寫,比如setXxx 對應的name為xxx --> 13 <!-- ref:引用哪一個bean(物件),值為bean的id/name --> 14 <property name="springDao" ref="springDao" /> 15 </bean> 16 17 </beans>ApplicationContext.xml
1 public class SpringDao { 2 public void test() { 3 System.out.println("SpringDao被呼叫!"); 4 } 5 }SpringDao
1 public class SpringService { 2 SpringDao springDao = null; 3 4 public void setSpringDao(SpringDao springDao) { 5 this.springDao = springDao; 6 } 7 8 public void test() { 9 System.out.println("SpringService被呼叫!"); 10 springDao.test(); 11 } 12 13 }SpringService
1 public class MyController { 2 public static void main(String[] args) { 3 //spring配置方式,建立spring工廠,載入spring配置檔案 4 ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); 5 //從spring工廠中獲取物件,通過bean的id/name 6 SpringService springService = (SpringService) ac.getBean("springService"); 7 springService.test(); 8 } 9 }MyController
注意:把Dao層注入到Service層時,要把Dao層當作Service層的屬性或者元素,所以要在Service層提供setXxxDao()方法。
1 public class SpringService { 2 SpringDao springDao = null; 3 4 public void setSpringDao(SpringDao springDao) { 5 this.springDao = springDao; 6 } 7 8 public void test() { 9 System.out.println("SpringService被呼叫!"); 10 springDao.test(); 11 } 12 13 }
簡要總結:IOC就是由原本手動建立物件交給Spring工廠去建立物件,DI就是把一個物件A當作另一個物件B的屬性或者元素注入到配置檔案的B物件中。
AOP(面向切面程式設計):
AOP (Aspect Oriented Programing) 稱為:面向切面程式設計,它是一種程式設計思想。AOP 是 OOP(面向物件程式設計(Object Oriented Programming,OOP,面向物件程式設計)是一種計算機程式設計架構),思想延續,它是基於代理思想,對原來目標物件,建立代理物件,在不修改原物件程式碼情況下,通過代理物件,呼叫增強功能的程式碼,從而對原有業務方法進行增強!
關於代理相關資料:https://www.cnblogs.com/Bernard94/p/12358728.html
AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性程式碼的編寫方式(例如效能監視、許可權管理、事務管理、安全檢查、快取、日誌記錄等)。
相關術語:
- Aspect(切面): 是通知和切入點的結合,通知和切入點共同定義了關於切面的全部內容---它的功能、在何時和何地完成其功能
- joinpoint(連線點):所謂連線點是指那些被攔截到的點。在spring中,這些點指的是方法,因為spring只支援方法型別的連線點.
- Pointcut(切入點):所謂切入點是指我們要對哪些joinpoint進行攔截的定義.通知定義了切面的”什麼”和”何時”,切入點就定義了”何地”.
- Advice(通知):所謂通知是指攔截到joinpoint之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)
- Target(目標物件):代理的目標物件
- weaving(織入):是指把切面應用到目標物件來建立新的代理物件的過程.切面在指定的連線點織入到目標物件
- Introduction(引介):在不修改類程式碼的前提下, Introduction可以在執行期為類動態地新增一些方法或Field.
JDK動態代理實現AOP:
1 public interface People { 2 public void eat() ; 3 }People
1 public class Student implements People { 2 3 public void eat() { 4 System.out.println("吃東西!"); 5 } 6 7 }Student
1 public class ProxyObject { 2 private Object target; 3 4 public ProxyObject(Object target) { 5 this.target = target; 6 } 7 8 public Object getProxyObject() { 9 // 引數1:目標物件的類載入器 10 // 引數2:目標物件實現的介面 11 // 引數3:回撥方法物件 12 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 13 new InvocationHandler() { 14 15 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 16 // 如果是儲存的方法,執行記錄日誌操作 17 if (method.getName().equals("eat")) { 18 ProxyEat(); 19 } 20 // 目標物件原來的方法執行 21 Object object = method.invoke(target, args);// 呼叫目標物件的某個方法,並且返回目標物件方法的返回值 22 23 return object; 24 } 25 }); 26 } 27 28 private void ProxyEat() { 29 System.out.println("eat方法加強!"); 30 } 31 }ProxyObject
1 public class test { 2 public static void main(String[] args) { 3 Student student = new Student(); 4 ProxyObject proxyObject = new ProxyObject(student); 5 People proxyStudenty = (People) proxyObject.getProxyObject(); 6 proxyStudenty.eat(); 7 } 8 }test
注意:因為JDK必須對介面生成代理,所以代理物件返回的是介面:People,不能是具體類:Student。
CGLIB動態代理實現AOP:
1 public class Dog { 2 public void bark() { 3 System.out.println("汪汪。。。"); 4 } 5 }Dog
1 public class ProxyObject implements MethodInterceptor { 2 private Object target; 3 4 public ProxyObject(Object target) { 5 this.target = target; 6 } 7 8 // 獲取代理物件 9 public Object getProxyObject() { 10 // 1.代理物件生成器(工廠思想) 11 Enhancer enhancer = new Enhancer(); 12 // 2.在增強器上設定兩個屬性 13 // 設定要生成代理物件的目標物件:生成的目標物件型別的子型別 14 enhancer.setSuperclass(target.getClass()); 15 // 設定回撥方法 16 enhancer.setCallback(this); 17 // Callback 18 // 3.建立獲取物件 19 return enhancer.create(); 20 } 21 22 // 回撥方法(代理物件的方法) 23 // 引數1:代理物件 24 // 引數2:目標物件的方法物件 25 // 引數3:目標物件的方法的引數的值 26 // 引數4:代理物件的方法物件 27 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 28 // 如果是儲存的方法,執行記錄日誌操作 29 if (method.getName().equals("bark")) { 30 ProxyBark(); 31 } 32 // 目標物件原來的方法執行 33 Object object = method.invoke(target, args);// 呼叫目標物件的某個方法,並且返回目標物件方法的執行結果 34 return object; 35 } 36 37 private void ProxyBark() { 38 System.out.println("Bark方法加強!"); 39 } 40 41 }ProxyObject
1 public class demo { 2 public static void main(String[] args) { 3 Dog dog = new Dog(); 4 ProxyObject proxyObject = new ProxyObject(dog); 5 Dog proxyDog = (Dog) proxyObject.getProxyObject(); 6 proxyDog.bark(); 7 } 8 }demo
注意:Cglib要實現“MethodInterceptor”介面,此介面在SpringFramework中,所以要引入spring-context依賴:
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactId>spring-context</artifactId> 4 <version>5.1.7.RELEASE</version> 5 </dependency>
總結:
- Jdk代理:基於介面的代理,一定是基於介面,會生成目標物件的介面型別的子物件。
- Cglib代理:基於類的代理,不需要基於介面,會生成目標物件型別的子物件。