1. 程式人生 > 實用技巧 >STM32之旅4——USART

STM32之旅4——USART

Sfring

1、spring是什麼:

  • Spring是一個輕量級的控制反轉(IoC)面向切面(AOP)的容器(框架)。

    • 控制反轉:通過描述(XML或註解)通過第三方去生產或獲取特定物件的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入(Dependency Injection,DI)。
    • 面向切邊:在一類業務裡面,橫向的增加新功能而不改變原有程式碼
  • spring容器特點:

    • 物件交給容器管理,不需要我們自己去new。
    • 其配置檔案可以集合mybatis的配置檔案,把sqlsession等mybatis物件收入spring容器中。

2、spring基本程式碼:

  1. 構建空的maven專案,dom匯入依賴,刪除掉src目錄,再構建模組。

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
    
  2. 在resources目錄下構造spring配置檔案‘ContextApplication.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">
    </beans>
    
    1. bean的配置:
    <!--
       id 是bean的識別符號,要唯一,如果沒有配置id,name就是預設識別符號
       如果配置id,又配置了name,那麼name是別名
       name可以設定多個別名,可以用逗號,分號,空格隔開
       如果不配置id和name,可以根據applicationContext.getBean(.class)獲取物件;
       class是bean的全限定名=包名+類名,需要用雙引號括起來
    -->
    <bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
       <property name="name" value="Spring"/>
    </bean>
    
    1. spring配置檔案的匯入:
    <import resource="{path}/beans.xml"/>
    

​ 5. 呼叫ioc容器及其中的bean物件:

//呼叫spring配置檔案new出context
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);
//呼叫物件的方法 .
user.show();

