1. 程式人生 > 實用技巧 >SSM框架學習之Spring淺談

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) {
3 MyBeanFactory myBeanFactory = new MyBeanFactory(); 4 SpringDao springDao = (SpringDao) myBeanFactory.getBean(); 5 springDao.test(); 6 } 7 8 }
Controller
 1 public class MyBeanFactory {
 2     public Object getBean() {
 3         Object bean = null;
 4         try {
5 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 }
BeanFactory
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採取橫向抽取機制,取代了傳統縱向繼承體系重複性程式碼的編寫方式(例如效能監視、許可權管理、事務管理、安全檢查、快取、日誌記錄等)。

相關術語:

  1. Aspect(切面): 是通知和切入點的結合,通知和切入點共同定義了關於切面的全部內容---它的功能、在何時和何地完成其功能
  2. joinpoint(連線點):所謂連線點是指那些被攔截到的點。在spring中,這些點指的是方法,因為spring只支援方法型別的連線點.
  3. Pointcut(切入點):所謂切入點是指我們要對哪些joinpoint進行攔截的定義.通知定義了切面的”什麼”和”何時”,切入點就定義了”何地”.
  4. Advice(通知):所謂通知是指攔截到joinpoint之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)
  5. Target(目標物件):代理的目標物件
  6. weaving(織入):是指把切面應用到目標物件來建立新的代理物件的過程.切面在指定的連線點織入到目標物件
  7. 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代理:基於類的代理,不需要基於介面,會生成目標物件型別的子物件。