spring入門aop和ioc

ning12發表於2024-06-05

目錄
  • spring分層架構
    • 表現層
    • 服務層(業務層)
    • 持久層
  • spring核心
    • ioc(控制反轉)
      • 1)接下來是程式碼示例:
      • 2)ioc容器的使用過程
      • 3)ioc中的bean管理
      • 4)例項化bean的三種方式
    • aop(面向切面開發)
  • 定義
  • 優勢
  • AOP底層原理
  • AOP相關的術語
  • AOP入門
  • aop註解開發
  • aop純註解開發
    • Di(依賴注入)
      • 1)屬性的set方法注入值的方式
      • 2)構造方法賦值的方法
    • 多配置檔案

spring分層架構

  1. 表現層

    springmvc

  2. 服務層(業務層)

    spring ioc

  3. 持久層

    mybatis

    mybatis plus

    hibernite

網際網路專案,多ssm結構

spring核心

  1. ioc(控制反轉)

    ioc將物件的建立權利交給spring框架,底層實際上是使用反射實現的。降低程式的耦合度

    1)接下來是程式碼示例:

    1. 建立一個空的maven專案

    2. 引入一些基本依賴

    <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.12</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    
    1. 準備實體類

      // 建立一個介面
      public interface UserService {
          public void hello();
      }
      // 建立一個實體類
      public class UserServiceImpl implements UserService {
          @Override
          public void hello() {
              System.out.println("Hello, world!");
          }
      }
      
      
    2. 準備配置檔案

      <?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="us" class="實體類的路徑"></bean>
      </beans>
      
    3. 在test中測試

      // 常規方法
          @Test
          public void testUser() {
              // TODO: write test cases for UserService
              UserService userService = new UserServiceImpl();
              userService.hello();
          }
      // 基於ioc容器管理的方法,ioc是一個map key是物件的標識,value是ioc建立的物件
          @Test
          public void run1() {
              // 建立spring ioc工廠
              ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 獲取bean
              UserService userService = (UserService) ac.getBean("us");
              userService.hello();
          }
      

    上面就是一個關於ioc容器的示例

    2)ioc容器的使用過程

    ApplicationContext介面,這個工廠介面,使用這個介面就可以獲得相應的Bean物件,該物件下有兩個實現類

    1. ClassPathXmlApplicationContext:載入類路徑下的spring配置檔案(常用)
    2. FileSystemXmlApplicationContext:載入本地磁碟下的spring配置檔案(讓專案和配置檔案分離管理,不常用)

    3)ioc中的bean管理

    1. id屬性:bean的別名,取名要求:必須要字母開頭,可以使用數字,連字元,下劃線,字母,不能出現特殊字元
    2. class屬性:bean物件的全路徑
    3. scope屬性: bean物件的作用範圍
      1. singleton:單例(預設)生命週期和配置檔案一樣
      2. prototype:多例,不是載入配置檔案時建立,而是在獲取例項物件時建立
      3. request: 多例,不常用,應用於web專案中,每個請求都會建立一個新的例項物件
      4. session:多例,不常用,應用於web專案中,向一個http session中共享一個例項
    4. init-method:bean物件建立時可以配置一個指定的方法自動呼叫
    5. destory-method:bean物件銷燬時可以配置一個指定的方並自動呼叫

    4)例項化bean的三種方式

    1. 預設是無參的構造方法

          <bean id="us" class="實體類的路徑"></bean>
      
    2. 靜態工廠例項化方法(好處是可以自己編寫業務邏輯)

      準備一個靜態工廠類

      public class StaticFactory {
          public static UserService createUser() {
              // 編寫業務邏輯
              // ......
              return new UserServiceImpl();
          }
      }
      

      在xml配置檔案中使用

          <bean id="us1" class="com.qc.util.StaticFactory" factory-method="createUser"></bean>
      
    3. 例項化工廠例項化方式

      準備一個可以例項化的類

      public class DFactory {
          public  UserService createUser() {
              // 編寫業務邏輯
              return new UserServiceImpl();
          }
      }
      

      在xml配置檔案中使用

          <bean id="factory" class="com.qc.util.DFactory"></bean>
          <bean id="us2" factory-bean="factory" factory-method="createUser"></bean>
      
  2. aop(面向切面開發)

    定義

    是一種編輯的正規化,是OOP的延續,也是Spring框架中函式程式設計的一種衍生正規化,利用AOP可以對業務的各個部分進行隔離,從而似的業務邏輯各部分之間的耦合度降低,提高了程式的重用性,同時提高了開發的效率,AOP採用橫向抽取機制,取代了傳統縱向繼承體系。

    優勢

    1. 執行期間,不修改原始碼的情況下,對已有的方法進行增強,減少重複程式碼
    2. 提高開發效率
    3. 方便維護

    AOP底層原理

    1. 如果使用介面,則使用JDK動態代理技術,如果沒有使用介面,使用的是繼承則是cglib代理

    AOP相關的術語

    1. Joinpoint(連線點)所謂連線點是指那些被連線到的點,在spring中,這些帶點指的是方法,因為spring只支援方法型別的節點
    2. Pointcut(切入點)所謂切入點是我們要對哪些Joinpoint進行攔截的定義
    3. Advice(通知\通知)所謂通知就是指,攔截到Joinpoint之後所要做的情況就就是通知,通知分為,前置通知,後置通知,異常通知,最終通知,環繞通知(切面類中要完成的功能)
    4. Target(目標物件)代理的目標物件
    5. Weaving(織入)是指把增強應用到目標物件來建立新的代理物件的過程
    6. Proxy(代理)一個類被AOP織入增強後,就產生一個結果代理類
    7. Aspect(切面)是切入點+通知的結合,是自己編寫和配置

    AOP入門

    1. 匯入座標依賴

      <dependency>
                  <groupId>aopalliance</groupId>
                  <artifactId>aopalliance</artifactId>
                  <version>1.0</version>
              </dependency>
              <!--Spring Aspects-->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aspects</artifactId>
                  <version>5.0.2.RELEASE</version>
              </dependency>
              <!--aspectj-->
              <dependency>
                  <groupId>org.aspectj</groupId>
                  <artifactId>aspectjweaver</artifactId>
                  <version>1.8.3</version>
              </dependency>
      
      <?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"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">
      </beans>
      
    2. 編寫spirng配置檔案中AOP的配置部分

          <!--    bean管理-->
          <bean id="us" class="com.qc.service.impl.UserServiceImpl"/>
          <bean id="myAspect" class="com.qc.util.MyAspect"/>
      
          <!--    配置aop-->
          <aop:config>
              <!--        配置切面-->
              <aop:aspect ref="myAspect">
                  <!--            字首通知-->
                  <aop:before method="log1" pointcut="execution(public void com.qc.service.impl.UserServiceImpl.save())"/>
                  <!--            後置通知-->
                  <aop:after-returning method="log2"
                                       pointcut="execution(public void com.qc.service.impl.UserServiceImpl.save())"/>
                  <!--            異常通知-->
                  <aop:after-throwing method="log3"
                                      pointcut="execution(public void com.qc.service.impl.UserServiceImpl.save())"/>
                  <!--            最終通知-->
                  <aop:after method="log4"
                             pointcut="execution(public void com.qc.service.impl.UserServiceImpl.save())"/>
      
                  <!--            環繞通知-->
                  <aop:around method="aroundLog"
                              pointcut="execution(public void com.qc.service.impl.UserServiceImpl.save())"/>
              </aop:aspect>
          </aop:config>
      
      import org.aspectj.lang.ProceedingJoinPoint;
      
      public class MyAspect {
          // 自定義切面類 = 切入點+通知
          //通知
          public void log1() {
              System.out.println("前置增強的");
          }
      
          public void log2() {
              System.out.println("後置增強的");
          }
      
          public void log3() {
              System.out.println("異常增強的");
          }
      
          public void log4() {
              System.out.println("最終增強的");
          }
      
      
          public void aroundLog(ProceedingJoinPoint point) {
              try {
                  log1();
                  point.proceed(); // 目標方法呼叫
                  log2();
              } catch (Throwable e) {
                  e.printStackTrace();
                  log3();
              } finally {
                  log4();
              }
          }
      }
      

      注意環繞通知使用的介面

    通知型別

    1. 前置通知:目標方法執行前進行增強
    2. 後置通知:目標方法執行後進行增強
    3. 異常通知:目標方法出現異常後進行增強
    4. 最終通知:目標方法無論是否異常都進行增強
    5. 環繞通知:目標方法執行前後,都進行增強,不過需要自己書寫

    切入點表示式

    • execution([修飾符] 返回值 包名.類名.方法名(引數))
    • 修飾符可以省略不寫
    • 返回值型別不能省略,根據方法來編寫返回值,可以用星號來代替
    • 包名型別方法名是不能省略的,但是可以用星號來代替,也可以部分代替
    • 引數如果是一個可以用星號來代替,如果是多個可以使用兩個點

    因此比較通用的表示式:

    execution( com.qc..Service.save(..))*

    aop註解開發

    給切面新增@Aspect,編寫增強的方法,使用通知型別註解宣告

        <!--    開啟註解掃描-->
        <context:component-scan base-package="com.qc"/>
    
        <!--    開啟自動代理-->
        <aop:aspectj-autoproxy/>
    
    @Aspect
    @Component
    public class MyAspect {
        // 自定義切面類 = 切入點+通知
        //通知
        @Before("execution(* com.qc.*.*.*ServiceImpl.save(..))")
        public void log1() {
            System.out.println("前置增強的");
        }
    
        @AfterReturning("execution(* com.qc.*.*.*ServiceImpl.save*(..))")
        public void log2() {
            System.out.println("後置增強的");
        }
    
        @AfterThrowing("execution(* com.qc.*.*.*ServiceImpl.save*(..))")
        public void log3() {
            System.out.println("異常增強的");
        }
    
        @After("execution(* com.qc.*.*.*ServiceImpl.save*(..))")
        public void log4() {
            System.out.println("最終增強的");
        }
    
        @Around("execution(* com.qc.*.*.*ServiceImpl.save*(..))")
        public void aroundLog(ProceedingJoinPoint point) {
            try {
                log1();
                point.proceed(); // 目標方法呼叫
                log2();
            } catch (Throwable e) {
                e.printStackTrace();
                log3();
            } finally {
                log4();
            }
        }
    }
    

    在每個方法上使用註解進行配置,和配置檔案類似

    通知型別註解:

    1. @Before 前置註解
    2. @AfterReturning 後置註解
    3. @AfterThrowing 異常註解
    4. @After 最終註解
    5. @Round 環繞註解

    注意:在配置檔案中開啟自動代理

    aop純註解開發

    @EnableAspectJAutoProxy 開啟自動代理

  3. Di(依賴注入)

    依賴注入:在spring框架負責建立bean物件時,動態的將物件注入到其他的bean物件中

    1)屬性的set方法注入值的方式

    1. 宣告變數並在需要依賴注入的地方設定set方法

    2. 在xml檔案中配置該變數的值

          <bean id="us" class="com.qc.service.impl.UserServiceImpl">
              <property name="userDao" ref=""></property>
              <property name="name" value=""></property>
          </bean>
      

      引數說明:如果property中的是一個簡單型別(基本型別和字串),那麼就使用value,否則就使用ref

    2)構造方法賦值的方法

    1. 在需要的地方新增構造方法

    2. 在配置檔案中使用帶引數的構造方法傳參

          <bean id="userDao" class="com.qc.dao.impl.UserDaoImpl"></bean>
          <bean id="us" class="com.qc.service.impl.UserServiceImpl">
              <constructor-arg name="userDao" ref="userDao"></constructor-arg>
          </bean>
      
    3. 其他型別的引數傳參

      準備一個java類

      public class CollectionBean {
          private String[] strs;
          private List<String> list;
          private Map<String, String> map;
          private Properties properties;
      
          public Properties getProperties() {
              return properties;
          }
      
          public void setProperties(Properties properties) {
              this.properties = properties;
          }
      
          public Map<String, String> getMap() {
              return map;
          }
      
          public void setMap(Map<String, String> map) {
              this.map = map;
          }
      
          public String[] getStrs() {
              return strs;
          }
      
          public List<String> getList() {
              return list;
          }
      
          public void setList(List<String> list) {
              this.list = list;
          }
      
          public CollectionBean() {
          }
      
          public void setStrs(String[] strs) {
              this.strs = strs;
          }
      
          public CollectionBean(String[] strs) {
              this.strs = strs;
          }
      }
      
      1. 不可變陣列

        <bean id="co" class="com.qc.service.CollectionBean">
                <property name="strs">
                    <array>
                        <value>小美</value>
                        <value>小張</value>
                        <value>小王</value>
                    </array>
                </property>
            </bean>
        
      2. 可變集合

            <bean id="co" class="com.qc.service.CollectionBean">
                <property name="list">
                    <list>
                        <value>張三</value>
                        <value>李四</value>
                        <value>王五</value>
                    </list>
                </property>
            </bean>
        
      3. map集合

        <bean id="co" class="com.qc.service.CollectionBean">
                <property name="map">
                    <map>
                        <entry key="name" value="zhangsan"></entry>
                        <entry key="age" value="18"></entry>
                    </map>
                </property>
            </bean>
        
      4. 其他型別

          <bean id="co" class="com.qc.service.CollectionBean">
                <property name="properties">
                    <props>
                        <prop key="username">zhangsan</prop>
                        <prop key="password">1234</prop>
                    </props>
                </property>
            </bean>
        
  4. 多配置檔案

    1. 在配置檔案中使用

      <import resource="applicationContext.xml"></import>
      
    2. 建立工廠時直接載入多個配置檔案

              ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext1.xml");
      

相關文章