3、依賴注入:

  1. 依賴注入是什麼:

    1. 依賴: Bean物件的依賴資源 .
  2. 注入:由容器來設定和裝配 .

  3. 怎麼注入:

    1. set注入:

    條件:物件的屬性有對應的 set方法,set方法的方法名由set + 屬性首字母大寫 , 如果屬性是boolean型別 , 沒有set方法 , 是 is .

     ```xml
     <property name="set後面的名字" ref="引用的另外一個bean"/>
     <property name="set後面的名字" value="基本資料值"/>
    
     
     	2. 構造器注入:
      
    條件:有對應的有參構造器
      
         ```xml
         <!-- 第一種根據index引數下標設定 -->
         <bean id="userT" class="com.kuang.pojo.UserT">
            <!-- index指構造方法 , 下標從0開始 -->
            <constructor-arg index="0" value="kuangshen2"/>
         </bean>
         <!-- 第二種根據引數名字設定 -->
         <bean id="userT" class="com.kuang.pojo.UserT">
            <!-- name指引數名 -->
            <constructor-arg name="name" value="kuangshen2"/>
         </bean>
         <!-- 第三種根據引數型別設定 -->
         <bean id="userT" class="com.kuang.pojo.UserT">
            <constructor-arg type="java.lang.String" value="kuangshen2"/>
         </bean>
    
    1. 屬性直接注入:(不通過構造器和set方法)

      1. 常量注入

        <property name="普通資料型別" value="值"/>
        
    ```
      
    2. **Bean注入** 
      
         ```xml
         <bean id="addr" class="com.kuang.pojo.Address">
              <property name="address" value="重慶"/>
          </bean>
          
          <bean id="student" class="com.kuang.pojo.Student">
              <property name="name" value="小明"/>
              <property name="address" ref="addr"/>
          </bean>
    ```
      
    3. **陣列注入**
      
         ```xml
         <property name="books">
              <array>
                  <value>西遊記</value>
                  <value>紅樓夢</value>
                  <value>水滸傳</value>
              </array>
          </property>
    ```
      
    4. **List注入**
      
         ```xml
         <list>
              <value>聽歌</value>
              <value>看電影</value>
              <value>爬山</value>
          </list>
    ```
      
    5. **Map注入**
      
         ```xml
         <map>
             <entry key="" value=""></entry>
         </map>  
    ```
      
    6. **set注入**
      
         ```xml
         <property name="games">
              <set>
                  <value>LOL</value>
              </set>
          </property>
    ```
      
    7. **Null注入**
      
         ```xml
          <property name="wife"><null/></property>
    ```
      
    8. **Properties注入**
      
         ```
          <property name="info">
              <props>
                  <prop key="學號">20190604</prop>
                  <prop key="性別">男</prop>
                  <prop key="姓名">小明</prop>
              </props>
          </property>
         ```
    

4、自動裝配:

  1. 什麼是自動裝配:

    1. spring會在應用上下文中為某個bean尋找其依賴的bean。

    2. 三種裝配機制:

      1. 在xml中顯式配置;(最不推薦)
        2. 在java中顯式配置;(註解)
        3. 隱式的bean發現機制和自動裝配。(重點講解)
    3. 在xml中顯式配置:

      1. autowire byName (按名稱自動裝配)
    • 修改bean配置,增加一個屬性 autowire="byName"

      <bean id="user" class="com.kuang.pojo.User" autowire="byName">
         <property name="str" value="qinjiang"/>
      </bean>
      
    • 小結:當一個bean節點帶有 autowire byName的屬性時。

      1. 將查詢其類中所有的set方法名,例如setCat,獲得將set去掉並且首字母小寫的字串,即cat。

      2. 去spring容器中尋找是否有此字串名稱id的物件。

      3. 如果有,就取出注入;如果沒有,就報空指標異常。

        1. autowire byType(按型別自動裝配)
    • 使用autowire byType首先需要保證:同一型別的物件,在spring容器中唯一。如果不唯一,會報不唯一的異常。

    1. 在java中顯式配置;(註解)

      1. 準備工作:
    • 在spring配置檔案中引入context檔案頭

    • xmlns:context="http://www.springframework.org/schema/context"
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      
      2. 開啟屬性註解支援:
      
    <context:annotation-config/>
    
    1. @Autowired

    • @Autowired是按型別自動轉配的,不支援id匹配。
    • 需要匯入 spring-aop的包!
    • 寫在需要自動配置的屬性的上一行

    @Qualifier

    • 與autowired搭配使用,不可單獨使用,可以根據byName的方式自動裝配

    @Resource(java的方法)

    • @Resource如有指定的name屬性,先按該屬性進行byName方式查詢裝配;
    • 其次再進行預設的byName方式進行裝配;
    • 如果以上都不成功,則按byType的方式自動裝配。
    • 都不成功,則報異常。

    小結:

    @Autowired與@Resource異同:

    1、@Autowired與@Resource都可以用來裝配bean。都可以寫在欄位上,或寫在setter方法上。

    2、@Autowired預設按型別裝配(屬於spring規範),預設情況下必須要求依賴物件必須存在,如果要允許null 值,可以設定它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier註解進行使用

    3、@Resource(屬於J2EE復返),預設按照名稱進行裝配,名稱可以通過name屬性進行指定。如果沒有指定name屬性,當註解寫在欄位上時,預設取欄位名進行按照名稱查詢,如果註解寫在setter方法上預設取屬性名進行裝配。當找不到與名稱匹配的bean時才按照型別進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。

    它們的作用相同都是用註解方式注入物件,但執行順序不同。@Autowired先byType,@Resource先byName。

5、spring使用註解開發

環境準備:

需要先引入aop包(在先前引入的spring依賴中已有)

引入一個context約束(idea在使用context方法時自動會引入)

Bean的實現:

說明:我們之前都是使用 bean 的標籤進行bean注入,但是實際開發中,我們一般都會使用註解!

1、配置掃描哪些包下的註解

<!--指定註解掃描包-->
<context:component-scan base-package="com.kuang.pojo"/>

2、在指定包下編寫類,增加註解

@Component("user")
// 相當於配置檔案中 <bean id="user" class="當前註解的類"/>
public class User {
   public String name = "秦疆";
}

3、測試

@Test
public void test(){
   ApplicationContext applicationContext =
       new ClassPathXmlApplicationContext("beans.xml");
   User user = (User) applicationContext.getBean("user");
   System.out.println(user.name);
}

屬性注入

使用註解注入屬性

1、可以不用提供set方法,直接在直接名上新增@value("值")

@Component("user")
// 相當於配置檔案中 <bean id="user" class="當前註解的類"/>
public class User {
   @Value("秦疆")
   // 相當於配置檔案中 <property name="name" value="秦疆"/>
   public String name;
}

2、如果提供了set方法,在set方法上新增@value("值");

@Component("user")
public class User {

   public String name;

   @Value("秦疆")
   public void setName(String name) {
       this.name = name;
  }
}

小結

XML與註解比較

  • XML可以適用任何場景 ,結構清晰,維護方便
  • 註解不是自己提供的類使用不了,開發簡單方便

xml與註解整合開發 :推薦最佳實踐

  • xml管理Bean
  • 註解完成屬性注入
  • 使用過程中, 可以不用掃描,掃描是為了類上的註解
<context:annotation-config/>  

作用:

  • 進行註解驅動註冊,從而使註解生效
  • 用於啟用那些已經在spring容器裡註冊過的bean上面的註解,也就是顯示的向Spring註冊
  • 如果不掃描包,就需要手動配置bean
  • 如果不加註解驅動,則注入的值為null!

基於Java類進行配置

JavaConfig 原來是 Spring 的一個子專案,它通過 Java 類的方式提供 Bean 的定義資訊,在 Spring4 的版本, JavaConfig 已正式成為 Spring4 的核心功能 。

測試:

1、編寫一個實體類,Dog

@Component  //將這個類標註為Spring的一個元件,放到容器中!
public class Dog {
   public String name = "dog";
}

2、新建一個config配置包,編寫一個MyConfig配置類

@Configuration  //代表這是一個配置類
public class MyConfig {

   @Bean //通過方法註冊一個bean,這裡的返回值就Bean的型別,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }

}

3、測試

@Test
public void test2(){
   ApplicationContext applicationContext =
           new AnnotationConfigApplicationContext(MyConfig.class);
   Dog dog = (Dog) applicationContext.getBean("dog");
   System.out.println(dog.name);
}

4、成功輸出結果!

說明:@Import(MyConfig2.class) //匯入合併其他配置類,類似於配置檔案中的 inculde 標籤

6、代理模式:

1、靜態代理

​ 問題引入:假設現在專案經理有一個需求:在專案現有所有類的方法前後列印日誌。

​ 你如何在不修改已有程式碼的前提下,完成這個需求?

1.為現有的每一個類都編寫一個對應的代理類,並且讓它實現和目標類相同的介面(假設都有)

2.在建立代理物件時,通過構造器塞入一個目標物件,然後在代理物件的方法內部呼叫目標物件同名方法,並在呼叫前後列印日誌。也就是說,代理物件 = 增強程式碼 + 目標物件(原物件)。有了代理物件後,就不用原物件了

靜態代理的缺陷:

程式設計師要手動為每一個目標類編寫對應的代理類。如果當前系統已經有成百上千個類,工作量太大了。所以,現在我們的努力方向是:如何少寫或者不寫代理類,卻能完成代理功能?

2、複習物件的建立

很多初學Java的朋友眼中建立物件的過程:

反射下的物件建立:

要建立一個例項,最關鍵的就是得到對應的Class物件。

Class物件包含了一個類的所有資訊,比如構造器、方法、欄位等。如果我們不寫代理類,這些資訊從哪獲取呢?苦思冥想,突然靈光一現:代理類和目標類理應實現同一組介面。之所以實現相同介面,是為了儘可能保證代理物件的內部結構和目標物件一致,這樣我們對代理物件的操作最終都可以轉移到目標物件身上,代理物件只需專注於增強程式碼的編寫。

希望可以通過介面直接得到代理物件------問題:介面無法自己建立物件!

3、動態代理

動態代理相關的類和方法:java.lang.reflect.InvocationHandler介面和 java.lang.reflect.Proxy類,這兩個類相互配合,入口是Proxy,所以我們先聊它。

Proxy有個靜態方法:getProxyClass(ClassLoader, interfaces),只要你給它傳入類載入器和一組介面,它就給你返回代理Class物件。

用通俗的話說,getProxyClass()這個方法,會從你傳入的介面Class中,“拷貝”類結構資訊到一個新的Class物件中,但新的Class物件帶有構造器,是可以建立物件的。打個比方,一個大內太監(介面Class),空有一身武藝(類資訊),但是無法傳給後人。現在江湖上有個妙手神醫(Proxy類),發明了克隆大法(getProxyClass),不僅能克隆太監的一身武藝,還保留了小DD(構造器)...(這到底是道德の淪喪,還是人性的扭曲,歡迎走進動態代理)

所以,一旦我們明確介面,完全可以通過介面的Class物件,建立一個代理Class,通過代理Class即可建立代理物件。

得到代理Class物件後,Class物件有一個構造器,,需要傳入InvocationHandler。而且代理物件的每個方法內部都會呼叫handler.invoke()!InvocationHandler物件成了代理物件和目標物件的橋樑,不像靜態代理這麼直接。

當代理物件呼叫方法時,在傳入InvocationHandler中包含了mothed方法和對應的引數,在呼叫handler.invoke()

所有呼叫的方法都會最終導向invoke方法,需要在裡面具體實現目標類的方法和代理方法(呼叫目標類的方法需要又重新new目錄類)

但這種寫法不夠優雅,屬於硬編碼。我這次代理A物件,下次想代理B物件還要進來改invoke()方法,太差勁了。改進一下,讓呼叫者把目標物件作為引數傳進來:

public class ProxyTest {
	public static void main(String[] args) throws Throwable {
		CalculatorImpl target = new CalculatorImpl();
                //傳入目標物件
                //目的:1.根據它實現的介面生成代理物件 2.代理物件呼叫目標物件方法
		Calculator calculatorProxy = (Calculator) getProxy(target);
		calculatorProxy.add(1, 2);
		calculatorProxy.subtract(2, 1);
	}

	private static Object getProxy(final Object target) throws Exception {
		//引數1:隨便找個類載入器給它, 引數2:目標物件實現的介面,讓代理物件實現相同介面
		Class proxyClazz = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces());
		Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
		Object proxy = constructor.newInstance(new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println(method.getName() + "方法開始執行...");
				Object result = method.invoke(target, args);
				System.out.println(result);
				System.out.println(method.getName() + "方法執行結束...");
				return result;
			}
		});
		return proxy;
	}
}

這樣就非常靈活,非常優雅了。無論現在系統有多少類,只要你把例項傳進來,getProxy()都能給你返回對應的代理物件。就這樣,我們完美地跳過了代理類,直接建立了代理物件!

不過實際程式設計中,一般不用getProxyClass(),而是使用Proxy類的另一個靜態方法:Proxy.newProxyInstance(),直接返回代理例項,連中間得到代理Class物件的過程都幫你隱藏

public class ProxyTest {
	public static void main(String[] args) throws Throwable {
		CalculatorImpl target = new CalculatorImpl();
		Calculator calculatorProxy = (Calculator) getProxy(target);
		calculatorProxy.add(1, 2);
		calculatorProxy.subtract(2, 1);
	}

	private static Object getProxy(final Object target) throws Exception {
		Object proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),/*類載入器*/
				target.getClass().getInterfaces(),/*讓代理物件和目標物件實現相同介面*/
				new InvocationHandler(){/*代理物件的方法最終都會被JVM導向它的invoke方法*/
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println(method.getName() + "方法開始執行...");
						Object result = method.invoke(target, args);
						System.out.println(result);
						System.out.println(method.getName() + "方法執行結束...");
						return result;
					}
				}
		);
		return proxy;
	}

代理物件的本質就是:和目標物件實現相同介面的例項。代理Class可以叫任何名字,whatever,只要它實現某個介面,就能成為該介面型別。

動態代理的弊端:

​ 動態代理生成的代理物件,最終都可以用介面接收,和目標物件一起形成了多型,可以隨意切換展示不同的功能。但是切換的同時,只能使用該介面定義的方法。

7、spring結合mybatis

1、環境準備:

pom依賴匯入:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.5</version>
    </dependency>

</dependencies>

2、mybatis結構:

java:Mapper(介面,宣告操作資料庫的方法) Mapper.xml(連線介面,對應方法編寫sql語句)

resources: mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置檔案-->
 <properties resource="db.properties">

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root

工具類:utils:(得到sqlsession)

//sqlSessionFactory --> sqlSession
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            //使用Mybatis第一步:獲取sqlSessionFactory物件
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //既然有了 SqlSessionFactory,顧名思義,我們就可以從中獲得 SqlSession 的例項了。
    // SqlSession 完全包含了面向資料庫執行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

myService:操作mapper

   SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);

3、spring結合mybatis

1、增加spring-dao.xml配置

(代替db.properties)

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

(建立sqlSessionFactory代替工具類,且可結合mybatis-config.xml)

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml">
        </property>
</bean>

(為Mapper建立實現類MapperImpl,減少了Service操作,使其更加存粹)

得到MapperImpl物件的兩種方式:

方法一:spring-dao.xml中在ioc容器中存入sqlSession和對應的MapperImpl物件:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
    </bean>
    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"></property>
    </bean>

方法二::spring-dao.xml中在ioc容器中存入MapperImpl物件,引數直接引入sqlSessionFactory、

<bean id="userMapper1" class="com.kuang.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>

在MapperImpl在繼承SqlSessionDaoSupport,直接getSqlSession()得到SqlSession物件

8、事務

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="txpointCut" expression="execution(* com.kuang.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointCut"/>
</aop:config>

事務只在方法裡面實現