網(wǎng)站建設(shè)論文 網(wǎng)站建設(shè)論文單頁(yè)網(wǎng)站排名優(yōu)化
文章目錄
- 一、引言
- 1.1 原生web開(kāi)發(fā)中存在哪些問(wèn)題?
- 二、Spring框架
- 2.1 概念
- 2.2 訪問(wèn)與下載
- 三、Spring架構(gòu)組成
- 四、山寨版的Spring容器
- 4.1準(zhǔn)備工作
- 4.2 山寨IOC容器
- 4.3 配置文件告訴容器 管理哪些bean
- 4.4 相關(guān)類(lèi)
- 4.5 測(cè)試 容器
- 五、構(gòu)建Maven項(xiàng)目
- 5.1 新建項(xiàng)目
- 5.2 選擇Maven目錄
- 5.3 GAV坐標(biāo)
- 六、Spring環(huán)境搭建
- 6.1 pom.xml中引入Spring常用依賴(lài)
- 6.2 創(chuàng)建Spring配置文件
- 七、Spring工廠編碼
- 八、依賴(lài)與配置文件詳解
- 8.1 Spring依賴(lài)關(guān)系
- 8.2 schema
- 九、IoC(Inversion of Control )控制反轉(zhuǎn)【`重點(diǎn)`】
- 9.1 項(xiàng)目中強(qiáng)耦合問(wèn)題
- 9.2 解決方案
- 9.3 IOC 接口
- 十、DI(Dependency Injection)依賴(lài)注入【`重點(diǎn)`】
- 10.1 概念
- 10.2 Set注入
- 10.2.1 定義目標(biāo)Bean類(lèi)型
- 10.2.2 基本類(lèi)型 + 字符串類(lèi)型 + 日期類(lèi)型
- 10.2.3 容器類(lèi)型
- 10.2.4 自建類(lèi)型
- 10.3 構(gòu)造注入【了解】
- 10.3.1 定義目標(biāo)Bean類(lèi)型
- 10.3.2 注入
- 10.4 自動(dòng)注入
- 十一、Bean細(xì)節(jié)
- 11.1 控·制簡(jiǎn)單對(duì)象的單例、多例模式 ----- bean的作用域
- 11.2 FactoryBean創(chuàng)建復(fù)雜對(duì)象
- 11.2.1 實(shí)現(xiàn)FactoryBean接口
- 11.2.2 配置spring-context.xml
- 11.2.3 特例
- 十二、Spring工廠特性
- 12.1 餓漢式創(chuàng)建優(yōu)勢(shì) --- 面試題 為什么spring bean 默認(rèn)是單例
- 12.2 生命周期方法 ---- 面試題 spring bean 的聲明周期是什么 非常重要
- 12.3 生命周期注解
- 12.4 生命周期階段
- 十三、代理設(shè)計(jì)模式
- 13.1 概念
- 13.2 靜態(tài)代理設(shè)計(jì)模式
- 13.3 動(dòng)態(tài)代理設(shè)計(jì)模式
- 13.3.1 JDK動(dòng)態(tài)代理實(shí)現(xiàn)(基于接口) 代理對(duì)象和真實(shí)對(duì)象的關(guān)系 像是兄弟 代理對(duì)象 對(duì)真實(shí)對(duì)象進(jìn)行增強(qiáng)
- 13.3.2 CGlib動(dòng)態(tài)代理實(shí)現(xiàn)(基于繼承) 代理對(duì)象和真實(shí)對(duì)象的關(guān)系 就像是 父子
- 十四、面向切面編程【`重點(diǎn)`】
- 14.1 概念
- 14.2 AOP開(kāi)發(fā)術(shù)語(yǔ)
- 14.3 作用
- 14.4 環(huán)境搭建
- 14.5 開(kāi)發(fā)流程
- 14.6 AOP小結(jié)
- 14.7 通知類(lèi)【可選】
- 14.8 通配切入點(diǎn)
- 14.9 JDK和CGLIB選擇
- 14.10 后處理器
- 14.10.1 后處理器定義
- 14.10.2 配置后處理器
- 14.10.3 bean生命周期
- 14.10.4 動(dòng)態(tài)代理源碼(了解)
- 十五、Spring + MyBatis【`重點(diǎn)`】
- 15.1 配置數(shù)據(jù)源
- 15.1.1 引入jdbc.properties配置文件
- 15.1.2 整合Spring配置文件和properties配置文件
- 15.1.3 Druid連接池可選參數(shù)
- 15.1.4 Druid監(jiān)控中心
- 15.1.5 測(cè)試監(jiān)控中心
- 15.2 整合MyBatis
- 15.2.1 導(dǎo)入依賴(lài)
- 15.2.2 配置SqlSessionFactory
- 15.2.3 配置MapperScannerConfigurer
- 15.2.4 配置Service
- 15.3 我的整合步驟 看我的這個(gè)步驟 上面的可以不看
- 十六、事務(wù)【`重點(diǎn)`】
- 16.1 配置DataSourceTransactionManager
- 16.3 事務(wù)屬性
- 16.3.1 隔離級(jí)別
- 16.3.1.1 概念
- 16.3.1.2 特性
- 16.3.1.3 并發(fā)問(wèn)題
- 16.3.2 傳播行為
- 16.3.3 讀寫(xiě)性
- 16.3.4 事務(wù)超時(shí)
- 16.3.5 事務(wù)回滾
- 16.4 編織
- 十七、注解開(kāi)發(fā)
- 17.1 聲明bean
- 17.2 注入(DI)
- 17.3 事務(wù)控制
- 17.4 注解所需配置
- 17.5 AOP開(kāi)發(fā)
- 17.5.1 注解使用
- 17.5.2 配置
- 十八、集成JUnit
- 18.1 導(dǎo)入依賴(lài)
- 18.2 編碼
- 十九、個(gè)人擴(kuò)展
- 二十 個(gè)人源碼解析
一、引言
1.1 原生web開(kāi)發(fā)中存在哪些問(wèn)題?
- 傳統(tǒng)Web開(kāi)發(fā)存在硬編碼所造成的過(guò)度程序耦合(例如:Service中作為屬性Dao對(duì)象)。
- UserDao userdao = new UserDaoImpl();
- 部分Java EE API較為復(fù)雜,使用效率低(例如:JDBC開(kāi)發(fā)步驟 ,Servlet的開(kāi)發(fā)步驟)。
- 侵入性強(qiáng),移植性差(例如:DAO實(shí)現(xiàn)的更換,從Connection到SqlSession)。
二、Spring框架
2.1 概念
Spring是一個(gè)項(xiàng)目管理框架,同時(shí)也是一套Java EE解決方案。
Spring是眾多優(yōu)秀設(shè)計(jì)模式的組合(工廠、單例、代理、適配器、包裝器、觀察者、模板、策略)。
Spring并未替代現(xiàn)有框架產(chǎn)品,而是將眾多框架進(jìn)行有機(jī)整合,簡(jiǎn)化企業(yè)級(jí)開(kāi)發(fā),俗稱(chēng)"膠水框架"。
2.2 訪問(wèn)與下載
官方網(wǎng)站:https://spring.io/
下載地址:http://repo.spring.io/release/org/springframework/spring/
三、Spring架構(gòu)組成
Spring架構(gòu)由諸多模塊組成,可分類(lèi)為
- 核心技術(shù):依賴(lài)注入,事件,資源,i18n,驗(yàn)證,數(shù)據(jù)綁定,類(lèi)型轉(zhuǎn)換,SpEL,AOP。
- 測(cè)試:模擬對(duì)象,TestContext框架,Spring MVC測(cè)試,WebTestClient。
- 數(shù)據(jù)訪問(wèn):事務(wù),DAO支持,JDBC,ORM,封送XML。
- Spring MVC和 Spring WebFlux Web框架。
- 集成:遠(yuǎn)程處理,JMS,JCA,JMX,電子郵件,任務(wù),調(diào)度,緩存。
- 語(yǔ)言:Kotlin,Groovy,動(dòng)態(tài)語(yǔ)言。
Spring架構(gòu)組成 |
---|
![]() |
GroupId | ArtifactId | 說(shuō)明 |
---|---|---|
org.springframework | spring-beans | Beans 支持,包含 Groovy |
org.springframework | spring-aop | 基于代理的AOP支持 |
org.springframework | spring-aspects | 基于AspectJ 的切面 |
org.springframework | spring-context | 應(yīng)用上下文運(yùn)行時(shí),包括調(diào)度和遠(yuǎn)程抽象 |
org.springframework | spring-context-support | 支持將常見(jiàn)的第三方類(lèi)庫(kù)集成到 Spring 應(yīng)用上下文 |
org.springframework | spring-core | 其他模塊所依賴(lài)的核心模塊 |
org.springframework | spring-expression | Spring 表達(dá)式語(yǔ)言,SpEL |
org.springframework | spring-instrument | JVM 引導(dǎo)的儀表(監(jiān)測(cè)器)代理 |
org.springframework | spring-instrument-tomcat | Tomcat 的儀表(監(jiān)測(cè)器)代理 |
org.springframework | spring-jdbc | 支持包括數(shù)據(jù)源設(shè)置和 JDBC 訪問(wèn)支持 |
org.springframework | spring-jms | 支持包括發(fā)送/接收J(rèn)MS消息的助手類(lèi) |
org.springframework | spring-messaging | 對(duì)消息架構(gòu)和協(xié)議的支持 |
org.springframework | spring-orm | 對(duì)象/關(guān)系映射,包括對(duì) JPA 和 Hibernate 的支持 |
org.springframework | spring-oxm | 對(duì)象/XML 映射(Object/XML Mapping,OXM) |
org.springframework | spring-test | 單元測(cè)試和集成測(cè)試支持組件 |
org.springframework | spring-tx | 事務(wù)基礎(chǔ)組件,包括對(duì) DAO 的支持及 JCA 的集成 |
org.springframework | spring-web | web支持包,包括客戶(hù)端及web遠(yuǎn)程調(diào)用 |
org.springframework | spring-webmvc | REST web 服務(wù)及 web 應(yīng)用的 MVC 實(shí)現(xiàn) |
org.springframework | spring-webmvc-portlet | 用于 Portlet 環(huán)境的MVC實(shí)現(xiàn) |
org.springframework | spring-websocket | WebSocket 和 SockJS 實(shí)現(xiàn),包括對(duì) STOMP 的支持 |
org.springframework | spring-jcl | Jakarta Commons Logging 日志系統(tǒng) |
四、山寨版的Spring容器
創(chuàng)建容器 ,從容器得到對(duì)象 ,而不是程序員自己new 對(duì)象,從而實(shí)現(xiàn) 對(duì)象間的解耦
4.1準(zhǔn)備工作
UserService
public interface UserService {public void addUser();
}
UserServiceImpl
@Overridepublic void addUser() {//使用 spring容器 之前的做法 是 在這里 new 一個(gè) dao 對(duì)象 程序員自己new//UserDao userDao = new UserJdbcDaoImpl();//現(xiàn)在 需求變了 要使用mybatis 操作數(shù)據(jù)庫(kù) 硬編碼 耦合度太高 我們希望 不改代碼 來(lái)改變底層的實(shí)現(xiàn)userDao = new UserMybatisDaoImpl();userDao.addUser();}
UserDao
public interface UserDao {void addUser();}
UserJdbcDaoImpl
public class UserJdbcDaoImpl implements UserDao {@Overridepublic void addUser() {System.out.println("使用jdbc來(lái)添加用戶(hù)");}
}
UserMybatisDaoImpl
public class UserMybatisDaoImpl implements UserDao {@Overridepublic void addUser() {System.out.println("使用mybatis來(lái)添加用戶(hù)");}
}
原來(lái)咱們的做法 沒(méi)有spring容器的時(shí)候
@Testpublic void test1(){//使用 spring 容器之前的 做法 自己 newUserService userService = new UserServiceImpl();userService.addUser();}
4.2 山寨IOC容器
引入spring 容器, 從容器中獲取對(duì)象
MyClassPathXmlApplicationContext
package com.glls.java2301.framework;import com.glls.java2301.dao.UserDao;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;/*** @date 2023/4/19* @desc 山寨版IOC容器 來(lái)模擬 真實(shí)的 基于XML的 spring 容器*/
public class MyClassPathXmlApplicationContext {//用于 存放 解析完 xml文件之后的 bean 的 信息, 以 beanId 為key 以BeanDefinition 為值private Map<String,BeanDefinition> beansMap = new HashMap<>();//這個(gè)map 用于存放 spring 容器 創(chuàng)建出來(lái)的對(duì)象 在容器里面創(chuàng)建出來(lái)的對(duì)象 叫 bean// 這個(gè)map 以 beanId 為 key 以反射創(chuàng)建出來(lái)的對(duì)象 為值private Map<String,Object> objectMap = new HashMap<>();public MyClassPathXmlApplicationContext() {}public MyClassPathXmlApplicationContext(String xmlPath) throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {//這個(gè)構(gòu)造方法 創(chuàng)建 容器對(duì)象 , 創(chuàng)建容器對(duì)象的時(shí)候 要把容器管理的bean 對(duì)象 也創(chuàng)建好//咱們就得告訴 Spring 容器 它所管理的bean對(duì)象 有哪些 ,通常 咱們會(huì)通過(guò) xml , 注解, java 配置類(lèi)//這些方式 告訴 spring 容器 管理的 bean 有哪些 ,這里 咱們 采用的方式 是 xml 的方式 告知spring 管理// 哪些bean 所以 現(xiàn)在 咱們要做的事情 就是 解析 xml 文件 ,得到 要?jiǎng)?chuàng)建的 bean 的 信息//1. 解析 xml 文件 得到 bean的信息 把bean 的信息 封裝到 BeanDefinition ,每個(gè)bean 都會(huì)有自己的//beanDefinition , 咱們把 很多 bean 的 beanDefinition 放入一個(gè) map 中parseXml(xmlPath);//2.從 beansMap 中 獲取bean 的信息 通過(guò)反射 創(chuàng)建對(duì)象 將創(chuàng)建出來(lái)的對(duì)象 放入 objectMapcreateObject();//3. di 依賴(lài)注入 第二部創(chuàng)建的對(duì)象的屬性時(shí)沒(méi)有值的di(); //dependency injection 屬性注入}//依賴(lài)注入 通過(guò)反射 調(diào)用對(duì)象的 set 方法 給對(duì)象的屬性賦值private void di() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {Set<Map.Entry<String, BeanDefinition>> entrySet = beansMap.entrySet();Iterator<Map.Entry<String, BeanDefinition>> iterator = entrySet.iterator();while (iterator.hasNext()){Map.Entry<String, BeanDefinition> beanDefinitionEntry = iterator.next();String beanId = beanDefinitionEntry.getKey();BeanDefinition beanDefinition = beanDefinitionEntry.getValue();//得到的bean 的property 集合Map<String, PropertyDefinition> propsMap = beanDefinition.getPropsMap();Set<Map.Entry<String, PropertyDefinition>> proSet = propsMap.entrySet();Iterator<Map.Entry<String, PropertyDefinition>> proIter = proSet.iterator();while (proIter.hasNext()){Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = proIter.next();//得到 property 的nameString proName = propertyDefinitionEntry.getKey();PropertyDefinition propertyDefinition = propertyDefinitionEntry.getValue();//ref 指向 屬性對(duì)應(yīng)的beanIdString ref = propertyDefinition.getRef();//根據(jù)屬性名 得到set方法 通過(guò)set方法給對(duì)象的屬性賦值//setUserDaoString setMethodName = "set" + proName.substring(0,1).toUpperCase()+proName.substring(1);//通過(guò)反射 得到方法對(duì)象String beanClass = beanDefinition.getBeanClass();Class<?> aClass = Class.forName(beanClass);Method[] declaredMethods = aClass.getDeclaredMethods();for(Method m:declaredMethods){//判斷對(duì)象中 有沒(méi)有這個(gè) set 方法if(m.getName().equals(setMethodName)){//表示 有這個(gè) set 方法//得到對(duì)象 給這個(gè)對(duì)象的屬性賦值 要反射調(diào)用這個(gè)對(duì)象的 set 方法Object o = objectMap.get(beanId);//得到 set 方法的參數(shù)Object para = objectMap.get(ref);//反射調(diào)用set 方法 給 對(duì)象o 的屬性賦值// userService.setUserDao(para);m.invoke(o,para);}}}}}//創(chuàng)建對(duì)象private void createObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException {//通過(guò)反射創(chuàng)建對(duì)象 要拿到類(lèi)的全路徑信息Set<Map.Entry<String, BeanDefinition>> entrySet = beansMap.entrySet();Iterator<Map.Entry<String, BeanDefinition>> iterator = entrySet.iterator();while (iterator.hasNext()){Map.Entry<String, BeanDefinition> bean = iterator.next();String beanId = bean.getKey();BeanDefinition beanDefinition = bean.getValue();//得到類(lèi)的全路徑名String beanClass = beanDefinition.getBeanClass();//通過(guò)反射創(chuàng)建對(duì)象Object o = Class.forName(beanClass).newInstance();//將創(chuàng)建好的對(duì)象 存入 obejctMapobjectMap.put(beanId,o);}}private void parseXml(String xmlPath) throws DocumentException {//讀取配置文件 得到流對(duì)象InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(xmlPath);//使用dom4j的apiSAXReader saxReader = new SAXReader();//得到文檔對(duì)象Document doc = saxReader.read(resourceAsStream);//得到根節(jié)點(diǎn)Element rootElement = doc.getRootElement();//遍歷根節(jié)點(diǎn)Iterator<Element> iterator = rootElement.elementIterator();while (iterator.hasNext()){Element bean = iterator.next();String beanId = bean.attributeValue("id");String beanClass = bean.attributeValue("class");//System.out.println(beanId+"---"+beanClass);//把解析出來(lái)的信息 存入 BeanDefinition 中BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setBeanId(beanId);beanDefinition.setBeanClass(beanClass);//遍歷bean 標(biāo)簽的子標(biāo)簽Iterator<Element> proIterator = bean.elementIterator();while (proIterator.hasNext()){Element pro = proIterator.next();String proName = pro.attributeValue("name");String proRef = pro.attributeValue("ref");//將bean 的 子節(jié)點(diǎn) 屬性信息 封裝到 PropertyDefinition 中PropertyDefinition propertyDefinition = new PropertyDefinition();propertyDefinition.setName(proName);propertyDefinition.setRef(proRef);//將屬性信息 封裝到 BeanDefinition 中的 propsMapMap<String, PropertyDefinition> propsMap = beanDefinition.getPropsMap();propsMap.put(proName,propertyDefinition);}//將這個(gè) bean 的定義信息 封裝到 beansMapbeansMap.put(beanId,beanDefinition);}}//從容器拿對(duì)象的方法public Object getBean(String beanName) {return objectMap.get(beanName);}
}
4.3 配置文件告訴容器 管理哪些bean
<beans><bean id="userJdbcDao" class="com.glls.java2301.dao.impl.UserJdbcDaoImpl" ></bean><bean id="userMybatisDao" class="com.glls.java2301.dao.impl.UserMybatisDaoImpl"></bean><bean id="userService" class="com.glls.java2301.service.impl.UserServiceImpl">
<!-- 以后需求變了 不想使用jdbc 來(lái)訪問(wèn)數(shù)據(jù)庫(kù)了 ,想使用mybatis 那么僅僅只需要改一下下面的ref 就可以了--><property name="userDao" ref="userMybatisDao"></property></bean></beans>
4.4 相關(guān)類(lèi)
BeanDefinition
package com.glls.java2301.framework;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.HashMap;
import java.util.Map;/*** @date 2023/4/19* @desc bean 的定義信息 來(lái)封裝bean 的 信息*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BeanDefinition {//解析xml 解析出來(lái)的數(shù)據(jù) 封裝到這個(gè)類(lèi)的對(duì)象中private String beanId; // bean 的 名字 bean 在容器中的標(biāo)識(shí)private String beanClass; // 封裝bean 的 全路徑private Map<String,PropertyDefinition> propsMap = new HashMap<>();}
PropertyDefinition
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PropertyDefinition {//property 節(jié)點(diǎn)的 name 屬性值private String name;//property 節(jié)點(diǎn)的 ref 屬性值private String ref;}
4.5 測(cè)試 容器
@Testpublic void test2() throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {//山寨版的IOC容器// 由容器 來(lái) 創(chuàng)建 管理 對(duì)象// 創(chuàng)建咱們 的IOC 容器, 從容器中 獲取 對(duì)象 ,這里 咱們模擬的情況是//容器 創(chuàng)建好了 里面的bean 也創(chuàng)建好了 , 可以直接用了String config= "applicationContext.xml";//創(chuàng)建容器對(duì)象 告知容器對(duì)象掃描的 xml 配置文件 ,xml 文件中 記錄了 spring 容器要管理的 bean的信息MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext(config);//UserDao userDao = (UserDao) applicationContext.getBean("userJdbcDao");//UserDao userDao2 = (UserDao) applicationContext.getBean("userMybatisDao");UserService userService = (UserService) applicationContext.getBean("userService");//userDao.addUser();//userDao2.addUser();userService.addUser();}
手寫(xiě) ioc 容器 – 基于 xml 版本的 目的 理解 ioc 容器
詳見(jiàn) springday1 案例
后面有時(shí)間 再帶大家寫(xiě)一個(gè) 基于注解版本的 理解 bean的生命周期
五、構(gòu)建Maven項(xiàng)目
5.1 新建項(xiàng)目
使用IDEA打開(kāi)已創(chuàng)建的文件夾目錄 |
---|
![]() |
5.2 選擇Maven目錄
選擇Maven項(xiàng)目 |
---|
![]() |
5.3 GAV坐標(biāo)
GAV坐標(biāo) |
---|
![]() |
六、Spring環(huán)境搭建
6.1 pom.xml中引入Spring常用依賴(lài)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>hello-spring</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- Spring常用依賴(lài) --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.6.RELEASE</version></dependency></dependencies>
</project>
6.2 創(chuàng)建Spring配置文件
命名無(wú)限制,約定俗成命名有:spring-context.xml、applicationContext.xml、beans.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>
七、Spring工廠編碼
定義目標(biāo)Bean類(lèi)型
public class MyClass{public void show(){System.out.println("HelloWorld");}
}
spring-context.xml中的< beans >內(nèi)部配置bean標(biāo)簽
<!-- 在 spring 配置文件中,使用 bean 標(biāo)簽,標(biāo)簽里面添加對(duì)應(yīng)屬性,就可以實(shí)現(xiàn)對(duì)象創(chuàng)建, 配置實(shí)例(id:“唯一標(biāo)識(shí)” class="需要被創(chuàng)建的目標(biāo)對(duì)象全限定名") 創(chuàng)建對(duì)象時(shí)候,默認(rèn)也是執(zhí)行無(wú)參數(shù)構(gòu)造方法完成對(duì)象創(chuàng)建-->
<bean id="mc" class="com.qf.spring.part1.factory.MyClass" />
調(diào)用Spring工廠API(ApplicationContext接口)
public class TestFactory{/*** 程序中的對(duì)象都交由Spring的ApplicationContext工廠進(jìn)行創(chuàng)建。*/public static void main(String[] args){//1. 讀取配置文件中所需創(chuàng)建的bean對(duì)象,并獲得工廠對(duì)象ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");//2. 通過(guò)id獲取bean對(duì)象MyClass mc = (MyClass) ctx.getBean("mc");//3. 使用對(duì)象mc.show();}
}
八、依賴(lài)與配置文件詳解
Spring框架包含多個(gè)模塊,每個(gè)模塊各司其職,可結(jié)合需求引入相關(guān)依賴(lài)Jar包實(shí)現(xiàn)功能。
8.1 Spring依賴(lài)關(guān)系
Spring常用功能的Jar包依賴(lài)關(guān)系 |
---|
![]() |
- 注意:Jar包彼此存在依賴(lài),只需引入最外層Jar即可由Maven自動(dòng)將相關(guān)依賴(lài)Jar引入到項(xiàng)目中。
8.2 schema
配置文件中的頂級(jí)標(biāo)簽中包含了語(yǔ)義化標(biāo)簽的相關(guān)信息
- xmlns:語(yǔ)義化標(biāo)簽所在的命名空間。
- xmlns:xsi:XMLSchema-instance 標(biāo)簽遵循Schema標(biāo)簽標(biāo)準(zhǔn)。
- xsi:schemaLocation:xsd文件位置,用以描述標(biāo)簽語(yǔ)義、屬性、取值范圍等。
九、IoC(Inversion of Control )控制反轉(zhuǎn)【重點(diǎn)
】
Inverse Of Controll:控制反轉(zhuǎn)
反轉(zhuǎn)了依賴(lài)關(guān)系的滿(mǎn)足方式,由之前的自己創(chuàng)建依賴(lài)對(duì)象,變?yōu)橛晒S推送。(變主動(dòng)為被動(dòng),即反轉(zhuǎn))
解決了具有依賴(lài)關(guān)系的組件之間的強(qiáng)耦合,使得項(xiàng)目形態(tài)更加穩(wěn)健
補(bǔ)充: 什么是IOC
1.控制反轉(zhuǎn),把對(duì)象創(chuàng)建和對(duì)象之間的調(diào)用過(guò)程,交給 Spring 進(jìn)行管理
2.使用 IOC 目的:為了耦合度降低
3.入門(mén)案例就是 IOC 實(shí)現(xiàn)
IOC 底層原理
xml 解析、工廠模式、反射
9.1 項(xiàng)目中強(qiáng)耦合問(wèn)題
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService {// !!!強(qiáng)耦合了UserDAOImpl!!!,使得UserServiceImpl變得不穩(wěn)健!!private UserDAO userDAO= new UserDAOImpl();@Overridepublic User queryUser() {return userDAO.queryUser();}....
}
9.2 解決方案
// 不引用任何一個(gè)具體的組件(實(shí)現(xiàn)類(lèi)),在需要其他組件的位置預(yù)留存取值入口(set/get)
public class UserServiceImpl implements UserService {// !!!不再耦合任何DAO實(shí)現(xiàn)!!!,消除不穩(wěn)健因素!!private UserDAO userDAO;// 為userDAO定義set/get,允許userDAO屬性接收spring賦值//Getters And Setters@Overridepublic User queryUser() {return userDAO.queryUser();}....
}
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean>
<!-- UserServiceImpl組件 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"><!-- 由spring為userDAO屬性賦值,值為id="userDAO"的bean --><property name="userDAO" ref="userDAO"/>
</bean>
此時(shí),如果需要更換其他UserDAO實(shí)現(xiàn)類(lèi),則UserServiceImpl不用任何改動(dòng)!
則此時(shí)的UserServiceImpl組件變得更加穩(wěn)健!
9.3 IOC 接口
1.IOC 思想基于 IOC 容器完成,IOC 容器底層就是對(duì)象工廠
2.Spring 提供 IOC 容器實(shí)現(xiàn)兩種方式:(兩個(gè)接口)
-
BeanFactory:IOC 容器基本實(shí)現(xiàn),是 Spring 內(nèi)部的使用接口,不提供開(kāi)發(fā)人員進(jìn)行使用
- 加載配置文件時(shí)候不會(huì)創(chuàng)建對(duì)象,在獲取對(duì)象(使用)才去創(chuàng)建對(duì)象
-
ApplicationContext:BeanFactory 接口的子接口,提供更多更強(qiáng)大的功能,一般由開(kāi)發(fā)人 員進(jìn)行使用
- 加載配置文件時(shí)候就會(huì)把在配置文件對(duì)象進(jìn)行創(chuàng)建
3.IOC操作bean管理
Bean的管理指的是兩個(gè)操作:1.Spring創(chuàng)建對(duì)象 2.Spring注入屬性
Bean的常見(jiàn)的管理操作有兩種方式:1.基于 xml 配置文件方式實(shí)現(xiàn) 2.基于注解方式實(shí)現(xiàn)
十、DI(Dependency Injection)依賴(lài)注入【重點(diǎn)
】
10.1 概念
在Spring創(chuàng)建對(duì)象的同時(shí),為其屬性賦值,稱(chēng)之為依賴(lài)注入。
10.2 Set注入
創(chuàng)建對(duì)象時(shí),Spring工廠會(huì)通過(guò)Set方法為對(duì)象的屬性賦值。
10.2.1 定義目標(biāo)Bean類(lèi)型
public class User {private Integer id;private String password;private String sex;private Integer age;private Date bornDate;private String[] hobbys;private Set<String> phones;private List<String> names;private Map<String,String> countries;private Properties files;//Getters And Setters
}
10.2.2 基本類(lèi)型 + 字符串類(lèi)型 + 日期類(lèi)型
<bean id="u1" class="com.qf.spring.part1.injection.User"><!--base field--><property name="id" value="1001" /><property name="password" value="123456" /><property name="sex" value="male" /><property name="age" value="20" /><property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>
10.2.3 容器類(lèi)型
<bean id="u1" class="com.qf.spring.part1.injection.User"> <!--Array--><property name="hobbys"><array><value>Run</value><value>Swim</value><value>Climb</value></array></property><!--Set--><property name="phones"><set><value>13777777777</value><value>13888888888</value><value>13999999999</value></set></property><!--List--><property name="names"><list><value>tom</value><value>jack</value><value>marry</value></list></property><!--Map--><property name="countries"><map><entry key="CN" value="China" /><entry key="US" value="America" /><entry key="KR" value="Korea" /></map></property><!--Properties--><property name="files"><props><prop key="first">One</prop><prop key="second">Two</prop><prop key="third">Three</prop></props></property>
</bean>
10.2.4 自建類(lèi)型
<!--次要bean,被作為屬性-->
<bean id="addr" class="com.qf.spring.part1.injection.Address"><property name="position" value="北京市海淀區(qū)" /><property name="zipCode" value="100001" />
</bean><!--主要bean,操作的主體-->
<bean id="u2" class="com.qf.spring.part1.injection.User"><property name="address" ref="addr" /><!--address屬性引用addr對(duì)象-->
</bean>
<!--次要bean,被作為屬性-->
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" /><!--主要bean,操作的主體-->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"><property name="ud" ref="userDao" /><!--ud屬性引用userDao對(duì)象-->
</bean>
10.3 構(gòu)造注入【了解】
創(chuàng)建對(duì)象時(shí),Spring工廠會(huì)通過(guò)構(gòu)造方法為對(duì)象的屬性賦值。
10.3.1 定義目標(biāo)Bean類(lèi)型
public class Student {private Integer id;private String name;private String sex;private Integer age;//Constructorspublic Student(Integer id , String name , String sex , Integer age){this.id = id;this.name = name;this.sex = sex;this.age = age;}
}
10.3.2 注入
<!--構(gòu)造注入-->
<bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student"><constructor-arg name="id" value="1234" /> <!-- 除標(biāo)簽名稱(chēng)有變化,其他均和Set注入一致 --><constructor-arg name="name" value="tom" /><constructor-arg name="age" value="20" /><constructor-arg name="sex" value="male" />
</bean>
10.4 自動(dòng)注入
不用在配置中 指定為哪個(gè)屬性賦值,及賦什么值.
由spring自動(dòng)根據(jù)某個(gè) “原則” ,在工廠中查找一個(gè)bean,為屬性注入屬性值
也叫自動(dòng)裝配 根據(jù)裝配規(guī)則 (屬性名和屬性類(lèi)型) spring 自動(dòng)將匹配的屬性值進(jìn)行注入
演示過(guò)程
1.根據(jù)屬性名稱(chēng)自動(dòng)注入在 bean 標(biāo)簽 使用 autowire 配置自動(dòng)裝配,autowire 屬性 常用兩個(gè)值:byName 根據(jù)屬性名稱(chēng)注入 注入值的 bean 的 id 和 類(lèi)的屬性名一樣byType 根據(jù)屬性類(lèi)型注入 如果 容器內(nèi) 有多個(gè)這個(gè)類(lèi)型的bean 那么 容器如果不知道注入哪一個(gè) 就會(huì)報(bào)錯(cuò)
public class UserServiceImpl implements UserService {private UserDAO userDAO;//Getters And Setters....
}
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 為UserServiceImpl中的屬性基于類(lèi)型自動(dòng)注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 為UserServiceImpl中的屬性基于名字自動(dòng)注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byName"></bean>
十一、Bean細(xì)節(jié)
11.1 控·制簡(jiǎn)單對(duì)象的單例、多例模式 ----- bean的作用域
在spring 容器里 設(shè)置 bean 實(shí)例 是 單例 還是 多例 , 默認(rèn)情況下 bean 是 單例的
配置< bean scope=“singleton | prototype” />
<!--singleton(默認(rèn)):每次調(diào)用工廠,得到的都是同一個(gè)對(duì)象。 加載 spring 配置文件時(shí)候就會(huì)創(chuàng)建單實(shí)例對(duì)象prototype:每次調(diào)用工廠,都會(huì)創(chuàng)建新的對(duì)象。 不是在加載 spring 配置文件時(shí)候創(chuàng)建 對(duì)象,在調(diào)用
getBean 方法時(shí)候創(chuàng)建多實(shí)例對(duì)象-->
<bean id="mc" class="com.qf.zcg.spring.day1.t1.basic.MyClass" scope="singleton" />
- 注意:需要根據(jù)場(chǎng)景決定對(duì)象的單例、多例模式。
- 可以共用:Service、DAO、SqlSessionFactory(或者是所有的工廠)。
- 不可共用:Connection、SqlSession、ShoppingCart。
11.2 FactoryBean創(chuàng)建復(fù)雜對(duì)象
注意 和 前面的 BeanFactory 的區(qū)別
補(bǔ)充:spring有兩種類(lèi)型的bean ,一種是 普通bean ,另外一種是 工廠bean (FactoryBean)
普通的bean : 在配置文件中定義的bean的類(lèi)型 就是返回類(lèi)型
工廠bean 在配置文件中定義的bean類(lèi)型可以和返回的bean類(lèi)型不一樣
作用:讓Spring可以創(chuàng)建復(fù)雜對(duì)象、或者無(wú)法直接通過(guò)反射創(chuàng)建的對(duì)象。
FactoryBean解決復(fù)雜對(duì)象創(chuàng)建 |
---|
![]() |
11.2.1 實(shí)現(xiàn)FactoryBean接口
接口方法描述 |
---|
![]() |
- 注意:isSingleton方法的返回值,需根據(jù)所創(chuàng)建對(duì)象的特點(diǎn)決定返回true/false。
- 例如:Connection 不應(yīng)該被多個(gè)用戶(hù)共享,返回false。
- 例如:SqlSessionFactory 重量級(jí)資源,不該過(guò)多創(chuàng)建,返回true。
11.2.2 配置spring-context.xml
配置與獲取方式 |
---|
![]() |
11.2.3 特例
獲取FactoryBean接口的實(shí)現(xiàn)類(lèi)對(duì)象,而非getObject()所生產(chǎn)的對(duì)象。 |
---|
![]() |
十二、Spring工廠特性
12.1 餓漢式創(chuàng)建優(yōu)勢(shì) — 面試題 為什么spring bean 默認(rèn)是單例
工廠創(chuàng)建之后,會(huì)將Spring配置文件中的所有對(duì)象都創(chuàng)建完成(餓漢式)。
提高程序運(yùn)行效率。避免多次IO,減少對(duì)象創(chuàng)建時(shí)間。(概念接近連接池,一次性創(chuàng)建好,使用時(shí)直接獲取)
如果默認(rèn)是多例bean 首先創(chuàng)建多例bean 會(huì)消耗資源 還會(huì)占用堆空間
12.2 生命周期方法 ---- 面試題 spring bean 的聲明周期是什么 非常重要
什么生命周期?
bean 對(duì)象從創(chuàng)建到銷(xiāo)毀的過(guò)程
步驟
- bean 的實(shí)例化 通過(guò)構(gòu)造方法創(chuàng)建bean 的實(shí)例 默認(rèn)是無(wú)參構(gòu)造
- 給bean 的屬性賦值 調(diào)用 set 方法
- 執(zhí)行初始化的方法 需要配置初始化方法
- 得到完整的bean 對(duì)象 ,這時(shí)的bean 對(duì)象才能夠使用
- 銷(xiāo)毀bean 需要配置 銷(xiāo)毀的方法
要考慮 bean 的后置處理器 BeanPostProcessor
創(chuàng)建一個(gè)類(lèi)實(shí)現(xiàn)BeanPostProcessor 重寫(xiě) 他的兩個(gè)方法
public class MyBeanPost implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("在bean 初始化之前執(zhí)行");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("在bean初始化之后執(zhí)行");if(bean instanceof User){User user = (User) bean;user.setName("貂蟬");return user;}return bean;}
}
在配置文件 配置這個(gè) bean
<bean id="myPostProcessor" class="com.qf.postprocessor.MyBeanPost"></bean>
總結(jié): 面試答案
- bean 的實(shí)例化 通過(guò)構(gòu)造方法創(chuàng)建bean 的實(shí)例 默認(rèn)是無(wú)參構(gòu)造
- 給bean 的屬性賦值
- 把 bean 的 實(shí)例 傳遞給 bean的后置處理器的方法 postProcessBeforeInitialization
- 執(zhí)行初始化的方法
- 把 bean 的 實(shí)例 傳遞給 bean的后置處理器的方法 postProcessAfterInitialization
- 得到完整的bean 對(duì)象 ,這時(shí)的bean 對(duì)象才能夠使用
- 銷(xiāo)毀bean 當(dāng)容器關(guān)閉的時(shí)候 調(diào)用銷(xiāo)毀的方法
自定義初始化方法:添加“init-method”屬性,Spring則會(huì)在創(chuàng)建對(duì)象之后,調(diào)用此方法。
自定義銷(xiāo)毀方法:添加“destroy-method”屬性,Spring則會(huì)在銷(xiāo)毀對(duì)象之前,調(diào)用此方法。
銷(xiāo)毀:工廠的close()方法被調(diào)用之后,Spring會(huì)毀掉所有已創(chuàng)建的單例對(duì)象。
分類(lèi):Singleton對(duì)象由Spring容器銷(xiāo)毀、Prototype對(duì)象由JVM銷(xiāo)毀。
12.3 生命周期注解
初始化注解、銷(xiāo)毀注解
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@PostConstruct //初始化
public void init(){System.out.println("init method executed");
}@PreDestroy //銷(xiāo)毀
public void destroy(){System.out.println("destroy method executed");
}
12.4 生命周期階段
**單例bean:**singleton
隨工廠啟動(dòng)創(chuàng)建 ==》 構(gòu)造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 構(gòu)建完成 ==》隨工廠關(guān)閉銷(xiāo)毀
**多例bean:**prototype
被使用時(shí)創(chuàng)建 ==》 構(gòu)造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 構(gòu)建完成 ==》JVM垃圾回收銷(xiāo)毀
十三、代理設(shè)計(jì)模式
13.1 概念
將核心功能與輔助功能(事務(wù)、日志、性能監(jiān)控代碼)分離,達(dá)到核心業(yè)務(wù)功能更純粹、輔助業(yè)務(wù)功能可復(fù)用。
功能分離 |
---|
![]() |
13.2 靜態(tài)代理設(shè)計(jì)模式
通過(guò)代理類(lèi)的對(duì)象,為原始類(lèi)的對(duì)象(目標(biāo)類(lèi)的對(duì)象)添加輔助功能,更容易更換代理實(shí)現(xiàn)類(lèi)、利于維護(hù)。
靜態(tài)代理 |
---|
![]() |
- 代理類(lèi) = 實(shí)現(xiàn)原始類(lèi)相同接口 + 添加輔助功能 + 調(diào)用原始類(lèi)的業(yè)務(wù)方法。
- 靜態(tài)代理的問(wèn)題
- 代理類(lèi)數(shù)量過(guò)多,不利于項(xiàng)目的管理。
- 多個(gè)代理類(lèi)的輔助功能代碼冗余,修改時(shí),維護(hù)性差。
13.3 動(dòng)態(tài)代理設(shè)計(jì)模式
動(dòng)態(tài)創(chuàng)建代理類(lèi)的對(duì)象,為原始類(lèi)的對(duì)象添加輔助功能。
13.3.1 JDK動(dòng)態(tài)代理實(shí)現(xiàn)(基于接口) 代理對(duì)象和真實(shí)對(duì)象的關(guān)系 像是兄弟 代理對(duì)象 對(duì)真實(shí)對(duì)象進(jìn)行增強(qiáng)
//目標(biāo)
final OrderService os = new OrderServiceImpl();
//額外功能
InvocationHandler handler = new InvocationHandler(){//1.設(shè)置回調(diào)函數(shù)(額外功能代碼)@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("start...");method.invoke(os, args);System.out.println("end...");return null;}
};
//2.創(chuàng)建動(dòng)態(tài)代理類(lèi)
Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);
個(gè)人代碼
接口
public interface Marry {public void marry();public int money();}
真實(shí)對(duì)象
public class You implements Marry {@Overridepublic void marry() {System.out.println("終身大事");}@Overridepublic int money() {int num = 10;System.out.println("花了"+num+"塊錢(qián)");return num;}
}
封裝的一個(gè)工具類(lèi)
public class JdkProxy implements InvocationHandler {private Object target; //真實(shí)對(duì)象 被代理的對(duì)象//把真實(shí)對(duì)象傳進(jìn)來(lái) 返回代理對(duì)象public Object getProxy(Object target){this.target = target;return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}//對(duì)真實(shí)對(duì)象的功能增強(qiáng)@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("準(zhǔn)備工作");Object result = method.invoke(target, args);System.out.println("收尾工作");if(target instanceof Marry){return 100;}return result;}
}
測(cè)試
public class TestJdkProxy {public static void main(String[] args) {//真實(shí)對(duì)象You you = new You();JdkProxy jdkProxy = new JdkProxy();//得到代理對(duì)象Marry proxy = (Marry) jdkProxy.getProxy(you);//調(diào)用代理對(duì)象的方法proxy.marry();int money = proxy.money();System.out.println(money);}
}
13.3.2 CGlib動(dòng)態(tài)代理實(shí)現(xiàn)(基于繼承) 代理對(duì)象和真實(shí)對(duì)象的關(guān)系 就像是 父子
final OrderService os = new OrderServiceImpl();
Enhancer cnh = new Enhancer();//1.創(chuàng)建字節(jié)碼曾強(qiáng)對(duì)象
enh.setSuperclass(os.getClass());//2.設(shè)置父類(lèi)(等價(jià)于實(shí)現(xiàn)原始類(lèi)接口)
enh.setCallback(new InvocationHandler(){//3.設(shè)置回調(diào)函數(shù)(額外功能代碼)@Overridepublic Object invoke(Object proxy , Method method, Object[] args) throws Throwable{System.out.println("start...");Object ret = method.invoke(os,args);System.out.println("end...");return ret;}
});
OrderService proxy = (OrderService)enh.create();//4.創(chuàng)建動(dòng)態(tài)代理類(lèi)
proxy,createOrder();
個(gè)人代碼
真實(shí)對(duì)象
public class You {public void marry() {System.out.println("終身大事");}public int money() {int num = 10;System.out.println("花了"+num+"塊錢(qián)");return num;}}
工具類(lèi) 封裝得到代理對(duì)象的方法 和 回調(diào)方法
package com.qf.proxy.cglibproxy;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;import java.lang.reflect.Method;public class CglibProxy implements InvocationHandler {//真實(shí)對(duì)象private Object target;//傳入真實(shí)對(duì)象 得到代理對(duì)象public Object getProxy(Object target){this.target = target;Enhancer enhancer = new Enhancer();//1.創(chuàng)建字節(jié)碼曾強(qiáng)對(duì)象enhancer.setSuperclass(target.getClass());//2.設(shè)置父類(lèi)(等價(jià)于實(shí)現(xiàn)原始類(lèi)接口)//設(shè)置回調(diào)函數(shù)enhancer.setCallback(this);//返回代理對(duì)象return enhancer.create();}//真實(shí)對(duì)象方法增強(qiáng)@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {System.out.println("準(zhǔn)備工作");Object result = method.invoke(target, objects);System.out.println("收尾工作");return result;}
}
測(cè)試
package com.qf.proxy.cglibproxy;public class TestCglibProxy {public static void main(String[] args) {//真實(shí)對(duì)象You you = new You();//工具類(lèi)對(duì)象CglibProxy cglibProxy = new CglibProxy();//得到代理對(duì)象You proxy = (You) cglibProxy.getProxy(you);proxy.marry();}
}
十四、面向切面編程【重點(diǎn)
】
14.1 概念
AOP(Aspect Oriented Programming),即面向切面編程,利用一種稱(chēng)為"橫切"的技術(shù),剖開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,并將其命名為"Aspect",即切面。所謂"切面",簡(jiǎn)單說(shuō)就是那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊之間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
什么是AOP ?
1.面向切面編程 利用AOP 可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離 從而使業(yè)務(wù)邏輯的各部分之間 耦合度降低,提高程序的可重用性,提好了開(kāi)發(fā)效率,通俗的講 可以實(shí)現(xiàn)不修改源代碼的方式,在核心業(yè)務(wù)里面 添加新的功能
AOP 底層的原理 就是 動(dòng)態(tài)代理 ,真正干活的 bean 是 代理bean , 代理bean 對(duì)真實(shí)bean 功能增強(qiáng)
14.2 AOP開(kāi)發(fā)術(shù)語(yǔ)
- 連接點(diǎn)(Joinpoint):連接點(diǎn)是程序類(lèi)中客觀存在的方法,可被Spring攔截并切入內(nèi)容。
- 說(shuō)白了 類(lèi)中的哪些方法 可以被增強(qiáng) 這些方法 就稱(chēng)為是 連接點(diǎn)
- 切入點(diǎn)(Pointcut):被Spring切入連接點(diǎn)。
- 真正被增強(qiáng)的方法 稱(chēng)為 切入點(diǎn)
- 通知、增強(qiáng)(Advice):可以為切入點(diǎn)添加額外功能,分為:前置通知、后置通知、異常通知、環(huán)繞通知,最終通知等。
- 實(shí)際增強(qiáng)的邏輯部分 稱(chēng)為通知(增強(qiáng))
- 目標(biāo)對(duì)象(Target):代理的目標(biāo)對(duì)象 真實(shí)對(duì)象
- 引介(Introduction):一種特殊的增強(qiáng),可在運(yùn)行期為類(lèi)動(dòng)態(tài)添加Field和Method。
- 織入(Weaving):把通知應(yīng)用到具體的類(lèi),進(jìn)而創(chuàng)建新的代理類(lèi)的過(guò)程。
- 代理(Proxy):被AOP織入通知后,產(chǎn)生的結(jié)果類(lèi)。
- 切面(Aspect):由切點(diǎn)和通知組成,將橫切邏輯織入切面所指定的連接點(diǎn)中。是一個(gè)動(dòng)作 把通知 應(yīng)用到 切入點(diǎn)的過(guò)程
14.3 作用
Spring的AOP編程即是通過(guò)動(dòng)態(tài)代理類(lèi)為原始類(lèi)的方法添加輔助功能。
14.4 環(huán)境搭建
引入AOP相關(guān)依賴(lài)
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.1.6.RELEASE</version>
</dependency>
spring-context.xml引入AOP命名空間
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>
14.5 開(kāi)發(fā)流程
定義原始類(lèi)
package com.qf.aaron.aop.basic;public interface UserService {public void save();
}
package com.qf.aaron.aop.basic;public class UserServiceImpl implements UserService {public void save() {System.out.println("save method executed...");}
}
下面是 基于 Schema-based 的 aop實(shí)現(xiàn)方式 得認(rèn)識(shí)
四種通知
MyBeforeAdvice 前置通知
AfterReturningAdvice 后置返回通知
注意 Schema-based 沒(méi)有最終通知
MethodInterceptor 環(huán)繞通知
ThrowsAdvice 異常通知
定義通知類(lèi)(添加額外功能)
package com.qf.aaron.aop.basic;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;public class MyAdvice implements MethodBeforeAdvice { //實(shí)現(xiàn)前置通知接口public void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("before advice executed...");}
}
定義bean標(biāo)簽
<!--原始對(duì)象-->
<bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /><!--輔助對(duì)象-->
<bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />
定義切入點(diǎn)(PointCut)
形成切面(Aspect)
<aop:config><!--切點(diǎn)--><aop:pointcut id="myPointCut" expression="execution(* save())" />
</aop:config>
<aop:config><!--組裝切面 --><aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" />
</aop:config>
下面是基于 AspectJ 的 AOP 實(shí)現(xiàn)方式
方式1 基于 xml 的配置
創(chuàng)建 通知類(lèi)
package com.qf.aspectjadvice;import org.aspectj.lang.ProceedingJoinPoint;/*** @ClassName : MyAspectJAdvice* @Description : AspectJ 的 通知類(lèi) 無(wú)需實(shí)現(xiàn)接口*/
public class MyAspectJAdvice {public void mybefore(){System.out.println("aspectj的前置通知");}public void myafter(){System.out.println("aspectj的后置通知");}public Object myaround(ProceedingJoinPoint p) throws Throwable {System.out.println("環(huán)繞前置");Object result = p.proceed();System.out.println("環(huán)繞后置");return result;}
}
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"xmlns:aop="http://www.springframework.org/schema/aop"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/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--基于注解的形式 管理 bean--><!--掃描 com.qf 下的全部組件--><context:component-scan base-package="com.qf"></context:component-scan><!--了解 基于 AspectJ 的 xml 的 AOP實(shí)現(xiàn) 配置--><bean id="myaspectj" class="com.qf.aspectjadvice.MyAspectJAdvice"></bean><aop:config><aop:aspect ref="myaspectj"><aop:pointcut id="aspectjpoint1" expression="execution(* save(..))"/></aop:aspect><aop:aspect ref="myaspectj"><aop:pointcut id="aspectjpoint2" expression="execution(* com.qf.service.impl.OrderServiceImpl.save(..))"/></aop:aspect><aop:aspect ref="myaspectj"><aop:before method="mybefore" pointcut-ref="aspectjpoint1"></aop:before><aop:after method="myafter" pointcut-ref="aspectjpoint1"></aop:after><aop:around method="myaround" pointcut-ref="aspectjpoint2"></aop:around></aop:aspect></aop:config></beans>
測(cè)試:
@Testpublic void test2(){// 基于 Aspectj 的 xml 的 AOP 了解 看得懂ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans2.xml");UserService userService = applicationContext.getBean("userService", UserService.class);OrderService orderService = applicationContext.getBean("orderService", OrderService.class);userService.save();//userService.delete(1);Order order = new Order(1, "訂單描述");//orderService.save(order);}
方式2 aspectj 基于 注解的aop 實(shí)現(xiàn) 要求掌握
- 配置文件
<?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:aop="http://www.springframework.org/schema/aop"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/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--基于注解的形式 管理 bean--><!--掃描 com.qf 下的全部組件--><context:component-scan base-package="com.qf"></context:component-scan><!--掌握 基于 AspectJ 的 注解 的 AOP實(shí)現(xiàn) 配置--><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>
- 通知類(lèi)
package com.qf.aspectaannotation;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class AspectjAnnotationAdvice {// @Before 表示前置通知@Before("execution(* save(..))")public void before(){System.out.println("基于aspectj注解形式的前置通知,要求大家掌握");}
}
- 測(cè)試效果
14.6 AOP小結(jié)
通過(guò)AOP提供的編碼流程,更便利的定制切面,更方便的定制了動(dòng)態(tài)代理。
進(jìn)而徹底解決了輔助功能冗余的問(wèn)題;
業(yè)務(wù)類(lèi)中職責(zé)單一性得到更好保障;
輔助功能也有很好的復(fù)用性。
14.7 通知類(lèi)【可選】
定義通知類(lèi),達(dá)到通知效果
前置通知:MethodBeforeAdvice后置通知:AfterAdvice后置通知:AfterReturningAdvice //有異常不執(zhí)行,方法會(huì)因異常而結(jié)束,無(wú)返回值異常通知:ThrowsAdvice環(huán)繞通知:MethodInterceptor
沒(méi)有必要把通知的執(zhí)行順序 記得非常精確 因?yàn)?spring 新版本 5 和 之前的舊版本 通知的執(zhí)行順序 不一樣
14.8 通配切入點(diǎn)
根據(jù)表達(dá)式通配切入點(diǎn)
<!--匹配參數(shù)-->
<aop:pointcut id="myPointCut" expression="execution(* *(com.qf.aaron.aop.basic.User))" />
<!--匹配方法名(無(wú)參)-->
<aop:pointcut id="myPointCut" expression="execution(* save())" />
<!--匹配方法名(任意參數(shù))-->
<aop:pointcut id="myPointCut" expression="execution(* save(..))" />
<!--匹配返回值類(lèi)型-->
<aop:pointcut id="myPointCut" expression="execution(com.qf.aaron.aop.basic.User *(..))" />
<!--匹配類(lèi)名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.UserServiceImpl.*(..))" />
<!--匹配包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.*.*(..))" />
<!--匹配包名、以及子包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop..*.*(..))" />
切入點(diǎn)表達(dá)式:expression 知道對(duì)哪個(gè)類(lèi)的那個(gè)方法進(jìn)行增強(qiáng)
語(yǔ)法結(jié)構(gòu): execution([權(quán)限修飾符] [返回值類(lèi)型] [類(lèi)全路徑] [方法名稱(chēng)] ([參數(shù)列表])
14.9 JDK和CGLIB選擇
spring底層,包含了jdk代理和cglib代理兩種動(dòng)態(tài)代理生成機(jī)制
基本規(guī)則是:目標(biāo)業(yè)務(wù)類(lèi)如果有接口則用JDK代理,沒(méi)有接口則用CGLib代理
class DefaultAopProxyFactory{// 該方法中明確定義了 JDK代理和CGLib代理的選取規(guī)則// 基本規(guī)則是:目標(biāo)業(yè)務(wù)類(lèi)如果有接口則用JDK代理,沒(méi)有接口則用CGLib代理public AopProxy createAopProxy(){...}
}
14.10 后處理器
spring中定義了很多后處理器;
每個(gè)bean在創(chuàng)建完成之前 ,都會(huì)有一個(gè)后處理過(guò)程,即再加工,對(duì)bean做出相關(guān)改變和調(diào)整;
spring-AOP中,就有一個(gè)專(zhuān)門(mén)的后處理器,負(fù)責(zé)通過(guò)原始業(yè)務(wù)組件(Service),再加工得到一個(gè)代理組件。
常用后處理器 |
---|
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-7iaN9bjA-1682258194560)(https://gllspictures.oss-cn-beijing.aliyuncs.com/img/系統(tǒng)后處理器.jpg)] |
14.10.1 后處理器定義
/*** 定義bean后處理器* 作用:在bean的創(chuàng)建之后,進(jìn)行再加工*/
public class MyBeanPostProcessor implements BeanPostProcessor{/*** 在bean的init方法之前執(zhí)行* @param bean 原始的bean對(duì)象* @param beanName* @return* @throws BeansException*/public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("后處理器 在init之前執(zhí)行~~~"+bean.getClass());return bean;}/*** 在bean的init方法之后執(zhí)行* @param bean postProcessBeforeInitialization返回的bean* @param beanName* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("后處理器 在init之后執(zhí)行~~~"+bean.getClass());return bean;// 此處的返回是 getBean() 最終的返回值}
}
14.10.2 配置后處理器
<!-- 配置后處理器,將對(duì)工廠中所有的bean聲明周期進(jìn)行干預(yù) -->
<bean class="com.qianfeng.beanpostprocessor.MyBeanPostProcessor"></bean>
14.10.3 bean生命周期
構(gòu)造 》 注入屬性 滿(mǎn)足依賴(lài) 》 后處理器前置過(guò)程 》 初始化 》后處理器后置過(guò)程 》 返回 》 銷(xiāo)毀
記住 記住 記住 記住 記住 記住
1.如果是單例 bean , 隨著容器的創(chuàng)建而創(chuàng)建 即 實(shí)例化,多例bean 是 獲取的時(shí)候 實(shí)例化
2.屬性注入
3.后處理器前置過(guò)程 即在初始化方法之前執(zhí)行的 方法 postProcessBeforeInitialization
4.初始化方法
5.后處理器后置過(guò)程 即在初始化方法之后執(zhí)行的 方法 postProcessAfterInitialization aop動(dòng)態(tài)代理就在這一步
6.得到最終的 bean
7.銷(xiāo)毀
14.10.4 動(dòng)態(tài)代理源碼(了解)
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父類(lèi)
// 該后處理器類(lèi)中的 wrapIfNecessary方法即動(dòng)態(tài)代理生成過(guò)程
AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){if (!this.earlyProxyReferences.contains(cacheKey)) {// 開(kāi)始動(dòng)態(tài)定制代理return wrapIfNecessary(bean, beanName, cacheKey);}
}
十五、Spring + MyBatis【重點(diǎn)
】
15.1 配置數(shù)據(jù)源
將數(shù)據(jù)源配置到項(xiàng)目中
15.1.1 引入jdbc.properties配置文件
#jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
15.1.2 整合Spring配置文件和properties配置文件
<!--spring-context.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"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"><!--配置文件參數(shù)化(參數(shù)占位符)--><context:property-placeholder location="classpath:jdbc.properties" /><!--與PooledDataSource集成(二選一)--><bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"><property name="driver" value="${driverClass}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></bean><!--與DruidDataSource集成(二選一)--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!--基本配置--><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>
</bean>
15.1.3 Druid連接池可選參數(shù)
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!--基本配置--><property name="driverClassName" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><!-- 配置初始化大小、最小、最大 --><property name="initialSize" value="${jdbc.init}"/><property name="minIdle" value="${jdbc.minIdle}"/><property name="maxActive" value="${jdbc.maxActive}"/><!-- 配置獲取連接等待超時(shí)的時(shí)間 --><property name="maxWait" value="60000"/><!-- 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000"/><!-- 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒 --><property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
15.1.4 Druid監(jiān)控中心
<!--web.xml-->
<servlet><servlet-name>DruidStatView</servlet-name><servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>DruidStatView</servlet-name><url-pattern>/druid/*</url-pattern>
</servlet-mapping>
15.1.5 測(cè)試監(jiān)控中心
配置tomcat,并訪問(wèn)protocol://ip:port/project/druid/index.html
15.2 整合MyBatis
將 SqlSessionFactory、DAO、Service 配置到項(xiàng)目中
15.2.1 導(dǎo)入依賴(lài)
<!-- spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.6.RELEASE</version>
</dependency><!-- spring+mybatis集成依賴(lài) -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version>
</dependency>
15.2.2 配置SqlSessionFactory
<!-- 工廠bean:生成SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 注入連接池 --><property name="dataSource" ref="dataSource"></property><!-- 注入dao-mapper文件信息 ,如果映射文件和dao接口 同包且同名,則此配置可省略--><property name="mapperLocations"><list><value>classpath:com/qf/spring/dao/*.xml</value></list></property><!-- 為 dao-mapper文件中的實(shí)體 定義缺省包路徑 如:<select id="queryAll" resultType="User"> 中 User類(lèi)可以不定義包--><property name="typeAliasesPackage" value="com.qf.entity"></property>
</bean>
15.2.3 配置MapperScannerConfigurer
管理DAO實(shí)現(xiàn)類(lèi)的創(chuàng)建,并創(chuàng)建DAO對(duì)象,存入工廠管理
掃描所有DAO接口,去構(gòu)建DAO實(shí)現(xiàn)
將DAO實(shí)現(xiàn)存入工廠管理
DAO實(shí)現(xiàn)對(duì)象在工廠中的id是:“首字母小寫(xiě)的-接口的類(lèi)名”,
例如:UserDAO==>userDAO , OrderDAO==>orderDAO
<!-- mapperScannerConfigurer -->
<bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- dao接口所在的包 如果有多個(gè)包,可以用逗號(hào)或分號(hào)分隔 <property name="basePackage" value="com.a.dao,com.b.dao"></property>--><property name="basePackage" value="com.qf.spring.dao"></property><!-- 如果工廠中只有一個(gè)SqlSessionFactory的bean,此配置可省略 --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
15.2.4 配置Service
<bean id="userService" class="com.qf.spring.service.UserServiceImpl"><!-- 注意ref中的值是對(duì)應(yīng)DAO接口的首字母小寫(xiě)的接口名 --><property name="userDAO" ref="userDAO"></property>
</bean>
15.3 我的整合步驟 看我的這個(gè)步驟 上面的可以不看
1.添加依賴(lài)
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>sm</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>sm Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.0.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.0.RELEASE</version></dependency><!--MyBatis核心依賴(lài)--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.6</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><!-- log4j日志依賴(lài) https://mvnrepository.com/artifact/log4j/log4j --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- spring-jdbc --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.0.RELEASE</version></dependency><!-- spring+mybatis集成依賴(lài) --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.2</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency></dependencies><build><finalName>sm</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.2.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin></plugins></pluginManagement><!-- 如果不添加此節(jié)點(diǎn)src/main/java目錄下的所有配置文件都會(huì)被漏掉。 --><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include><include>**/*.properties</include><include>**/*.ini</include></includes></resource></resources></build>
</project>
2.spring的配置文件
準(zhǔn)備數(shù)據(jù)源配置文件 db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456
<?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:aop="http://www.springframework.org/schema/aop"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/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--基于注解的形式 管理 bean--><!--掃描 com.qf 下的全部組件--><context:component-scan base-package="com.qf"></context:component-scan><!--掌握 基于 AspectJ 的 注解 的 AOP實(shí)現(xiàn) 配置--><aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--配置文件參數(shù)化(參數(shù)占位符)--><context:property-placeholder location="classpath:db.properties" /><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!--基本配置--><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><!-- 配置獲取連接等待超時(shí)的時(shí)間 --><property name="maxWait" value="60000"/><!-- 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="60000"/><!-- 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒 --><property name="minEvictableIdleTimeMillis" value="300000"/></bean><!-- 工廠bean:生成SqlSessionFactory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--數(shù)據(jù)源屬性--><property name="dataSource" ref="dataSource"></property><!--mybatis的配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"></property></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--掃描mapper 接口 自動(dòng)創(chuàng)建mapper層代理對(duì)象 交給spring管理--><property name="basePackage" value="com.qf.mapper"></property><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property></bean></beans>
mybatis的配置文件
<?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><typeAliases><!--自動(dòng)掃描包,將原類(lèi)名作為別名--><package name="com.qf.pojo" /></typeAliases><mappers><!--掃描xml 映射文件--><package name="com.qf.mapper"/></mappers>
</configuration>
整合 logback 日志
添加依賴(lài)
<!--logback 日志--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.2.3</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.logback-extensions</groupId><artifactId>logback-ext-spring</artifactId><version>0.1.4</version></dependency>
添加日志的配置文件 logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><Target>System.out</Target><Encoding>UTF-8</Encoding><encoder><pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} %5p [%t] [%c{1}]:%L - %m%n</pattern></encoder></appender><appender name="logfile"class="ch.qos.logback.core.rolling.RollingFileAppender"><Encoding>UTF-8</Encoding><encoder><pattern>%d %p [%t] [%c]:%L - %m%n</pattern></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>DEBUG</level></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>log/%d{yyyy-MM-dd-HH'.log'}</fileNamePattern></rollingPolicy></appender><logger name="org.springframework" level="WARN" /><logger name="org.springframework.remoting" level="WARN" /><logger name="org.springframework.scheduling.quartz" level="WARN" /><logger name="org.springframework.data.jpa" level="DEBUG" /><logger name="org.cometd" level="WARN" /><logger name="ch.qos.logback" level="WARN" /><logger name="com.springapp.mvc" level="DEBUG" /><logger name="com.qf.mapper" level="DEBUG"></logger><!-- <logger name="com.ibatis" level="DEBUG"></logger> --><root level="ERROR"><appender-ref ref="stdout" /><appender-ref ref="logfile" /></root>
</configuration>
配置到 web.xml 中
<!-- logback --><context-param><param-name>logbackConfigLocation</param-name><param-value>classpath:logback.xml</param-value></context-param><listener><listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class></listener>
整合 servlet ,后期 換成 springmvc ,當(dāng)前先用servlet
1.添加依賴(lài)
<!--整合一下 web--><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.2.0.RELEASE</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jsp-api</artifactId><version>2.0</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency>
2.在web.xml 中 添加一個(gè)監(jiān)聽(tīng)器 用于讀取spring 的配置文件 創(chuàng)建 容器
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><!--這個(gè)監(jiān)聽(tīng)器 幫助加載spring 配置文件,創(chuàng)建spring 容器--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>
- 創(chuàng)建servlet 在 servlet中 得到 容器對(duì)象 再通過(guò)容器對(duì)象來(lái)得到 service bean
package com.qf.web;import com.qf.pojo.User;
import com.qf.service.UserService;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @ClassName : LoginServlet* @Author : glls* @Date: 2021/8/10 14:43* @Description :*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {private UserService userService;@Overridepublic void init() throws ServletException {WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());userService = applicationContext.getBean("userService", UserService.class);}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String username = req.getParameter("username");String password = req.getParameter("password");//有了spring 管理 bean ,就不要再自己new 對(duì)象了 ,所以//從容器 把 userService 取出來(lái)User user = new User();user.setName(username);user.setPassword(password);User result = userService.login(user);System.out.println(result);}
}
十六、事務(wù)【重點(diǎn)
】
-
什么是事務(wù)?
事務(wù)是數(shù)據(jù)庫(kù)操作的基本單元,邏輯上的一組操作,要么都成功,要么都失敗,也就是說(shuō)只有一組操作中 有一個(gè)失敗了,那么這組操作都失敗
舉個(gè)栗子
wsc轉(zhuǎn)賬500給wjl
update t_account set money = money-500 where accountid = 1 wsc 的賬戶(hù) 減500
update t_account set money = money+500 where accountid=2 wjl的賬戶(hù) 加 500
-
事務(wù)的特性 ACID
A atomicity 原子性 一組操作 整體不可拆分要么都成功 要么都失敗
C consistency 一致性 數(shù)據(jù)在事務(wù)的前后,業(yè)務(wù)整體一致
I isolation 隔離性 事務(wù)之間 互相隔離
D durability 持久性 一旦事務(wù)執(zhí)行成功 數(shù)據(jù)一定會(huì)落盤(pán)在數(shù)據(jù)庫(kù)
3.準(zhǔn)備事務(wù)的環(huán)境
賬戶(hù)表
service 定義轉(zhuǎn)賬方法
//轉(zhuǎn)賬操作@Overridepublic void transferMoney(int from, int to, double money) {//from 給 to 轉(zhuǎn)了 money//一個(gè)賬戶(hù) 扣錢(qián)accountMapper.reduceMoney(from,money);//模擬一個(gè)異常int i = 5/0;//一個(gè)賬戶(hù)加錢(qián)accountMapper.addMOney(to,money);}
dao
@Update("update t_account set money= money - #{money} where userid= #{from} ")void reduceMoney(@Param("from") int from,@Param("money") double money);@Update("update t_account set money = money + #{money} where userid = #{to} ")void addMOney(@Param("to")int to, @Param("money") double money);
轉(zhuǎn)賬過(guò)程中 模擬異常,出現(xiàn)一個(gè)賬戶(hù)扣錢(qián)了,另一個(gè)賬戶(hù)卻沒(méi)有加錢(qián) 數(shù)據(jù)不一致問(wèn)題 怎么解決?
使用事務(wù) 進(jìn)行解決
spring提供了 事務(wù)的解決方案 底層原理是 AOP
- 基于注解的方案 掌握
- 基于xml配置文件的方式 了解
16.1 配置DataSourceTransactionManager
事務(wù)管理器,其中持有DataSource,可以控制事務(wù)功能(commit,rollback等)。
<!-- 1. 引入一個(gè)事務(wù)管理器,其中依賴(lài)DataSource,借以獲得連接,進(jìn)而控制事務(wù)邏輯 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property>
</bean>
注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一個(gè)DataSource的Bean,否則事務(wù)控制失敗!!!
####16.2 配置事務(wù)通知
基于事務(wù)管理器,進(jìn)一步定制,生成一個(gè)額外功能:Advice。
此Advice可以切入任何需要事務(wù)的方法,通過(guò)事務(wù)管理器為方法控制事務(wù)。
<tx:advice id="txManager" transaction-manager="tx"><tx:attributes><!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>--><!-- 以User結(jié)尾的方法,切入此方法時(shí),采用對(duì)應(yīng)事務(wù)實(shí)行--><tx:method name="*User" rollback-for="Exception"/><!-- 以query開(kāi)頭的方法,切入此方法時(shí),采用對(duì)應(yīng)事務(wù)實(shí)行 --><tx:method name="query*" propagation="SUPPORTS"/><!-- 剩余所有方法 --><tx:method name="*"/></tx:attributes>
</tx:advice>
16.3 事務(wù)屬性
16.3.1 隔離級(jí)別
16.3.1.1 概念
isolation
隔離級(jí)別
名稱(chēng) | 描述 |
---|---|
default | (默認(rèn)值)(采用數(shù)據(jù)庫(kù)的默認(rèn)的設(shè)置) (建議) |
read-uncommited | 讀未提交 |
read-commited | 讀提交 (Oracle數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別) |
repeatable-read | 可重復(fù)讀 (MySQL數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別) |
serialized-read | 序列化讀 |
隔離級(jí)別由低到高為:read-uncommited < read-commited < repeatable-read < serialized-read
16.3.1.2 特性
安全性:級(jí)別越高,多事務(wù)并發(fā)時(shí),越安全。因?yàn)楣蚕淼臄?shù)據(jù)越來(lái)越少,事務(wù)間彼此干擾減少。
并發(fā)性:級(jí)別越高,多事務(wù)并發(fā)時(shí),并發(fā)越差。因?yàn)楣蚕淼臄?shù)據(jù)越來(lái)越少,事務(wù)間阻塞情況增多。
16.3.1.3 并發(fā)問(wèn)題
事務(wù)并發(fā)時(shí)的安全問(wèn)題
問(wèn)題 | 描述 |
---|---|
臟讀 | 一個(gè)事務(wù)讀取到另一個(gè)事務(wù)還未提交的數(shù)據(jù)。大于等于 read-commited 可防止 |
不可重復(fù)讀 | 一個(gè)事務(wù)內(nèi)多次讀取一行數(shù)據(jù)的相同內(nèi)容,其結(jié)果不一致。大于等于 repeatable-read 可防止 |
幻影讀 | 幻讀,并不是說(shuō)兩次讀取獲取的結(jié)果集不同,幻讀側(cè)重的方面是某一次的 select 操作得到的結(jié)果所表征的數(shù)據(jù)狀態(tài)無(wú)法支撐后續(xù)的業(yè)務(wù)操作。更為具體一些:select 某記錄是否存在,不存在,準(zhǔn)備插入此記錄,但執(zhí)行 insert 時(shí)發(fā)現(xiàn)此記錄已存在,無(wú)法插入,此時(shí)就發(fā)生了幻讀。mysql 幻讀的詳解、實(shí)例及解決辦法 - SegmentFault 思否 |
16.3.2 傳播行為
propagation
傳播行為
當(dāng)涉及到事務(wù)嵌套(Service調(diào)用Service)時(shí),可以設(shè)置:
SUPPORTS = 不存在外部事務(wù),則不開(kāi)啟新事務(wù);存在外部事務(wù),則合并到外部事務(wù)中。(適合查詢(xún))
REQUIRED = 不存在外部事務(wù),則開(kāi)啟新事務(wù);存在外部事務(wù),則合并到外部事務(wù)中。 (默認(rèn)值)(適合增刪改)
參考資料: 事務(wù)隔離級(jí)別和傳播行為_(kāi)Spring事務(wù)的傳播行為和隔離級(jí)別_weixin_39604685的博客-CSDN博客
16.3.3 讀寫(xiě)性
readonly
讀寫(xiě)性
true:只讀,可提高查詢(xún)效率。(適合查詢(xún))
false:可讀可寫(xiě)。 (默認(rèn)值)(適合增刪改)
16.3.4 事務(wù)超時(shí)
timeout
事務(wù)超時(shí)時(shí)間
當(dāng)前事務(wù)所需操作的數(shù)據(jù)被其他事務(wù)占用,則等待。
- 100:自定義等待時(shí)間100(秒)。
- -1:由數(shù)據(jù)庫(kù)指定等待時(shí)間,默認(rèn)值。(建議)
16.3.5 事務(wù)回滾
rollback-for
回滾屬性
如果事務(wù)中拋出 RuntimeException,則自動(dòng)回滾
如果事務(wù)中拋出 CheckException(非運(yùn)行時(shí)異常 Exception),不會(huì)自動(dòng)回滾,而是默認(rèn)提交事務(wù)
處理方案 : 將CheckException轉(zhuǎn)換成RuntimException上拋,或 設(shè)置 rollback-for=“Exception.class”
16.4 編織
將事務(wù)管理的Advice 切入需要事務(wù)的業(yè)務(wù)方法中
<aop:config><aop:pointcut expression="execution(* com.qf.spring.service.UserServiceImpl.*(..))" id="pc"/><!-- 組織切面 --><aop:advisor advice-ref="txManager" pointcut-ref="pc"/>
</aop:config>
基于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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--1. 配置事務(wù)管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!--2. 配置 事務(wù)的通知--><tx:advice id="txadvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="transferMoney" propagation="REQUIRED"/></tx:attributes></tx:advice><!--3.織入切面--><aop:config><!--配置切點(diǎn)--><aop:pointcut id="txpoint" expression="execution(* com.qf.service.impl.AccountServiceImpl.*(..))"/><!--配置切面--><aop:advisor advice-ref="txadvice" pointcut-ref="txpoint"></aop:advisor></aop:config></beans>
十七、注解開(kāi)發(fā)
1.什么是注解
(1)注解是代碼特殊標(biāo)記,格式:@注解名稱(chēng)(屬性名稱(chēng)=屬性值, 屬性名稱(chēng)=屬性值…)
(2)使用注解,注解作用在類(lèi)上面,方法上面,屬性上面
(3)使用注解目的:簡(jiǎn)化 xml 配置
2.spring針對(duì)Bean管理創(chuàng)建對(duì)象提供的注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四個(gè)注解功能是一樣的,都可以用來(lái)創(chuàng)建 bean 實(shí)例
3.基于注解方式 實(shí)現(xiàn)對(duì)象創(chuàng)建
1.需要aop 依賴(lài)的支持 spring-aop
2.開(kāi)啟組件掃描
1 如果掃描多個(gè)包,多個(gè)包使用逗號(hào)隔開(kāi) 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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"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/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 掃描--><context:component-scan base-package="com.glls"></context:component-scan></beans>
3.創(chuàng)建類(lèi) 在類(lèi)上添加創(chuàng)建bean 的注解
//在注解里面 value 屬性值可以省略不寫(xiě),
//默認(rèn)值是類(lèi)名稱(chēng),首字母小寫(xiě)
//UserService – userService
4.開(kāi)啟組件掃描細(xì)節(jié)配置
<!--下面的配置 是 只掃描Controller<context:component-scan base-package="com.glls" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>--><!--下面的配置 是 忽略Controller<context:component-scan base-package="com.glls" ><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>--><!--掃描該包下 的所有注解--><context:component-scan base-package="com.glls" ></context:component-scan>
5.基于注解的方式 實(shí)現(xiàn)屬性注入
(1)@Autowired:先根據(jù)類(lèi)型注入 再根據(jù)名字注入
第一步 把 service 和 dao 對(duì)象創(chuàng)建,在 service 和 dao 類(lèi)添加創(chuàng)建對(duì)象注解
第二步 在 service 注入 dao 對(duì)象,在 service 類(lèi)添加 dao 類(lèi)型屬性,在屬性上面使用注解
@Service public class UserService { //定義 dao 類(lèi)型屬性 //不需要添加 set 方法 //添加注入屬性注解 @Autowired private UserDao userDao; public void add() { System.out.println("service add......."); userDao.add(); } }
(2)@Qualifier:根據(jù)名稱(chēng)進(jìn)行注入
這個(gè)@Qualifier 注解的使用,和上面@Autowired 一起使用
//定義 dao 類(lèi)型屬性 //不需要添加 set 方法//添加注入屬性注解 @Autowired //根據(jù)類(lèi)型進(jìn)行注入 @Qualifier(value = "userDaoImpl1") //根據(jù)名稱(chēng)進(jìn)行注入 private UserDao userDao;
(3)@Resource:先根據(jù)名字注入 再根據(jù)類(lèi)型注入
//@Resource //根據(jù)類(lèi)型進(jìn)行注入 @Resource(name = "userDaoImpl1") //根據(jù)名稱(chēng)進(jìn)行注入 private UserDao userDao;
(4)@Value:注入普通類(lèi)型屬性
@Value(value = "abc") private String name;
6、完全注解開(kāi)發(fā)
(1)創(chuàng)建配置類(lèi),替代 xml 配置文件
@Configuration //作為配置類(lèi),替代 xml 配置文件 @ComponentScan(basePackages = {"com.glls"}) public class SpringConfig { }
(2)編寫(xiě)測(cè)試類(lèi)
@Test public void testService2() { //加載配置類(lèi) ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-2kf3XTow-1682258194561)(D:\前鋒學(xué)習(xí)筆記\筆記\image-20230423162534899.png)]
17.1 聲明bean
用于替換自建類(lèi)型組件的 <bean…>標(biāo)簽;可以更快速的聲明bean
@Service 業(yè)務(wù)類(lèi)專(zhuān)用
@Repository dao實(shí)現(xiàn)類(lèi)專(zhuān)用
@Controller web層專(zhuān)用@Component 通用
@Scope 用戶(hù)控制bean的創(chuàng)建模式
// @Service說(shuō)明 此類(lèi)是一個(gè)業(yè)務(wù)類(lèi),需要將此類(lèi)納入工廠 等價(jià)替換掉 <bean class="xxx.UserServiceImpl">
// @Service默認(rèn)beanId == 首字母小寫(xiě)的類(lèi)名"userServiceImpl"
// @Service("userService") 自定義beanId為"userService"
@Service //聲明bean,且id="userServiceImpl"
@Scope("singleton") //聲明創(chuàng)建模式,默認(rèn)為單例模式 ;@Scope("prototype")即可設(shè)置為多例模式
public class UserServiceImpl implements UserService {...
}
17.2 注入(DI)
用于完成bean中屬性值的注入
- @Autowired 基于類(lèi)型自動(dòng)注入 先根據(jù)類(lèi)型注入 如果找到多個(gè) 再根據(jù)名稱(chēng)注入
- @Resource 基于名稱(chēng)自動(dòng)注入 先根據(jù)名稱(chēng)注入 如果根據(jù)名稱(chēng)沒(méi)找到 則根據(jù)類(lèi)型找 根據(jù)類(lèi)型 找到多個(gè) 則報(bào)錯(cuò) 找到一個(gè)則注入
- @Qualifier(“userDAO”) 限定要自動(dòng)注入的bean的id,一般和@Autowired聯(lián)用
- @Value 注入簡(jiǎn)單類(lèi)型數(shù)據(jù) (jdk8種+String)
@Service
public class UserServiceImpl implements UserService {@Autowired //注入類(lèi)型為UserDAO的bean@Qualifier("userDAO2") //如果有多個(gè)類(lèi)型為UserDAO的bean,可以用此注解從中挑選一個(gè)private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService {@Resource("userDAO3") //注入id=“userDAO3”的beanprivate UserDAO userDAO;/*@Resource //注入id=“userDAO”的beanprivate UserDAO userDAO;*/
}
public class XX{@Value("100") //注入數(shù)字private Integer id;@Value("shine") //注入Stringprivate String name;
}
17.3 事務(wù)控制
用于控制事務(wù)切入
@Transactional
工廠配置中的 <tx:advice… 和 <aop:config… 可以省略 !!
//類(lèi)中的每個(gè)方法都切入事務(wù)(有自己的事務(wù)控制的方法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {...//該方法自己的事務(wù)控制,僅對(duì)此方法有效@Transactional(propagation=Propagation.SUPPORTS)public List<User> queryAll() {return userDao.queryAll();}public void save(User user){userDao.save(user);}
}
17.4 注解所需配置
<!-- 告知spring,哪些包中 有被注解的類(lèi)、方法、屬性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan><!-- 1. 引入一個(gè)事務(wù)管理器,其中依賴(lài)DataSource,借以獲得連接,進(jìn)而控制事務(wù)邏輯 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!-- 開(kāi)啟事務(wù)的注解--><tx:annotation-driven transaction-manager="transactionManager"/>
17.5 AOP開(kāi)發(fā)
17.5.1 注解使用
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect // 聲明此類(lèi)是一個(gè)切面類(lèi):會(huì)包含切入點(diǎn)(pointcut)和通知(advice)
@Component //聲明組件,進(jìn)入工廠
public class MyAspect {// 定義切入點(diǎn)@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")public void pc(){}@Before("pc()") // 前置通知public void mybefore(JoinPoint a) {System.out.println("target:"+a.getTarget());System.out.println("args:"+a.getArgs());System.out.println("method's name:"+a.getSignature().getName());System.out.println("before~~~~");}@AfterReturning(value="pc()",returning="ret") // 后置通知public void myAfterReturning(JoinPoint a,Object ret){System.out.println("after~~~~:"+ret);}@Around("pc()") // 環(huán)繞通知public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {System.out.println("interceptor1~~~~");Object ret = p.proceed();System.out.println("interceptor2~~~~");return ret;}@AfterThrowing(value="pc()",throwing="ex") // 異常通知public void myThrows(JoinPoint jp,Exception ex){System.out.println("throws");System.out.println("===="+ex.getMessage());}
}
17.5.2 配置
<!-- 添加如下配置,啟用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
十八、集成JUnit
18.1 導(dǎo)入依賴(lài)
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>4.3.6.RELEASE</version>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency>
18.2 編碼
可以免去工廠的創(chuàng)建過(guò)程;
可以直接將要測(cè)試的組件注入到測(cè)試類(lèi)。
@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner啟動(dòng)測(cè)試
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置
public class SpringTest{//當(dāng)前測(cè)試類(lèi)也會(huì)被納入工廠中,所以其中屬性可以注入@Autowired // 注入要測(cè)試的組件@Qualifier("userDAO")private UserDAO userDAO;@Testpublic void test(){// 測(cè)試使用userDAOuserDAO.queryUser();....}
}
集成Junit 5
<!--spring整合 junit 5 --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.9.RELEASE</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.5.2</version><scope>test</scope></dependency>
測(cè)試類(lèi)
@SpringJUnitConfig(locations = "classpath:applicationContext2.xml")
class OrderServiceImplTest {@Autowiredprivate OrderService orderService;@Testvoid findOrderByUserId() {List<Order> orderByUserId = orderService.findOrderByUserId(10);}
}
十九、個(gè)人擴(kuò)展
spring5 新的模塊 webflux
1、SpringWebflux 介紹
(1)是 Spring5 添加新的模塊,用于 web 開(kāi)發(fā)的,功能和 SpringMVC 類(lèi)似的,Webflux 使用 當(dāng)前一種比較流程響應(yīng)式編程出現(xiàn)的框架。
(2)使用傳統(tǒng) web 框架,比如 SpringMVC,這些基于 Servlet 容器,Webflux 是一種異步非阻 塞的框架,異步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相關(guān) API 實(shí)現(xiàn) 的。
(3)解釋什么是異步非阻塞
- 異步和同步
- 非阻塞和阻塞
- 上面都是針對(duì)對(duì)象不一樣
- 異步和同步針對(duì)調(diào)用者,調(diào)用者發(fā)送請(qǐng)求,如果等著對(duì)方回應(yīng)之后才去做其他事情就是同 步,如果發(fā)送請(qǐng)求之后不等著對(duì)方回應(yīng)就去做其他事情就是異步
- 阻塞和非阻塞針對(duì)被調(diào)用者,被調(diào)用者受到請(qǐng)求之后,做完請(qǐng)求任務(wù)之后才給出反饋就是阻 塞,受到請(qǐng)求之后馬上給出反饋然后再去做事情就是非阻塞
(4)Webflux 特點(diǎn):
第一 非阻塞式:在有限資源下,提高系統(tǒng)吞吐量和伸縮性,以 Reactor 為基礎(chǔ)實(shí)現(xiàn)響應(yīng)式編程
第二 函數(shù)式編程:Spring5 框架基于 java8,Webflux 使用 Java8 函數(shù)式編程方式實(shí)現(xiàn)路由請(qǐng)求
(5)比較 SpringMVC
第一 兩個(gè)框架都可以使用注解方式,都運(yùn)行在 Tomet 等容器中
第二 SpringMVC 采用命令式編程,Webflux 采用異步響應(yīng)式編程
2、響應(yīng)式編程(Java 實(shí)現(xiàn))
(1)什么是響應(yīng)式編程 響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式。這意味著可以在編程語(yǔ)言中很方便 地表達(dá)靜態(tài)或動(dòng)態(tài)的數(shù)據(jù)流,而相關(guān)的計(jì)算模型會(huì)自動(dòng)將變化的值通過(guò)數(shù)據(jù)流進(jìn)行傳播。 電子表格程序就是響應(yīng)式編程的一個(gè)例子。單元格可以包含字面值或類(lèi)似"=B1+C1"的公 式,而包含公式的單元格的值會(huì)依據(jù)其他單元格的值的變化而變化。
3.手寫(xiě)spring 偽源碼
- 創(chuàng)建maven工程 無(wú)需添加任何依賴(lài)
-
在spring包下創(chuàng)建容器類(lèi) GllsApplicationContext
-
二十 個(gè)人源碼解析
個(gè)人學(xué)習(xí)
源碼分析
ClassPathXmlApplicationContext:
super(parent); 此時(shí) 傳進(jìn)來(lái)的super 為空, 在這個(gè)方法中 調(diào)父類(lèi)AbstractApplicationContext 構(gòu)造方法 創(chuàng)建了資源解析器
/**
Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
setConfigLocations(configLocations);




調(diào)父類(lèi)構(gòu)造

這個(gè)方法由子類(lèi)重寫(xiě)



總結(jié): resolvePath(String path) 方法做了什么事情?
1.實(shí)例化 Environment
2.解析傳進(jìn)來(lái)的 path
refresh()方法
AbstractApplicationContext 類(lèi)中
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}