用dw個人網站怎么做優(yōu)化網站的軟件下載
文章目錄
- spring6
- 1. 一些基本的概念、優(yōu)勢
- 2. 入門案例實現(xiàn)
- maven聚合工程創(chuàng)建步驟
- 分析實現(xiàn)過程
- 3. IoC(Inversion of Control)
- 基于xml的bean環(huán)境搭建
- 獲取bean
- 獲取接口創(chuàng)建實現(xiàn)類
- 依賴注入 setter注入 和 構造器注入
- 原生方式的setter注入
- 原生方式的構造器注入
- 使用bean的方式進行依賴注入步驟
- 對象類型屬性賦值步驟
- 引入外部屬性文件步驟
- bean的八大生命周期(重要)
- 基于xml的bean自動裝配
- 基于注解的bean自動裝配(極其重要)
- 使用注解創(chuàng)建對象
- 手動實現(xiàn)IoC
- 實現(xiàn)過程
- 手動實現(xiàn)一個IoC反射
- 手動實現(xiàn)屬性注入過程
- 4. 面向切面編程
- 靜態(tài)代理圖解
- 靜態(tài)代理術語
- 動態(tài)代理
- 動態(tài)代理的實現(xiàn)過程
- 概念
- 相關術語
- AOP思想綜述
- 基于注解方式配置AOP
- 通知類型及其使用
- bean配置文件
- 測試文件
- 報錯信息解決
- 重用切入點表達式
- 5. 單元測試
- 6. 事務
- JdbcTemplate對象操作Mysql數據庫(Spring事務處理前置知識)
- 準備環(huán)境
- 利用JdbcTemplate對象完成對數據庫的增加、修改、刪除操作
- 利用JdbcTemplate對象完成對數據庫的查詢操作
- 報錯信息解決
- 事務處理
- 概念
- 特性
- 聲明式事務管理(基于注解實現(xiàn))
- 準備環(huán)境(基于上面的JDBC示例進行添加,目錄結構如下)
- BookController
- BookDao
- BookDaoImpl
- BookService
- BookServiceImpl
- TestBookTx
- 分析
- 開啟事務步驟(使用注解的方式)
- 事務注解的一些屬性
- Resoource資源操作
- UrlResource實現(xiàn)類訪問url資源
- ClassPathResourceDemo訪問本地資源(項目內資源)
- loadAndReadUrlResource訪問磁盤內文件
- 國際化:i18n
- 數據校驗
- 通過Validator接口實現(xiàn)數據校驗
- 基于注解校驗
- 基于方法校驗
- 自定義校驗
- AOT
導語:感覺自己記得可能不全,所以我找了一篇優(yōu)秀的筆記
(雙手)奉上鏈接:https://lixx.cn/archives/spring6-2
https://lixx.cn/categories/spring6
spring6
1. 一些基本的概念、優(yōu)勢
主流,輕量級、開源 框架
廣義劃分:以SpringFirework為核心的Spring技術棧
狹義劃分:專指SpringFireWork
兩大核心:Ioc和AOP
特點:
- 非侵入式:功能組件使用注解進行標記,簡潔。
- 控制反轉:自動創(chuàng)建對象,讓我們享受資源注入。
- 面向切面編程:在不修改源代碼的基礎上增強代碼功能。
- 組件化:拆分復雜應用為多個組件共同實現(xiàn)
- 一站式:在集合許多依賴
2. 入門案例實現(xiàn)
maven聚合工程創(chuàng)建步驟
- 父工程 - spring6
- 子模塊 - spring-first
- 引入spring相關依賴
<dependencies><!--引入spring-context依賴--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.10</version></dependency><!--測試依賴:junit--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.2</version></dependency>
</dependencies>
- 創(chuàng)建類,定義屬性和方法
package com.itchen.spring6;public class User {public void add(){System.out.println("add ...");}public static void main(String[] args) {User user = new User();user.add();}
}
-
按照spring要求創(chuàng)建一個配置文件.xml
-
在配置文件中創(chuàng)建相應的配置信息
<?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"><!--完成對User對象的創(chuàng)建id屬性:唯一標識 (為了方便一般使用類的首字母小寫)class:要創(chuàng)建的類的對象的全路徑(包名稱 + 類名稱)--><bean id="user" class="com.itchen.spring6.User"></bean>
</beans>
- 最終測試
package com.itchen.spring6;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {@Testpublic void testUserObject(){// 加載spring配置文件,對象創(chuàng)建ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 獲取創(chuàng)建的對象User user = (User)context.getBean("user");System.out.println(user);// 使用對象的調用方法進行測試user.add();}
}
- 輸出了user對象的全路徑和user的地址
分析實現(xiàn)過程
- 執(zhí)行了實體類的無參數構造方法
- 如何返回創(chuàng)建的對象
- 加載bean.xml配置文件
- 對xml文件進行解析操作
- 獲取標簽的屬性值
- 使用反射根據類的全路徑創(chuàng)建對象
- 對象存放在項目的一個Map集合中;格式<key,value>
3. IoC(Inversion of Control)
Spring通過loC容器來管理所有Java對象的實例化和初始化,控制對象與對象之間的依賴關系。我們將由loC容器管理的Java對象稱為Spring Bean,它與使用關鍵字new創(chuàng)建的Java對象沒有任何區(qū)別。
將對象的創(chuàng)建和對象與對象的關系都交給了容器進行管理。
基于xml的bean環(huán)境搭建
- 將子模塊pom文件中的依賴放在父模塊中(子模塊依然可以調用相應的依賴)
- 創(chuàng)建資源文件(.xml)和java實體類
獲取bean
- 實體類user.java
package com.itchen.spring6.iocxml;public class User {private String name;private Integer age;public void run(){System.out.println("run ...");}
}
- bean資源文件獲取
- 注意:此時的class類型的bean只能有一個
<?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="user" class="com.itchen.spring6.iocxml.User"></bean>
</beans>
- 測試獲取bean的三種方式
package com.itchen.spring6.iocxml;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestUser {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 根據對象獲取beanUser user = (User)context.getBean("user");System.out.println("根據id獲取bean:" + user);// 根據類型獲取beanUser user2 = (User)context.getBean(User.class);System.out.println("根據類型獲取bean:" + user2);// 根據id和類型獲取beanUser user3 = (User)context.getBean("user", User.class);System.out.println("根據id和類型獲取bean:" + user3);}
}
獲取接口創(chuàng)建實現(xiàn)類
- 創(chuàng)建接口,聲明需要實現(xiàn)的方法;在實體類中實現(xiàn)接口。
- 在bean.xml中配置相應的impl實現(xiàn)類
<bean id="USerDaoImpl" class="com.itchen.spring6.iocxml.bean.USerDaoImpl"></bean>
注意:這里的bean必須是唯一的。一個接口只能由一個impl實體類來實現(xiàn)
依賴注入 setter注入 和 構造器注入
原生方式的setter注入
先在類中定義基本的set和get方法,然后在其他類中創(chuàng)建類的對象,調用其setget方法來傳值。
原生方式的構造器注入
先定義基本的有參構造器和無參構造器,然后在創(chuàng)建對象的時候在構造器中傳入相對應的值。
使用bean的方式進行依賴注入步驟
- 創(chuàng)建一個類,在其中定義基本屬性,生成set方法
- 在spring中進行配置
<bean id="book" class="com.itchen.spring6.iocxml.di.Book"><property name="bname" value="java后端開發(fā)"></property><property name="author" value="陳志偉"></property>
</bean>
- 測試
@Test
public void testSetter(){// 1. 加載配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");Book book = context.getBean("book", Book.class);System.out.println(book);
}
or
- 創(chuàng)建類,定義屬性,生成有參數的構造方法
- 進行配置
<bean id="BookConstructor" class="com.itchen.spring6.iocxml.di.Book"><constructor-arg name="bname" value="前端開發(fā)"></constructor-arg><constructor-arg name="author" value="陳志偉"></constructor-arg>
</bean>
- 測試
注意:setter注入調用的是無參構造方法,而構造器注入調用的是有參構造方法
對象類型屬性賦值步驟
- 準備工作:創(chuàng)建兩個類(班和學生),學生中定義班的對象。定義其中的構造方法、和get、set方法
- 創(chuàng)建bean.xml(名字任意)
<!--第一種方式:引入外部bean的方式-->
<bean id="class" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="241班"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Aim"></property><property name="age" value="22"></property><property name="cls" ref="class"></property>
</bean>
<!--第二種方式:引入內部bean的方式-->
<bean id="student2" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Mary"></property><property name="age" value="20"></property><!--內部bean--><property name="cls"><bean id="class2" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="242班"></property></bean></property>
</bean>
<!--第三種方式:級聯(lián)屬性賦值-->
<bean id="cls3" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="144班"></property></bean><bean id="student3" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Tom"></property><property name="age" value="28"></property><property name="cls" ref="class"></property><!--級聯(lián)再賦值--><property name="cls.cname" value="141班"></property></bean>
<!--第四種方式:數組注入-->
<!--注入數組類型的屬性-->
<bean id="clss" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="143班"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.ditest.Student"><!--普通--><property name="sname" value="Aim"></property><property name="age" value="22"></property><!--對象--><property name="cls" ref="clss"></property><!--數組--><property name="loves"><array><value>吃飯</value><value>睡覺</value><value>玩jj</value></array></property>
</bean>
<!--第五種方式:集合類型屬性的注入-->
<bean id="studen1" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Aim"></property><property name="age" value="19"></property>
</bean>
<bean id="studen2" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Mary"></property><property name="age" value="19"></property>
</bean>
<bean id="studen3" class="com.itchen.spring6.iocxml.ditest.Student"><property name="sname" value="Tom"></property><property name="age" value="19"></property>
</bean>
<bean id="cls" class="com.itchen.spring6.iocxml.ditest.Class"><property name="cname" value="142班"></property><property name="stuList"><list><ref bean="studen1"></ref><ref bean="studen2"></ref><ref bean="studen3"></ref></list></property>
</bean>
注意:因為這里的包含關系變了,需要修改一下測試方法
public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean-diarr.xml");// 學生的對象Student stu = context.getBean("student",Student.class);stu.work();
}
<!--第六種方式:map類型屬性注入-->
<!--1. 創(chuàng)建兩個對象:用bean標簽進行配置2. 注入它的普通類型的屬性3. 在學生的bean中注入map集合的屬性
-->
<bean id="teacher" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tid" value="1000"></property><property name="tname" value="小李"></property>
</bean>
<bean id="student" class="com.itchen.spring6.iocxml.dimap.Student"><property name="sid" value="2000"></property><property name="sname" value="小明"></property><property name="teacherMap"><map><entry><key><value>10010</value></key><!--這里的(鍵值對的值)值如果是一個普通的值,可以使用value--><ref bean="teacher"></ref></entry></map></property>
</bean>
<!--第七種方式:集合類型的屬性注入-->
<!--1. 創(chuàng)建三個對象2. 注入普通類型的屬性3. 使用“util:類型” 定義4. 在學生bean中引入3.的bean
--><bean id="student" class="com.itchen.spring6.iocxml.dimap.Student"><property name="sid" value="10000"></property><property name="sname" value="張三"></property><!--注入list和map類型屬性--><property name="lessonList" ref="lessonList"></property><property name="teacherMap" ref="teacherMap"></property>
</bean><util:list id="lessonList"><ref bean="lesson1"></ref><ref bean="lesson2"></ref>
</util:list>
<util:map id="teacherMap"><entry><key><value>10010</value></key><ref bean="teacher1"></ref></entry><entry><key><value>10086</value></key><ref bean="teacher2"></ref></entry>
</util:map><bean id="lesson1" class="com.itchen.spring6.iocxml.dimap.Lesson"><property name="lessonName" value="java開發(fā)"></property>
</bean>
<bean id="lesson2" class="com.itchen.spring6.iocxml.dimap.Lesson"><property name="lessonName" value="前端開發(fā)"></property>
</bean><bean id="teacher1" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tname" value="西門慶"></property><property name="tid" value="1000"></property>
</bean>
<bean id="teacher2" class="com.itchen.spring6.iocxml.dimap.Teacher"><property name="tname" value="歐陽弧"></property><property name="tid" value="2000"></property>
</bean>
注意:第七種方式在使用的時候需要引入標簽相應的命名空間
<?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:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
引入外部屬性文件步驟
- 引入外部屬性文件的相關依賴【pom文件】
- 創(chuàng)建一個外部的屬性文件。定義數據信息:用戶名、密碼、地址。【.properties文件】
- 創(chuàng)建spring的配置文件,引入context的命名空間。引入屬性文件,使用表達式完成注入。
<!--引入外部文件 完成數據庫的信息注入-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.user}"></property><property name="password" value="${jdbc.password}"></property><property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
bean的八大生命周期(重要)
可以在配置文件中【bean.xml文件】使用參數的形式制定bean對象的初始化函數和銷毀函數。
- bean對象的創(chuàng)建(調用無參構造方法)
- 給bean對象設置相關屬性
- bean后置處理器(初始化之前)
- bean對象初始化(調用制定的初始化方法)
- bean后置處理器(初始化之后)
- bean對象創(chuàng)建完成了,可以使用了
- bean對象銷毀(配置制定的銷毀方法)
- IoC容器關閉
基于xml的bean自動裝配
<!--根據類型自動裝配-->
<bean id="userController" class="com.itchen.spring6.iocxml.auto.Controller.UserController" autowire="byType"></bean>
<bean id="userService" class="com.itchen.spring6.iocxml.auto.Service.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.itchen.spring6.iocxml.auto.dao.UserDaoImpl" autowire="byType"></bean>
<!--根據名稱自動裝配-->
<bean id="userController" class="com.itchen.spring6.iocxml.auto.Controller.UserController" autowire="byName"></bean>
<bean id="userService" class="com.itchen.spring6.iocxml.auto.Service.UserServiceImpl" autowire="byName"></bean>
<bean id="userDao" class="com.itchen.spring6.iocxml.auto.dao.UserDaoImpl" autowire="byName"></bean>
<!--這時需要保證類的名字和id的名字一模一樣-->
基于注解的bean自動裝配(極其重要)
使用注解創(chuàng)建對象
- 引入依賴
- 開啟組件掃描
<context:component-scan base-package="com.itchen.spring6"></context:component-scan>
- 被掃描的類用
@component
修飾;自動注入的屬性用@Autowired
修飾;使用注解定義bean
注解 | 說明 |
---|---|
@Component | 該注解用于描述 Spring 中的 Bean,它是一個泛化的概念,僅僅表示容器中的一個組件(Bean),并且可以作用在應用的任何層次,例如 Service 層、Dao 層等。 使用時只需將該注解標注在相應類上即可。 |
@Repository | 該注解用于將數據訪問層(Dao 層)的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Service | 該注解通常作用在業(yè)務層(Service 層),用于將業(yè)務層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
@Controller | 該注解通常作用在控制層(如SpringMVC 的 Controller),用于將控制層的類標識為 Spring 中的 Bean,其功能與 @Component 相同。 |
手動實現(xiàn)IoC
主要使用java的反射機制和注解進行實現(xiàn)
實現(xiàn)過程
- 創(chuàng)建子模塊
- 創(chuàng)建測試類
- 創(chuàng)建兩個注解
- 約定注解@Bean @Di
- 創(chuàng)建bean容器的接口ApplicationContext,定義方法,返回對象
- 實現(xiàn)bean的容器接口
- 返回對象
- 根據包的規(guī)則加載bean(設置包的掃描規(guī)則)
手動實現(xiàn)一個IoC反射
loadBean步驟:
// 1. 判斷當前是否是文件夾
// 2. 獲取文件夾里面的所有內容
// 3. 文件夾里面為空,直接返回
// 4. 如果文件夾里面不為空
// 遍歷得到的每個file對象 重復3. 4.// 5. 如果是文件,得到包路徑 + 類名稱
// 6. 判斷文件是否是.class類型,如果是,則去掉后綴
// 7. 如果有@Bean注解使用反射實例化
// 將實例化結果放入map集合中
package com.itchen.bean;import com.itchen.annotation.Bean;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext{// 創(chuàng)建map集合用于創(chuàng)建bean的實例對象private static Map<Class,Object> beanFactory = new HashMap<>();private static String rootPath;// 返回對象@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}// 設置包的掃描規(guī)則// 創(chuàng)建有參構造,傳遞包路徑public static void pathDemo1(String basePackage){// 把點替換為反斜杠String packagePath = basePackage.replaceAll("\\.", "\\\\");// 獲取包的絕對路徑try {Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packagePath);while(dirs.hasMoreElements()){URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(), "utf-8");rootPath = filePath.substring(0, filePath.length() - packagePath.length());System.out.println(filePath);loadBean(new File(filePath));}} catch (IOException e) {throw new RuntimeException(e);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}// 包掃描過程,實例化beanprivate static void loadBean(File fileParent) throws ClassNotFoundException {// 1. 判斷當前是否是文件夾if (fileParent.isDirectory()) {// 2. 獲取文件夾里面的所有內容File[] childrenFiles = fileParent.listFiles();// 3. 文件夾里面為空,直接返回if (childrenFiles == null || childrenFiles.length == 0) {return;}// 4. 如果文件夾里面不為空// 遍歷得到的每個file對象 重復3. 4.for (File child : childrenFiles) {if (child.isDirectory()) {loadBean(child);} else {// 5. 如果是文件,得到包路徑 + 類名稱String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);// 6. 判斷文件是否是.class類型,如果是,則去掉后綴String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);// 7. 如果有@Bean注解使用反射實例化if (!aClass.isInterface()) {Bean annotation = aClass.getAnnotation(Bean.class);if (annotation != null) {Object instance = aClass.newInstance();if (aClass.getInterfaces().length > 0) {System.out.println("正在加載【" + aClass.getInterfaces()[0] + "】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);} else {System.out.println("正在加載【" + aClass.getName() + "】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}// 將實例化結果放入map集合中}catch (Exception e){e.printStackTrace();}}}}}
}
手動實現(xiàn)屬性注入過程
// 1. 實例化對象在beanFactory的map集合里面
// 遍歷map集合// 2. 獲取所有的value值,獲取每個對象的屬性// 3. 遍歷對象屬性的數組,得到每個屬性// 4. 判斷是否有@Di注解// 5. 如果有,將對象注入
package com.itchen.bean;import com.itchen.annotation.Bean;
import com.itchen.annotation.Di;
import com.itchen.bean.ApplicationContext;import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext {//存儲bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根據包掃描加載bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}// 屬性的注入loadDi();}private void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是個文件夾就繼續(xù)調用該方法,使用了遞歸loadBean(child);} else {//通過文件路徑轉變成全類名,第一步把絕對路徑部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//選中class文件if (pathWithClass.contains(".class")) {// com.xinzhi.dao.UserDao//去掉.class后綴,并且把 \ 替換成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的類實例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判斷一下有沒有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class當成key,實例對象當成valueSystem.out.println("正在加載【"+ aClass.getInterfaces()[0] +"】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class當成key,實例對象當成valueSystem.out.println("正在加載【"+ aClass.getName() +"】,實例對象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}// 屬性注入private void loadDi() {for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){//就是咱們放在容器的對象Object obj = entry.getValue();Class<?> aClass = obj.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields){Di annotation = field.getAnnotation(Di.class);if( annotation != null ){field.setAccessible(true);try {System.out.println("正在給【"+obj.getClass().getName()+"】屬性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}}
}
4. 面向切面編程
核心業(yè)務+任務日志 – > 代碼冗余
解決:用代理模式將其分離(分離任務日志) ----------------- 解耦
靜態(tài)代理圖解
靜態(tài)代理術語
代理:將非核心代碼分離出來后,封裝這些非邏輯代理、對象、方法
目標:被代理套用的非核心代碼類、對象、方法
動態(tài)代理
原因:因為靜態(tài)代理不具備代碼的靈活性,所以引入了動態(tài)代理
動態(tài)代理的實現(xiàn)過程
package com.itchen.spring6.aop.example;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;public class ProxyFactory {// 目標對象private Object target;public ProxyFactory(Object obj){target = obj;}// 返回代理對象的方法public Object getProxy(){/*** 參數1. 類加載器* 參數2. 目標對象實現(xiàn)的所有接口* 參數3. 設置代理修改目標函數的方法* */ClassLoader classLoader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler(){@Override/*** 參數1. 代理對象* 參數2. 需要重寫目標對象中的方法啊* 參數3. 目標方法中的參數* */public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[動態(tài)代理][日志] "+method.getName()+",參數:"+ Arrays.toString(args));Object result = method.invoke(target, args);System.out.println("[動態(tài)代理][日志] "+method.getName()+",結果:"+ result);return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);}
}
自行補充計算器的基類和計算器接口類
// 測試類
package com.itchen.spring6.aop.example;public class TestCaculator {public static void main(String[] args) {// 1. 創(chuàng)建代理對象ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());Calculator proxy = (Calculator) proxyFactory.getProxy();proxy.add(1,2);proxy.mul(2,4);}
}
概念
AOP(Aspect Oriented Programming)是一種設計思想,是軟件設計領域中的面向切面編程,它是面向對象編程的一種補充和完善,它以通過預編譯方式和運行期動態(tài)代理方式實現(xiàn),在不修改源代碼的情況下,給程序動態(tài)統(tǒng)一添加額外功能的一種技術。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。
相關術語
- 橫切關注點
- 項目中相同的需要被提取的部分
- 通知(增強)
- 需要增加的功能
- 通知方法:橫切關注點的實現(xiàn)
- 通知類型:五種
- 切面 > 通知
- 封裝通知方法的類(eg. 一個切面類包含:前置通知、返回通知、后置通知等)
- 目標
- 被代理的對象
- 代理
- 向目標對象應用通知后創(chuàng)建代理對象
- 連接點
- 能使用通知的地方
- 切入點
- 實際去增強方法的位置
AOP思想綜述
將特定的代碼封裝到切面類中,將切面類用于一個新的目標方法,最終實現(xiàn) 方法的增強(方法的增強好抽象呀)
基于注解方式配置AOP
通知類型及其使用
package com.itchen.spring6.aop.annoaop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;@Aspect // 切面類
@Component // 在spring的ioc容器中進行管理
public class LogAspect {// 設置切入點和通知類型@Before(value = "pointCut()")public void beforeMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());System.out.println("Logger-->前置通知,方法名:"+methodName+",參數:"+args);}@After("execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public void afterMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->后置通知,方法名:"+methodName);}@AfterReturning(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))", returning = "result")public void afterReturningMethod(JoinPoint joinPoint, Object result){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->返回通知,方法名:"+methodName+",結果:"+result);}@AfterThrowing(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->異常通知,方法名:"+methodName+",異常:"+ex);}@Around("execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());Object result = null;try {System.out.println("環(huán)繞通知-->目標對象方法執(zhí)行之前");//目標對象(連接點)方法的執(zhí)行result = joinPoint.proceed();System.out.println("環(huán)繞通知-->目標對象方法返回值之后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("環(huán)繞通知-->目標對象方法出現(xiàn)異常時");} finally {System.out.println("環(huán)繞通知-->目標對象方法執(zhí)行完畢");}return result;}// 重用切入點表達式@Pointcut(value = "execution(* com.itchen.spring6.aop.annoaop.CalculatorImpl.*(..))")public void pointCut(){}
}
bean配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--開啟組件的掃描--><context:component-scan base-package="com.itchen.spring6.aop.annoaop"></context:component-scan><!--開啟自動代理--><aop:aspectj-autoproxy />
</beans>
測試文件
package com.itchen.spring6.aop.annoaop;import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAop {@Testpublic void testAdd(){ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Calculator calculator = context.getBean(Calculator.class);calculator.add(1,1);}
}
報錯信息解決
NoSuchBeanDefinitionException:沒有在實現(xiàn)類中加入@Component注解創(chuàng)建bean
重用切入點表達式
通知注解后面的參數重復書寫,所以可以用注解將參數定義為一個類似宏的東西
5. 單元測試
Junit依賴自動注入方便測試
整合Junit5 和 Junit4
// 不整合的時候測試
public class TestUser {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");UserController userController = context.getBean("userController", UserController.class);userController.addUser();}
}
// 整合Junit5
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5 {@Autowiredprivate User user;// 測試方法@Testpublic void testUser(){System.out.println(user);user.run();}
}
// 整合Junit4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")
public class SpringTestJunit4 {@Autowiredprivate User user;@Testpublic void testUser4(){System.out.println(user);user.run();}
}
6. 事務
JdbcTemplate對象操作Mysql數據庫(Spring事務處理前置知識)
準備環(huán)境
- 導入依賴
<!--spring jdbc Spring 持久化層支持jar包-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.2</version>
</dependency>
<!-- MySQL驅動 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
<!-- 數據源 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.15</version>
</dependency>
<!--spring對junit的支持相關依賴-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>6.0.2</version>
</dependency><!--junit5測試-->
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.0</version>
</dependency>
- 配置jdbc.properties配置文件連接數據庫
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false
jdbc.driver=com.mysql.cj.jdbc.Driver
- 配置bean.xml配置JdbcTemplate
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 導入外部屬性文件 --><context:property-placeholder location="classpath:jdbc.properties" /><!-- 配置數據源 --><bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/></bean><!-- 配置 JdbcTemplate --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!-- 裝配數據源 --><property name="dataSource" ref="druidDataSource"/></bean>
</beans>
利用JdbcTemplate對象完成對數據庫的增加、修改、刪除操作
測試代碼
package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;// 添加、修改、刪除操作@Testpublic void testUpdate(){// 添加數據String sql = "insert into t_emp values(null,?,?,?)";Object[] params = {"洛薩",32,"男"};
// 添加數據
// int rows = jdbcTemplate.update(sql,params);
// System.out.println("影響了" + rows + "行");String sql2 = "update t_emp set name=? where id=?";// 修改id為?行數據的名字字段
// int row = jdbcTemplate.update(sql2,"666",3);
// System.out.println("影響了" + row + "行");// 刪除id為?的一行String sql3 = "delete from t_emp where id=?";
// int row3 = jdbcTemplate.update(sql3,3);
// System.out.println("影響了" + row3 + "行");}
}
利用JdbcTemplate對象完成對數據庫的查詢操作
package com.itchen.spring6.jdbc;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;import java.util.List;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class jdbcTemplateTest2 {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** queryForObject* */// 查詢并獲取數據庫的第一行數據@Testpublic void testSelectObject1(){String sql = "select * from t_emp where id=?";Emp person = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {Emp emp = new Emp();emp.setId(rs.getInt("id"));emp.setName(rs.getString("name"));emp.setAge(rs.getInt("age"));emp.setSex(rs.getString("sex"));return emp;}, 1);System.out.println(person);}/*** queryForObject* */@Testpublic void testSelectObject2(){String sql = "select * from t_emp where id=?";// 利用BeanPropertyRowMapper方法自動創(chuàng)建對象接收數據庫數據Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class),2);System.out.println(emp);}/*** query* */@Testpublic void testSelectList(){String sql = "select * from t_emp";List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class));System.out.println(list);}/*** queryForObject* *///查詢單行單列的值@Testpublic void selectCount(){String sql = "select count(id) from t_emp";Integer count = jdbcTemplate.queryForObject(sql, Integer.class);System.out.println(count);}
}
報錯信息解決
BadSqlGrammarException:沒有按照規(guī)定的sql語法進行傳遞參數信息
事務處理
概念
eg.銀行轉賬,要成功都成功;要失敗,都失敗
特性
原子性:要成功,都成功
一致性:操作前和操作后總量一致
隔離性:多個事務進行操作,對各個事務之間沒有影響
持久性:提交就永久保存
聲明式事務管理(基于注解實現(xiàn))
準備環(huán)境(基于上面的JDBC示例進行添加,目錄結構如下)
BookController
package com.itchen.spring.tx.controller;import com.itchen.spring.tx.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class BookController {@Autowiredprivate BookService bookService;// 買書的方法: 圖書的id 、 用戶的idpublic void buyBook(Integer bookId,Integer userId){// 調用Service方法bookService.buyBook(bookId,userId);}
}
BookDao
package com.itchen.spring.tx.dao;public interface BookDao {Integer getBookPriceByBookId(Integer bookId);void updateStock(Integer bookId);void updateUserBalance(Integer userId,Integer price);
}
BookDaoImpl
package com.itchen.spring.tx.dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class BookDaoImpl implements BookDao{@Autowiredprivate JdbcTemplate jdbcTemplate;Integer price;@Overridepublic Integer getBookPriceByBookId(Integer bookId) {String sql = "select price from t_book where book_id=?";price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);return price;}@Overridepublic void updateStock(Integer bookId) {String sql = "update t_book set stock=stock-1 where book_id=?";jdbcTemplate.update(sql,bookId);}@Overridepublic void updateUserBalance(Integer userId,Integer price) {String sql = "update t_user set balance=balance-? where user_id=?";jdbcTemplate.update(sql,price,userId);}
}
BookService
package com.itchen.spring.tx.service;public interface BookService {void buyBook(Integer bookId, Integer userId);
}
BookServiceImpl
package com.itchen.spring.tx.service;import com.itchen.spring.tx.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class BookServiceImpl implements BookService{@Autowiredprivate BookDao bookDao;@Overridepublic void buyBook(Integer bookId, Integer userId) {// 根據圖書id查詢圖書的價格Integer price = bookDao.getBookPriceByBookId(bookId);// 更新圖書表中的庫存量bookDao.updateStock(bookId);// 更新用戶余額bookDao.updateUserBalance(userId,price);}
}
TestBookTx
package com.itchen.spring.tx;import com.itchen.spring.tx.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;@SpringJUnitConfig(locations = "classpath:beans.xml")
public class TestBookTx {@Autowiredprivate BookController bookController;@Testpublic void testBuyBook(){bookController.buyBook(1,1);}
}
分析
將買書一個方法作為一個大的方法在Controller中調用。然后再Service將這個方法分解成多個數據庫操作。最后將數據庫的各個操作在dao層進行實現(xiàn)。(揉碎了喂到嘴里)
問題:這里如果用戶的余額不足,仍然會執(zhí)行圖書出庫操作,但是應為沒有事物的回滾,報錯的步驟不會執(zhí)行。
開啟事務步驟(使用注解的方式)
- 在bean中添加:開啟事務的注解驅動
- 在業(yè)務邏輯層(Service層)添加事務注解@Transactional**【這個注解可以加在方法上,也可以加在類上】**
運行結果:在用戶余額不足的情況下依然報錯,但是不會執(zhí)行圖書出庫操作
事務注解的一些屬性
參數 | 描述 |
---|---|
readOnly | 是否只讀(表示是否只能進行查詢操作) |
timeout | 設置超時時間(-1,表示永不超時;單位是秒) |
noRollbackFor | 事務策略(設置哪些異常不回滾) |
(見文章頭連接) | 隔離級別:解決臟讀、不可重讀、虛讀等 |
(見文章頭連接) | 傳播行為:事務之間的相互調用,使用哪一個事務進行邏輯處理 |
臟讀:兩個都未提交,但是都能看見對方的修改
不可重復:一個提交了,另一個修改了沒有提交的能看到修改完成之后的數據
虛讀:一個提交了,另一個做完了添加沒有提交之后能讀到自己添加的數據
Resoource資源操作
Resource接口提供了對低級資源訪問的抽象方法,接口有多個實現(xiàn)類供給給我們來訪問低級資源
UrlResource實現(xiàn)類訪問url資源
// 演示urlResource訪問網絡資源
public class UrlResourceDemo {public static void main(String[] args) {// http前綴loadUrlResource("https://www.baidu.com/");// file前綴 放在項目的根路徑loadUrlResource("file:itchen.txt");}// 訪問前綴httppublic static void loadUrlResource(String path){// 創(chuàng)建Resource接口的實現(xiàn)類的對象try {// 獲取資源UrlResource url = new UrlResource(path);System.out.println(url.getFilename());System.out.println(url.getURI());System.out.println(url.getDescription());System.out.println(url.getInputStream().read());} catch (Exception e) {throw new RuntimeException(e);}// 獲取資源信息}
}
ClassPathResourceDemo訪問本地資源(項目內資源)
// 訪問類路徑下的資源
public class ClassPathResourceDemo {public static void main(String[] args) {loadClasspathResource("itchen.txt");}public static void loadClasspathResource(String path){// 創(chuàng)建的對象ClassPathResource resource = new ClassPathResource(path);System.out.println(resource.getFilename());System.out.println(resource.getDescription());// 獲取文件內容try {InputStream in = resource.getInputStream();byte[] b = new byte[1024];while(in.read(b) != -1){System.out.println(new String(b));}} catch (IOException e) {throw new RuntimeException(e);}}
}
loadAndReadUrlResource訪問磁盤內文件
public class FileSystemResourceDemo {public static void loadAndReadUrlResource(String path) throws Exception{//相對路徑FileSystemResource resource = new FileSystemResource("itchen.txt");//絕對路徑//FileSystemResource resource = new FileSystemResource("D:\\itchen.txt");// 獲取文件名System.out.println("resource.getFileName = " + resource.getFilename());// 獲取文件描述System.out.println("resource.getDescription = "+ resource.getDescription());//獲取文件內容InputStream in = resource.getInputStream();byte[] b = new byte[1024];while(in.read(b)!=-1) {System.out.println(new String(b));}}public static void main(String[] args) throws Exception {loadAndReadUrlResource("itchen.txt");}
}
其他的實現(xiàn)類見文檔頭鏈接
國際化:i18n
調用不同的配置文件示例
@Test
public void test1(){ResourceBundle bundle = ResourceBundle.getBundle("itchen", new Locale("zh", "CN"));String value1 = bundle.getString("test");System.out.println(value1);
}
@Test
public void test2() {ResourceBundle bundle = ResourceBundle.getBundle("itchen", new Locale("en", "GB"));String value1 = bundle.getString("test");System.out.println(value1);
}
數據校驗
通過Validator接口實現(xiàn)數據校驗
public class TestMethod1 {public static void main(String[] args) {//創(chuàng)建person對象Person person = new Person();person.setName("lucy");person.setAge(-1);// 創(chuàng)建Person對應的DataBinderDataBinder binder = new DataBinder(person);// 設置校驗binder.setValidator(new PersonValidator());// 由于Person對象中的屬性為空,所以校驗不通過binder.validate();//輸出結果BindingResult results = binder.getBindingResult();System.out.println(results.getAllErrors());}
}
測試:
public class PersonValidator implements Validator {// 校驗類型確認@Overridepublic boolean supports(Class<?> clazz) {return Person.class.equals(clazz);}// 校驗邏輯@Overridepublic void validate(Object object, Errors errors) {ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");Person p = (Person) object;if (p.getAge() < 0) {errors.rejectValue("age", "error value < 0");} else if (p.getAge() > 110) {errors.rejectValue("age", "error value too old");}}
}
基于注解校驗
基于方法校驗
自定義校驗
其他的實現(xiàn)類見文檔頭鏈接
AOT
AOT先關概念
-
JIT:動態(tài)編譯,邊運行邊編譯
-
AOT:運行前編譯,提前編譯