1.spring
Spring 框架可以說是Java 世界最為成功的框架,在企業實際應用中,大部分的企業架構都基於Spring 框架。它的成功來自於理念,而不是技術,它最為核心的理念是IoC (控制反轉)和AOP (面向切面編程),其中IoC 是Spring的基礎,而AOP 則是其重要的功能,最為典型的當屬數據庫事務的使用。
Spring最根本的使命是解決企業級應用開發的復雜性,即簡化Java開發。
1.1.優點
-
方便解耦,簡化開發
Spring就是一個大工廠,可以將所有對象的創建和依賴關系的維護,交給Spring管理。
-
AOP編程的支持
Spring提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能。
-
聲明式事務的支持
只需要通過配置就可以完成對事務的管理,而無需手動編程。
-
方便程序的測試
Spring對Junit4支持,可以通過注解方便的測試Spring程序。
-
方便集成各種優秀框架
Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
-
降低JavaEE API的使用難度
Spring對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大降低。
1.2.缺點
- Spring明明一個很輕量級的框架,卻給人感覺大而全
- Spring依賴反射,反射影響性能
- 使用門檻升高,入門Spring需要較長時間
1.3.Spring框架的組成結構圖
Spring 總共大約有 20 個模塊, 由 1300 多個不同的文件構成。 而這些組件被分別整合在核心容器(Core Container) 、 AOP(Aspect Oriented Programming)和設備支持(Instrmentation) 、數據訪問與集成(Data Access/Integeration) 、 Web、 消息(Messaging) 、 Test等 6 個模塊中。 以下是 Spring 5 的模塊結構圖:
組成 Spring 框架的每個模塊(或組件)都可以單獨存在,或者與其他一個或多個模塊聯合實現。每個模塊的功能如下:
1.3.1.核心容器
Spring的核心容器是其他模塊建立的基礎,有spring-core、spring-beans、spring-context、spring-context-support和spring-expression(Spring表達式語言)等模塊組成。
spring-core 模塊:提供了框架的基本組成部分,包括控制反轉(Inversion of Control,IOC)和依賴注入(Dependency Injection,DI)功能。
spring-beans 模塊:提供了BeanFactory,是工廠模式的一個經典實現,Spring將管理對象稱為Bean。
spring-context 模塊:建立在Core和Beans模塊的基礎之上,提供一個框架式的對象訪問方式,是訪問定義和配置的任何對象的媒介。ApplicationContext接口是Context模塊的焦點。
spring-context-support 模塊:支持整合第三方庫到Spring應用程序上下文,特別是用於高速緩存(EhCache、JCache)和任務調度(CommonJ、Quartz)的支持。
Spring-expression 模塊:提供了強大的表達式語言去支持運行時查詢和操作對象圖。這是對JSP2.1規范中規定的統一表達式語言(Unified EL)的擴展。該語言支持設置和獲取屬性值、屬性分配、方法調用、訪問數組、集合和索引器的內容、邏輯和算術運算、變量命名以及從Spring的IOC容器中以名稱檢索對象。它還支持列表投影、選擇以及常用的列表聚合。
1.3.2.AOP 和設備支持
由spring-aop、 spring-aspects 和 spring-instrument等 3 個模塊組成。
spring-aop 模塊:是 Spring 的另一個核心模塊,提供了一個符合 AOP 要求的面向切面的編程實現。 作為繼 OOP(面向對象編程) 后, 對程序員影響最大的編程思想之一, AOP 極大地開拓了人們對於編程的思路。 在 Spring 中, 以動態代理技術為基礎,允許定義方法攔截器和切入點,將代碼按照功能進行分離,以便干凈地解耦。
spring-aspects 模塊:提供了與AspectJ的集成功能,AspectJ是一個功能強大且成熟的AOP框架。
spring-instrument 模塊:是 AOP 的一個支援模塊, 提供了類植入(Instrumentation)支持和類加載器的實現,可以在特定的應用服務器中使用。主要作用是在 JVM 啟用時, 生成一個代理類, 程序員通過代理類在運行時修改類的字節, 從而改變一個類的功能, 實現 AOP 的功能。
1.3.3.數據訪問與集成
由 spring-jdbc、spring-orm、spring-oxm、spring-jms 和 spring-tx 等 5 個模塊組成。
spring-jdbc 模塊:提供了一個JDBC的抽象層,消除了煩瑣的JDBC編碼和數據庫廠商特有的錯誤代碼解析, 用於簡化JDBC。主要是提供 JDBC 模板方式、 關系數據庫對象化方式、 SimpleJdbc 方式、 事務管理來簡化 JDBC 編程, 主要實現類是 JdbcTemplate、 SimpleJdbcTemplate 以及 NamedParameterJdbcTemplate。
spring-orm 模塊:是 ORM 框架支持模塊, 主要集成 Hibernate, Java Persistence API (JPA) 和Java Data Objects (JDO) 用於資源管理、 數據訪問對象(DAO)的實現和事務策略。
spring-oxm 模塊:主要提供一個抽象層以支撐 OXM(OXM 是 Object-to-XML-Mapping 的縮寫, 它是一個 O/M-mapper, 將 java 對象映射成 XML 數據, 或者將 XML 數據映射成 java 對象) , 例如: JAXB,Castor,XMLBeans,JiBX 和 XStream 等。
spring-jms模塊(Java Messaging Service):指Java消息傳遞服務,包含用於生產和使用消息的功能。自Spring4.1以后,提供了與spring-messaging模塊的集成。
spring-tx 模塊:事務模塊,支持用於實現特殊接口和所有POJO(普通Java對象)類的編程和聲明式事務管理。
1.3.4.Web
由spring-websocket、spring-webmvc、spring-web、portlet和spring-webflux模塊等 5 個模塊組成。
spring-websocket 模塊:Spring4.0以后新增的模塊,實現雙工異步通訊協議,實現了WebSocket和SocketJS,提供Socket通信和web端的推送功能。
spring-webmvc 模塊:也稱為Web-Servlet模塊,包含用於web應用程序的Spring MVC和REST Web Services實現。Spring MVC框架提供了領域模型代碼和Web表單之間的清晰分離,並與Spring Framework的所有其他功能集成。
spring-web 模塊:提供了基本的Web開發集成功能,包括使用Servlet監聽器初始化一個IOC容器以及Web應用上下文,自動載入WebApplicationContext特性的類,Struts集成類、文件上傳的支持類、Filter類和大量輔助工具類。
portlet 模塊:實現web模塊功能的聚合,類似於Servlet模塊的功能,提供了Portlet環境下的MVC實現。
spring-webflux 模塊:是一個新的非堵塞函數式 Reactive Web 框架, 可以用來建立異步的, 非阻塞,事件驅動的服務, 並且擴展性非常好。
1.3.5.消息(Messaging)
即 spring-messaging 模塊。
spring-messaging 是從 Spring4 開始新加入的一個模塊, 該模塊提供了對消息傳遞體系結構和協議的支持。
1.3.6.Test
即 spring-test 模塊。
spring-test 模塊主要為測試提供支持的,支持使用JUnit或TestNG對Spring組件進行單元測試和集成測試。
2.Spring核心ioc
Ioc—Inversion of Control,即“控制反轉”,不是什么技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。就是不實例化了。先注入。
誰控制誰,控制什么:傳統Java SE程序設計,我們直接在對象內部通過new進行創建對象,是程序主動去創建依賴對象;而IoC是有專門一個容器來創建這些對象,即由Ioc容器來控制對象的創建;誰控制誰?當然是IoC 容器控制了對象;控制什么?那就是主要控制了外部資源獲取。
●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙創建及注入依賴對象;為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。
ps:控制反轉是目標,依賴注入是手段。
2.1.ioc容器
IoC 容器是 Spring 的核心,也可以稱為 Spring 容器。Spring 通過 IoC 容器來管理對象的實例化和初始化,以及對象從創建到銷毀的整個生命周期。
Spring 中使用的對象都由 IoC 容器管理,不需要我們手動使用 new 運算符創建對象。由 IoC 容器管理的對象稱為 Spring Bean,Spring Bean 就是 Java 對象,和使用 new 運算符創建的對象沒有區別。
Spring 通過讀取 XML 或 Java 注解中的信息來獲取哪些對象需要實例化。
Spring 提供 2 種不同類型的 IoC 容器,即 BeanFactory 和 ApplicationContext 容器
2.1.BeanFactory 容器
BeanFactory 是最簡單的容器,由 org.springframework.beans.factory.BeanFactory 接口定義,采用懶加載(lazy-load),所以容器啟動比較快。BeanFactory 提供了容器最基本的功能。
為了能夠兼容 Spring 集成的第三方框架(如 BeanFactoryAware、InitializingBean、DisposableBean),所以目前仍然保留了該接口。
簡單來說,BeanFactory 就是一個管理 Bean 的工廠,它主要負責初始化各種 Bean,並調用它們的生命周期方法。
BeanFactory 接口有多個實現類,最常見的是 org.springframework.beans.factory.xml.XmlBeanFactory。使用 BeanFactory 需要創建 XmlBeanFactory 類的實例,通過 XmlBeanFactory 類的構造函數來傳遞 Resource 對象。如下所示。
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
2.1.2. ApplicationContext 容器
ApplicationContext 繼承了 BeanFactory 接口,由 org.springframework.context.ApplicationContext 接口定義,對象在啟動容器時加載。ApplicationContext 在 BeanFactory 的基礎上增加了很多企業級功能,例如 AOP、國際化、事件支持等。
ApplicationContext 接口有兩個常用的實現類,具體如下。
2.1.2.1.ClassPathXmlApplicationContext
該類從類路徑 ClassPath 中尋找指定的 XML 配置文件,並完成 ApplicationContext 的實例化工作,具體如下所示。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
在上述代碼中,configLocation 參數用於指定 Spring 配置文件的名稱和位置,如 Beans.xml。
2.1.2.2.FileSystemXmlApplicationContext
該類從指定的文件系統路徑中尋找指定的 XML 配置文件,並完成 ApplicationContext 的實例化工作,具體如下所示。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
它與 ClassPathXmlApplicationContext 的區別是:在讀取 Spring 的配置文件時,FileSystemXmlApplicationContext 不會從類路徑中讀取配置文件,而是通過參數指定配置文件的位置。即 FileSystemXmlApplicationContext 可以獲取類路徑之外的資源,如“F:/workspaces/Beans.xml”。
2.1.2.3.AnnotationConfigApplicationContext
讀取用注解創建容器
通常在 Java 項目中,會采用 ClassPathXmlApplicationContext 類實例化 ApplicationContext 容器的方式,而在 Web 項目中,ApplicationContext 容器的實例化工作會交由 Web 服務器完成。Web 服務器實例化 ApplicationContext 容器通常使用基於 ContextLoaderListener 實現的方式,它只需要在 web.xml 中添加如下代碼:
<!--指定Spring配置文件的位置,有多個配置文件時,以逗號分隔-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--spring將加載spring目錄下的applicationContext.xml文件-->
<param-value>
classpath:spring/applicationContext.xml
</param-value>
</context-param>
<!--指定以ContextLoaderListener方式啟動Spring容器-->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
需要注意的是,BeanFactory 和 ApplicationContext 都是通過 XML 配置文件加載 Bean 的。
二者的主要區別在於,如果 Bean 的某一個屬性沒有注入,使用 BeanFacotry 加載后,第一次調用 getBean() 方法時會拋出異常,而 ApplicationContext 則會在初始化時自檢,這樣有利於檢查所依賴的屬性是否注入。
因此,在實際開發中,通常都選擇使用 ApplicationContext,只有在系統資源較少時,才考慮使用 BeanFactory。
2.2.使用ioc容器
2.2.1.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">
<bean id="user" class="com.wyl.pojo.User">
<property name="name" value="王延領"/>
</bean>
</beans>
2.2.2.pojo.User
public class User {
private String name;
public User() {
System.out.println("user無參構造方法");
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
2.2.2.test
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//在執行getBean的時候, user已經創建好了 , 通過無參構造
User user = (User) context.getBean("user");
//調用對象的方法 .
user.show();
}
2.3.bean
2.3.1.定義
由 Spring IoC 容器管理的對象稱為 Bean,Bean 根據 Spring 配置文件中的信息創建。可以把 Spring IoC 容器看作是一個大工廠,Bean 相當於工廠的產品,如果希望這個大工廠生產和管理 Bean,則需要告訴容器需要哪些 Bean,以及需要哪種方式裝配 Bean。
Spring 配置文件支持兩種格式,即 XML 文件格式和 Properties 文件格式。
-
Properties 配置文件主要以 key-value 鍵值對的形式存在,只能賦值,不能進行其他操作,適用於簡單的屬性配置。
-
XML 配置文件是樹形結構,相對於 Properties 文件來說更加靈活。XML 配置文件結構清晰,但是內容比較繁瑣,適用於大型復雜的項目。
通常情況下,Spring 的配置文件使用 XML 格式。XML 配置文件的根元素是
2.3.2.創建
2.3.2.1 默認方式
無參
<!-- 1. 默認構造函數,如果類中沒有默認構造函數則無法創建對象;bean標簽中只有id和class就默認使用構造函數創建對象 -->
<bean id="userService" class="com.wyl.pojo.User"/>
有參
<!-- 第一種根據index參數下標設置 -->
<bean id="userService" class="com.wyl.pojo.User">
<!-- index指構造方法 , 下標從0開始 -->
<constructor-arg index="0" value="wyl"/>
</bean>
<!-- 第二種根據參數名字設置 -->
<bean id="userService" class="com.wyl.pojo.User">
<!-- name指參數名 -->
<constructor-arg name="name" value="wyl"/>
</bean>
<!-- 第三種根據參數類型設置 -->
<bean id="userService" class="com.wyl.pojo.User">
<constructor-arg type="java.lang.String" value="wyl"/>
</bean>
2.3.2.2 工廠類中的方法
<!-- 2. 使用工廠中的方法創建對象;工廠中有一個方法可以創建對象,先創建工廠對象,通過factory-bean指向工廠,使用factory-method方法獲取對象 -->
<bean id="beanFactory" class="org.factory.BeanFactory"/>
<bean id="userService" factory-bean="beanFactory" factory-method="getUserService"/>
2.3.2.3 靜態工廠中的靜態方法
<!-- 3. 使用靜態工廠中的靜態方法創建對象 -->
<bean id="userService" class="org.factory.StaticBeanFactory" factory-method="getUserService"/>
2.3.2.配置
2.3.2.1.別名
<!-- 別名 : 如果添加了別名,我們也可以使用別名獲取到這個對象 -->
<alias name="User" alias="u1"></alias>
2.3.2.2.bean 別名
<!--
bean標簽常用屬性:
id屬性:起名稱,id屬性值名稱任意命名,不能包含特殊符號
class屬性:創建對象所在類的全路徑
name屬性:功能和id屬性一樣的,但是在name屬性值里面可以包含特殊符號
scope屬性
singleton:默認值,單例
prototype:多例
request:創建對象把對象放到request域里面
session:創建對象把對象放到session域里面
globalSession:創建對象把對象放到globalSession里面
-->
<bean id="UserT" class="com.wyl.pojo.User" scope="singleton" name="u2 u21,u22;u23">
<property name="name" value="123"/>
</bean>
2.3.2.2.import
團隊的合作通過import來實現 .
<import resource="beans.xml"/>
能將多個人開發的不同的配置xml文件整合到applicationContext.xml文件中,並且能夠合適的去重。
2.3.3.作用域
<bean id="..." class="..." scope="singleton"/>
Spring 容器在初始化一個 Bean 實例時,同時會指定該實例的作用域。Spring 5 支持以下 6 種作用域。
singleton
默認值,單例模式,表示在 Spring 容器中只有一個 Bean 實例,Bean 以單例的方式存在。
prototype
原型模式,表示每次通過 Spring 容器獲取 Bean 時,容器都會創建一個 Bean 實例。
request
每次 HTTP 請求,容器都會創建一個 Bean 實例。該作用域只在當前 HTTP Request 內有效。
session
同一個 HTTP Session 共享一個 Bean 實例,不同的 Session 使用不同的 Bean 實例。該作用域僅在當前 HTTP Session 內有效。
application
同一個 Web 應用共享一個 Bean 實例,該作用域在當前 ServletContext 內有效。
類似於 singleton,不同的是,singleton 表示每個 IoC 容器中僅有一個 Bean 實例,而同一個 Web 應用中可能會有多個 IoC 容器,但一個 Web 應用只會有一個 ServletContext,也可以說 application 才是 Web 應用中貨真價實的單例模式。
websocket
websocket 的作用域是 WebSocket ,即在整個 WebSocket 中有效
equest、session、application、websocket 和 global Session 作用域只能在 Web 環境下使用,如果使用 ClassPathXmlApplicationContext 加載這些作用域中的任意一個的 Bean,就會拋出以下異常。
2.3.4.生命周期
-
Spring 啟動,查找並加載需要被 Spring 管理的 Bean,並實例化 Bean。
-
利用依賴注入完成 Bean 中所有屬性值的配置注入。
-
如果 Bean 實現了 BeanNameAware 接口,則 Spring 調用 Bean 的 setBeanName() 方法傳入當前 Bean 的 id 值。
-
如果 Bean 實現了 BeanFactoryAware 接口,則 Spring 調用 setBeanFactory() 方法傳入當前工廠實例的引用。
-
如果 Bean 實現了 ApplicationContextAware 接口,則 Spring 調用 setApplicationContext() 方法傳入當前 ApplicationContext 實例的引用。
-
如果 Bean 實現了 [BeanPostProcessor] 接口,則 Spring 調用該接口的預初始化方法 postProcessBeforeInitialzation() 對 Bean 進行加工操作,此處非常重要,Spring 的 AOP 就是利用它實現的。
-
如果 Bean 實現了 InitializingBean 接口,則 Spring 將調用 afterPropertiesSet() 方法。
-
如果在配置文件中通過 init-method 屬性指定了初始化方法,則調用該初始化方法。
-
如果 [BeanPostProcessor ]和 Bean 關聯,則 Spring 將調用該接口的初始化方法 postProcessAfterInitialization()。此時,Bean 已經可以被應用系統使用了。
-
如果在
中指定了該 Bean 的作用域為 singleton,則將該 Bean 放入 Spring IoC 的緩存池中,觸發 Spring 對該 Bean 的生命周期管理; 如果在 中指定了該 Bean 的作用域為 prototype,則將該 Bean 交給調用者,調用者管理該 Bean 的生命周期,Spring 不再管理該 Bean。 -
如果 Bean 實現了 DisposableBean 接口,則 Spring 會調用 destory() 方法銷毀 Bean;如果在配置文件中通過 destory-method 屬性指定了 Bean 的銷毀方法,則 Spring 將調用該方法對 Bean 進行銷毀。
2.3.4.1.單例
public class UserBean {
private String name;
public UserBean(){
System.out.println("UserBean()構造函數");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("setName()");
this.name = name;
}
public void init(){
System.out.println("this is init of UserBean");
}
public void destory(){
System.out.println("this is destory of UserBean " + this);
}
}
<bean id="user_singleton" class="com.wyl.userBean" scope="singleton"
init-method="init" destroy-method="destory" lazy-init="true"/>
當scope="singleton",即默認情況下,會在啟動容器時(即實例化容器時)時實例化。但我們可以指定Bean節點的lazy-init="true"來延遲初始化bean,這時候,只有在第一次獲取bean時才會初始化bean,即第一次請求該bean時才初始化.
如果想對所有的默認單例bean都應用延遲初始化,可以在根節點beans設置default-lazy-init屬性為true,如下所示:
<beans default-lazy-init="true">
public class LifeTest {
@Test
public void test() {
AbstractApplicationContext container =
new ClassPathXmlApplicationContext("user.xml");
UserBean user = (UserBean)container.getBean("user_singleton");
System.out.println(user);
container.close();
}
}
UserBean()構造函數
this is init of UserBean
com.wyl.UserBean@573f2bb1
……
this is destory of UserBeancom.wyl.UserBean@573f2bb1
默認情況下,Spring在讀取xml文件的時候,就會創建對象。在創建對象的時候先調用構造器[UserBean(),然后調用init-method屬性值中所指定的方法。對象在被銷毀的時候,會調用destroy-method屬性值中所指定的方法.
2.3.4.2.非單例管理的對象
當scope="prototype"時,容器也會延遲初始化bean,Spring讀取xml文件的時候,並不會立刻創建對象,而是在第一次請求該bean時才初始化(如調用getBean方法時)。
在第一次請求每一個prototype的bean時,Spring容器都會調用其構造器創建這個對象,然后調用init-method屬性值中所指定的方法。對象銷毀的時候,Spring容器不會幫我們調用任何方法,因為是非單例,這個類型的對象有很多個,Spring容器一旦把這個對象交給你之后,就不再管理這個對象了。
<bean id="user_prototype" class="com.bean.UserBean" scope="prototype" init-method="init" destroy-method="destroy"/>
public class UserTest {
@Test
public void test() {
AbstractApplicationContext container = new ClassPathXmlApplicationContext("User.xml");
UserBean User1 = (UserBean)container.getBean("User_singleton");
System.out.println(User1);
UserBean User2 = (UserBean)container.getBean("User_prototype");
System.out.println(User2);
container.close();
}
}
結果
UserBean()構造函數
this is init of UserBean
com.wyl.UserBean@573f2bb1
LifeBean()構造函數
this is init of UserBean
com.wyl.UserBean@5ae9a829
……
this is destory of lifeBean com.wyl.UserBean@573f2bb1
2.4.DI(依賴注入)
依賴注入Dependency Injection,在解耦的過程中,我們將對象的創建交給Spring容器管理,當我們需要用其他類的對象,由Spring提供,我們只需在配置文件里聲明即可。A類使用B類,就產生依賴關系,Spring給我們解決依賴關系就是依賴注入(DI)
2.4.1.構造器注入
private String name;
private Integer age;
private Date birthday;
// 構造函數
public UserServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
<!-- name:按字段名稱輔助;index:字段索引,給第幾個字段賦值;type:指定注入值的類型,該類型也是構造函數中某個或某些字段的類型; -->
<!-- value:要注入的值,基本類型和String;ref:注入其他類型數據,指向外部bean對象;這個外部bean需要存在於Spring容器 -->
<bean id="userService" class="org.service.impl.UserServiceImpl">
<constructor-arg name="name" value="張三"/>
<constructor-arg name="age" value="12"/>
<constructor-arg name="birthday" ref="date"/>
</bean>
<!-- 創建日期對象 -->
<bean id="date" class="java.util.Date"/>
2.4.2.Set方式注入
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
<bean id="userService2" class="org.service.impl.UserServiceImpl2">
<property name="name" value="李四"/>
<property name="age" value="12"/>
<property name="birthday" ref="date"/>
</bean>
<!-- 創建日期對象 -->
<bean id="date" class="java.util.Date"/>
2.4.3.對象類型注入
<!-- 注入對象類型屬性 -->
<!-- 1 配置service和dao對象 -->
<bean id="userDao" class="cn.ioc.UserDao"></bean>
<bean id="userService" class="cn.ioc.UserService">
<!-- 注入dao對象-->
<property name="userDao" ref="userDao"></property>
</bean>
2.4.4.復雜類型注入
<!-- 注入復雜類型屬性值 -->
<bean id="person" class="cn.property.Person">
<!-- 數組 -->
<property name="arrs">
<list>
<value>小王</value>
<value>小馬</value>
<value>小宋</value>
</list>
</property>
<!-- list -->
<property name="list">
<list>
<value>小奧</value>
<value>小金</value>
<value>小普</value>
</list>
</property>
<!-- map -->
<property name="map">
<map>
<entry key="aa" value="lucy"></entry>
<entry key="bb" value="mary"></entry>
<entry key="cc" value="tom"></entry>
</map>
</property>
<!-- properties -->
<property name="properties">
<props>
<prop key="driverclass">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
</props>
</property>
</bean>
<!--set-->
<property name="set">
<set>
<value>LOL</value>
<value>COC</value>
<value>WOW</value>
</set>
</property>
<!--null-->
<property name="marne">
<null/>
</property>
2.4.5.拓展方式注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
p命名注入 property
<!-- p命名空間注入,可以直接注入屬性的值:property -->
<bean id="User" class="com.wyl.pojo.User" p:name ="老秦" p:age ="18"/>
c命名空間注入 constructor
<!-- c命名空間注入,通過構造器注入:construct-args -->
<bean id="User2" class="com.wyl.pojo.User" c:age="18" c:name="老李"/>
注意點:p命名和c命名不能直接使用,需要導入xml約束
2.5.自動裝配
自動裝配是Spring滿足bean依賴的一種方式!Spring會在上下文中自動尋找,並自動給bean裝配屬性。
在Spring中有三種裝配的方式
-
在xml中顯示的配置
-
在java中顯示配置
-
隱式的自動裝配bean
名稱 說明 no 默認值,表示不使用自動裝配,Bean 依賴必須通過 ref 元素定義。 byName 根據 Property 的 name 自動裝配,如果一個 Bean 的 name 和另一個 Bean 中的 Property 的 name 相同,則自動裝配這個 Bean 到 Property 中。 byType 根據 Property 的數據類型(Type)自動裝配,如果一個 Bean 的數據類型兼容另一個 Bean 中 Property 的數據類型,則自動裝配。 constructor 類似於 byType,根據構造方法參數的數據類型,進行 byType 模式的自動裝配。 autodetect(3.0版本不支持) 如果 Bean 中有默認的構造方法,則用 constructor 模式,否則用 byType 模式。
2.5.1.byName
<!--
byName:會自動在容器上下文中查找,和自己對象set方法后面的值對應的beanid!
-->
<bean id="people" class="com.wyl.pojo.People" autowire="byName">
<property name="name" value="wangyanling"/>
</bean>
2.5.2.byType
<bean id="cat" class="com.wyl.pojo.Cat"/>
<bean id="dog" class="com.wyl.pojo.Dog"/>
<!--
byName:會自動在容器上下文中查找,和自己對象set方法后面的值對應的beanid!
byType:會自動在容器上下文中查找,和自己對象屬性類型相同的bean!
-->
<bean id="people" class="com.wyl.pojo.People" autowire="byType">
<property name="name" value="WANGAYNLING"/>
</bean>
2.5.3.注解
jdk1.5支持的注解,Spring2.5就支持注解了!
要使用注解須知:
-
導入約束 context約束
-
配置注解的支持: context:annotation-config/
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired
@Autowired是按類型自動轉配的,不支持id匹配。byType
需要導入 spring-aop的包!
直接在屬性上使用即可!也可以在set方式上使用!
使用Autowired我們可以不用編寫Set方法了,前提是這個自動裝配的屬性在IOC容器中存在,且符合名字byname。
@Autowired
private Cat cat;
@Autowired
private Dog dog;
@Nullable // 字段標記了這個注解,說明這個字段可以為null
或者 如果顯示定義了Autowired的required 的屬性為false ,說明這個對象可以為null,允許為空
autowired 注解應該是只能是別的,當注入 在IOC容器中該類型只有一個時,就通過byType進行裝配,當注入容器存在多個同意類型的對象是,就是根據byName進行裝配
如果@Autowired自動裝配的環境比較復雜,自動裝配無法通過一個注解[@Autowired]完成的時候,我們可以使用@Qualifier(value=“XXX”)去配置@Autowired的使用,指定一個唯一的bean對象注入。
@Qualifier
@Autowired是根據類型自動裝配的,加上@Qualifier則可以根據byName的方式自動裝配
@Qualifier不能單獨使用。
public class People {
private String name;
@Autowired
@Qualifier("cat")
private Cat cat;
@Autowired
@Qualifier("dog")
private Dog dog;
}
@Resource注解
- @Resource如有指定的name屬性,先按該屬性進行byName方式查找裝配;
- 其次再進行默認的byName方式進行裝配;
- 如果以上都不成功,則按byType的方式自動裝配。
- 都不成功,則報異常。
public class People {
private String name;
@Resource(name = "cat")
private Cat cat;
@Resource(name = "dog")
private Dog dog;
小結
@Autowired與@Resource異同:
@Autowired與@Resource都可以用來裝配bean。都可以寫在字段上,或寫在setter方法上。
@Autowired默認按類型裝配(屬於spring規范),默認情況下必須要求依賴對象必須存在,如果要允許null 值,可以設置它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier注解進行使用
@Resource(屬於J2EE復返),默認按照名稱進行裝配,名稱可以通過name屬性進行指定。如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行按照名稱查找,如果注解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是 需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
它們的作用相同都是用注解方式注入對象,但執行順序不同。@Autowired先byType,@Resource先byName。
2.6.ioc注解
@注解名稱(屬性名稱=屬性值)
2.6.1. Spring使用的注解大全和解釋
注解 | 解釋 |
---|---|
@Controller | 組合注解(組合了@Component注解),應用在MVC層(控制層),DispatcherServlet會自動掃描注解了此注解的類,然后將web請求映射到注解了@RequestMapping的方法上。 |
@Service | 組合注解(組合了@Component注解),應用在service層(業務邏輯層) |
@Repository | 組合注解(組合了@Component注解),應用在dao層(數據訪問層) |
@Component | 表示一個帶注釋的類是一個“組件”,成為Spring管理的Bean。當使用基於注解的配置和類路徑掃描時,這些類被視為自動檢測的候選對象。同時@Component還是一個元注解。 |
@Autowired | Spring提供的工具(由Spring的依賴注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自動注入。) |
@Resource | JSR-250提供的注解 |
@Inject | JSR-330提供的注解 |
@Configuration | 聲明當前類是一個配置類(相當於一個Spring配置的xml文件) |
@ComponentScan | 自動掃描指定包下所有使用@Service,@Component,@Controller,@Repository的類並注冊 |
@Bean | 注解在方法上,聲明當前方法的返回值為一個Bean。返回的Bean對應的類中可以定義init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定義,在構造之后執行init,在銷毀之前執行destroy。 |
@Aspect | 聲明一個切面(就是說這是一個額外功能) |
@After | 后置建言(advice),在原方法前執行。 |
@Before | 前置建言(advice),在原方法后執行。 |
@Around | 環繞建言(advice),在原方法執行前執行,在原方法執行后再執行(@Around可以實現其他兩種advice) |
@PointCut | 聲明切點,即定義攔截規則,確定有哪些方法會被切入 |
@Transactional | 聲明事務(一般默認配置即可滿足要求,當然也可以自定義) |
@Cacheable | 聲明數據緩存 |
@EnableAspectJAutoProxy | 開啟Spring對AspectJ的支持 |
@Value | 值得注入。經常與Sping EL表達式語言一起使用,注入普通字符,系統屬性,表達式運算結果,其他Bean的屬性,文件內容,網址請求內容,配置文件屬性值等等 |
@PropertySource | 指定文件地址。提供了一種方便的、聲明性的機制,用於向Spring的環境添加PropertySource。與@configuration類一起使用。 |
@PostConstruct | 標注在方法上,該方法在構造函數執行完成之后執行。 |
@PreDestroy | 標注在方法上,該方法在對象銷毀之前執行。 |
@Profile | 表示當一個或多個指定的文件是活動的時,一個組件是有資格注冊的。使用@Profile注解類或者方法,達到在不同情況下選擇實例化不同的Bean。@Profile(“dev”)表示為dev時實例化。 |
@EnableAsync | 開啟異步任務支持。注解在配置類上。 |
@Async | 注解在方法上標示這是一個異步方法,在類上標示這個類所有的方法都是異步方法。 |
@EnableScheduling | 注解在配置類上,開啟對計划任務的支持。 |
@Scheduled | 注解在方法上,聲明該方法是計划任務。支持多種類型的計划任務:cron,fixDelay,fixRate |
@Conditional | 根據滿足某一特定條件創建特定的Bean |
@Enable* | 通過簡單的@Enable來開啟一項功能的支持。所有@Enable注解都有一個@Import注解,@Import是用來導入配置類的,這也就意味着這些自動開啟的實現其實是導入了一些自動配置的Bean(1.直接導入配置類2.依據條件選擇配置類3.動態注冊配置類) |
@RunWith | 這個是Junit的注解,springboot集成了junit。一般在測試類里使用:@RunWith(SpringJUnit4ClassRunner.class) — SpringJUnit4ClassRunner在JUnit環境下提供Sprng TestContext Framework的功能 |
@ContextConfiguration | 用來加載配置ApplicationContext,其中classes屬性用來加載配置類:@ContextConfiguration(classes = {TestConfig.class(自定義的一個配置類)}) |
@ActiveProfiles | 用來聲明活動的profile–@ActiveProfiles(“prod”(這個prod定義在配置類中)) |
@EnableWebMvc | 用在配置類上,開啟SpringMvc的Mvc的一些默認配置:如ViewResolver,MessageConverter等。同時在自己定制SpringMvc的相關配置時需要做到兩點:1.配置類繼承WebMvcConfigurerAdapter類2.就是必須使用這個@EnableWebMvc注解。 |
@RequestMapping | 用來映射web請求(訪問路徑和參數),處理類和方法的。可以注解在類和方法上,注解在方法上的@RequestMapping路徑會繼承注解在類上的路徑。同時支持Serlvet的request和response作為參數,也支持對request和response的媒體類型進行配置。其中有value(路徑),produces(定義返回的媒體類型和字符集),method(指定請求方式)等屬性。 |
@ResponseBody | 將返回值放在response體內。返回的是數據而不是頁面 |
@RequestBody | 允許request的參數在request體中,而不是在直接鏈接在地址的后面。此注解放置在參數前。 |
@PathVariable | 放置在參數前,用來接受路徑參數。 |
@RestController | 組合注解,組合了@Controller和@ResponseBody,當我們只開發一個和頁面交互數據的控制層的時候可以使用此注解。 |
@ControllerAdvice | 用在類上,聲明一個控制器建言,它也組合了@Component注解,會自動注冊為Spring的Bean。 |
@ExceptionHandler | 用在方法上定義全局處理,通過他的value屬性可以過濾攔截的條件:@ExceptionHandler(value=Exception.class)–表示攔截所有的Exception。 |
@ModelAttribute | 將鍵值對添加到全局,所有注解了@RequestMapping的方法可獲得次鍵值對(就是在請求到達之前,往model里addAttribute一對name-value而已)。 |
@InitBinder | 通過@InitBinder注解定制WebDataBinder(用在方法上,方法有一個WebDataBinder作為參數,用WebDataBinder在方法內定制數據綁定,例如可以忽略request傳過來的參數Id等)。 |
@WebAppConfiguration | 一般用在測試上,注解在類上,用來聲明加載的ApplicationContext是一個WebApplicationContext。他的屬性指定的是Web資源的位置,默認為src/main/webapp,我們可以修改為:@WebAppConfiguration(“src/main/resources”)。 |
@EnableAutoConfiguration | 此注釋自動載入應用程序所需的所有Bean——這依賴於Spring Boot在類路徑中的查找。該注解組合了@Import注解,@Import注解導入了EnableAutoCofigurationImportSelector類,它使用SpringFactoriesLoader.loaderFactoryNames方法來掃描具有META-INF/spring.factories文件的jar包。而spring.factories里聲明了有哪些自動配置。 |
@SpingBootApplication | SpringBoot的核心注解,主要目的是開啟自動配置。它也是一個組合注解,主要組合了@Configurer,@EnableAutoConfiguration(核心)和@ComponentScan。可以通過@SpringBootApplication(exclude={想要關閉的自動配置的類名.class})來關閉特定的自動配置。 |
@ImportResource | 雖然Spring提倡零配置,但是還是提供了對xml文件的支持,這個注解就是用來加載xml配置的。例:@ImportResource({“classpath |
@ConfigurationProperties | 將properties屬性與一個Bean及其屬性相關聯,從而實現類型安全的配置。例:@ConfigurationProperties(prefix=”authot”,locations={“classpath |
@ConditionalOnBean | 條件注解。當容器里有指定Bean的條件下。 |
@ConditionalOnClass | 條件注解。當類路徑下有指定的類的條件下。 |
@ConditionalOnExpression | 條件注解。基於SpEL表達式作為判斷條件。 |
@ConditionalOnJava | 條件注解。基於JVM版本作為判斷條件。 |
@ConditionalOnJndi | 條件注解。在JNDI存在的條件下查找指定的位置。 |
@ConditionalOnMissingBean | 條件注解。當容器里沒有指定Bean的情況下。 |
@ConditionalOnMissingClass | 條件注解。當類路徑下沒有指定的類的情況下。 |
@ConditionalOnNotWebApplication | 條件注解。當前項目不是web項目的條件下。 |
@ConditionalOnResource | 條件注解。類路徑是否有指定的值。 |
@ConditionalOnSingleCandidate | 條件注解。當指定Bean在容器中只有一個,后者雖然有多個但是指定首選的Bean。 |
@ConditionalOnWebApplication | 條件注解。當前項目是web項目的情況下。 |
@EnableConfigurationProperties | 注解在類上,聲明開啟屬性注入,使用@Autowired注入。例:@EnableConfigurationProperties(HttpEncodingProperties.class)。 |
@AutoConfigureAfter | 在指定的自動配置類之后再配置。例:@AutoConfigureAfter(WebMvcAutoConfiguration.class) |
2.6.1.1.創建對象的注解
- @Component(標注當前類是Spring容器中的一個組件)
- @Repository(一般用於持久層)
- @Service(一般用於業務層)
- @Controller(一般用於表現層)
2.6.1.2、注入數據的注解
-
@Autowired:自動按類型注入,常用在變量上;如果容器中有唯一一個類型與注解的變量類型相同則可以自動注入成功。當有多個bean匹配則按照變量名稱去查找,找不到則注入失敗。
-
@Qualifier("userDaoImpl"):結合@Autowired使用,注入指定名稱的bean;在類的成員變量上不能單獨使用;在方法參數里使用可以單獨使用;
-
@Resource:相當於@Autowired自動注入,而@Resource(name="xxx")注入指定的bean,相當於同時使用@Autowired和@Qualifier("userDaoImpl")兩個注解。
上面三個注解都只能注入其他的bean類型,不能注入基本數據類型和String和復雜類型;復雜類型只能通過xml文件來注入。
-
@Value:注入基本數據類型和String類型。指定數據的值,寫法:${表達式}。
2.6.1.3、改變作用范圍的注解
- @Scope:取值有singleton單例(默認)和prototype多例
2.6.1.4、和生命周期相關注解
@PostConstruct
public void init() {
System.out.println("初始化注解");
}
@PreDestroy
public void destroy() {
System.out.println("銷毀注解");
}
這兩個注解和bean標簽里面的init-method、destroy-method作用相同。
2.6.1.5.新注解
-
@Configuration:作用在類上面標明當前類是一個配置類
-
@ComponentScan(basePackages = "com.wyl"):掃描包注解:相當於下面這一行配置
<!--<context:component-scan base-package="com.wyl"/>-->
-
@Bean:在配置類中寫在方法上,將方法返回的對象注入到Spring容器中。該注解的方法有參數時,會去容器中找bean對象,跟@Autowired注解一樣的。
-
@PropertySource("classpath:db.properties"):指定數據庫配置文件的位置
-
@Import:存在多個配置文件,用該注解引入其他配置文件。
2.6.1.6.Spring測試注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class) 純注解
// @ContextConfiguration(locations = "classpath:ApplicationContext.xml") xml配置文件
public class SpringTest {
@Autowired
private AccountServiceImpl accountService;
@Test
public void findAll(){
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
}
@RunWith(SpringJUnit4ClassRunner.class):替換掉原來junit的runner執行方法,使用Spring自己的執行方法。
@ContextConfiguration(classes = ApplicationConfig.class):如果是使用注解創建Spring的容器使用classes;
@ContextConfiguration(locations = "classpath:ApplicationContext.xml"):使用xml配置文件的方法
2.6.2.基於xml方式創建bean
public class User {
private Integer id;
private String name;
}
<?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.wyl.bean.User" >
<property name="id" value="1"></property>
<property name="name" value="wyl"></property>
</bean>
</beans>
@test
public void UserTest{
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("Bean.xml");
User userInfo=(User)context.getBean("user");
System.out.println(userInfo);
}
2.6.3.基於@Configuration 和@Bean 注解
Configuration 配置類
@Configuration
public class MyTestConfig {
//bean的id默認為方法名
@Bean
public User user(){
User user =new User();
user.setName("王延領");
user.setId(2);
return user;
}
}
@test
public void UserTest{
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(MyTestConfig.class);
User userInfo=(User)context.getBean("user");
System.out.println(userInfo.toString());
}
3.Spring核心AOP
AOP(Aspect Oriented Programming):面向切面編程,在不修改源代碼的情況下增強代碼的功能。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
3.1.AOP實現原理代理模式
代理模式,創建一個代理對象實現和被對代理對象相同的接口,這樣就擁有和被代理對象相同的功能,在這基礎上增強原有的方法。
- 靜態代理,手動去實現一個代理類
- 動態代理,通過反射動態的實現代理類
3.1.1 靜態代理
步驟:
-
抽象角色 : 一般使用接口或者抽象類來實現
public interface Rent { public void rent(); }
-
真實角色 : 被代理的角色
public class Host implements Rent{ @Override public void rent() { System.out.println("房東出租房子!"); } }
-
代理角色 : 代理真實角色 ; 代理真實角色后 , 一般會做一些附屬的操作 .
public class Proxy { private Host host; public Proxy(){ } public Proxy(Host host){ this.host=host; } public void rent(){ seeHouse(); host.rent(); hetong(); fare(); } public void seeHouse(){ System.out.println("中介帶你看房"); } public void fare(){ System.out.println("收中介費!"); } public void hetong(){ System.out.println("簽租領合同"); } }
-
客戶 : 使用代理角色來進行一些操作 .
public class Client {
public static void main(String[] args) {
Host host=new Host();
//host.rent();
Proxy proxy=new Proxy(host);
proxy.rent();
}
}
好處:
- 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
- 公共的業務由代理來完成 . 實現了業務的分工 ,
- 公共業務發生擴展時變得更加集中和方便 .
缺點 :
- 類多了 , 多了代理類 , 工作量變大了 . 開發效率降低 .
我們想要靜態代理的好處,又不想要靜態代理的缺點,所以 , 就有了動態代理
3.1.2.動態代理
動態代理的代理類是動態生成的 . 靜態代理的代理類是我們提前寫好的
動態代理分為兩類 :
-
基於接口的動態代理----JDK動態代理
//抽象角色:租房 public interface Rent { public void rent(); } //真實角色: 房東,房東要出租房子 public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); } } //代理:中介 public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理類,重點是第二個參數,獲取要代理的抽象角色!之前都是一個角色,現在可以代理一類角色 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); } // proxy : 代理類 method : 代理類的調用處理程序的方法對象. // 處理代理實例上的方法調用並返回結果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //核心:本質利用反射實現! Object result = method.invoke(rent, args); fare(); return result; } //看房 public void seeHouse(){ System.out.println("帶房客看房"); } //收中介費 public void fare(){ System.out.println("收中介費"); } } //租客 public class Client { public static void main(String[] args) { //真實角色 Host host = new Host(); //代理實例的調用處理程序 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setRent(host); //將真實角色放置進去! Rent proxy = (Rent)pih.getProxy(); //動態生成對應的代理類! proxy.rent(); } }
-
基於類的動態代理–cglib
// 被代理的對象
Account account = new Account();
Account o = (Account) Enhancer.create(account.getClass(), new MethodInterceptor() {
/**
* 被代理對象的方法執行前會執行
* @param obj 被代理的對象
* @param method 方法
* @param objects 參數
* @param methodProxy 當前執行方法的代理的對象
* @return 和被代理對象的方法相同的返回值
* @throws Throwable 異常
*/
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("增強前...");
Object invoke = method.invoke(account, objects);
System.out.println("增強后...");
return invoke;
}
});
o.findAll();
3.2.AOP術語
- Joinpoint(連接點):指的是方法,可以被動態代理增強的方法就是連接點,Spring只支持方法類型的連接點
- Pointcut(切入點):定義要對哪些Joinpoint連接點(方法)進行攔截增強功能。被增強的方法叫做切入點,所有的方法都可以看做是一個連接點。只有被增強了的方法才叫做切入點。
- Advice(通知/增強):攔截到Jointpoint(連接點)之后要做的事情就是通知。通知的類型:前置通知、后置通知、最終通知、環繞通知、異常通知。
- Introduction(引介):一種特殊的通知,在不修改代碼的前提下,可以在運行期為類動態的添加一些方法或字段。
- Target(目標對象):代理的目標對象
- Weaving(織入):是把增強 應用到 目標對象來創建新的代理對象的過程(添加新功能代碼的過程)。Spring采用的是動態代理織入,而AspectJ采用編譯期和類裝載織入。
- Proxy(代理):一個類被AOP織入增強后,就產生一個結果代理類。
- Aspect(切面):是切入點和通知(引介)的結合。
3.3.使用Spring實現Aop
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
3.3.1.通過 Spring API 實現
//接口與業務
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加用戶");
}
@Override
public void delete() {
System.out.println("刪除用戶");
}
@Override
public void update() {
System.out.println("更新用戶");
}
@Override
public void search() {
System.out.println("查詢用戶");
}
}
//增強
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被調用的方法
//args 被調用的方法的對象的參數
//target 被調用的目標對象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注冊bean-->
<bean id="userService" class="com.wyl.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入點 expression:表達式匹配要執行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.wyl.service.UserServiceImpl.*(..))"/>
<!--執行環繞; advice-ref執行方法 . pointcut-ref切入點-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
3.3.2.自定義類來實現Aop
//切入類
public class DiyPointcut {
public void before(){
System.out.println("---------方法執行前---------");
}
public void after(){
System.out.println("---------方法執行后---------");
}
}
<!--第二種方式自定義實現-->
<!--注冊bean-->
<bean id="diy" class="com.wyl.config.DiyPointcut"/
<!--aop的配置-->
<aop:config>
<!--第二種方式:使用AOP的標簽實現-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.wyl.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
3.3.3.使用注解實現AOP
//注解實現的增強類
package com.wyl.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.wyl.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法執行前---------");
}
@After("execution(* com.wyl.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法執行后---------");
}
@Around("execution(* com.wyl.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環繞前");
System.out.println("簽名:"+jp.getSignature());
//執行目標方法proceed
Object proceed = jp.proceed();
System.out.println("環繞后");
System.out.println(proceed);
}
}
4.事務和JdbcTemplate
4.1.JdbcTemplate使用
入門案例:
// Spring自帶的數據源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("root");
JdbcTemplate template = new JdbcTemplate(dataSource);
List<Account> accountList = template.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
for (Account account : accountList) {
System.out.println(account);
}
具體增刪改查用法:
@Autowired
private JdbcTemplate jdbcTemplate;
// 添加
@Test
public void insert(){
String sql = "insert into account(name, money) VALUES (?,?)";
Account account1 = new Account();
account1.setName("迪迦");
account1.setMoney(10000F);
jdbcTemplate.update(sql, account1.getName(), account1.getMoney());
find();
}
// 刪除
@Test
public void delete(){
String sql = "delete from account where id = ?";
jdbcTemplate.update(sql, 6);
find();
}
// 更新
@Test
public void update(){
List<Account> accounts = jdbcTemplate.query("select * from account where id = ?", new BeanPropertyRowMapper<>(Account.class), 1);
Account account = accounts.get(0);
account.setName("泰羅");
String sql = "update account set name = ? where id = ?";
jdbcTemplate.update(sql, account.getName(),account.getId());
find();
}
// 查詢所有
@Test
public void find(){
List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
for (Account account : accountList) {
System.out.println(account);
}
}
// 查詢一個bean
@Override
public Account findByName(String name) {
String sql = "select * from account where name = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), name);
}
// 查詢一個Object
@Test
public void findOne(){
String sql = "select count(id) from account";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(integer);
}
4.2.Spring配置事務
在Spring中有兩種方法管理事務:聲明式事務管理和編程式事務管理;
-
聲明式事務管理:
1、xml配置文件式:
1、配置事務管理器 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/></bean> 2、配置通知 <!-- 配置事務的通知/增強 --> <tx:advice id="interceptor"> <!-- 配置事務的屬性 --> <!-- isolation:事務隔離級別,默認使用數據庫的隔離級別 no-rollback-for:指定一個異常,除了該異常都回滾。 propagation:事務傳播行為,默認是required一定有事務,增刪改設置required,查詢設置supports read-only:是否只讀。只有查詢才能設置true。默認是false支持讀寫。 rollback-for:指定一個異常,出現該異常就回滾,其他異常不回滾。 timeout:事務超時時間,默認-1,永不超時。指定了以秒為單位。 --> <tx:attributes> <!-- 指定在哪種規則的方法上添加事務 --> <tx:method name="transfer*"/> </tx:attributes> </tx:advice> 3、事務管理器和切入點表達式關聯起來 <aop:config> <!-- service包下所有類的所有方法都添加事務 --> <aop:pointcut id="commonPointcut" expression="execution(* com.sample.service.*.*(..))"/> <!-- 將事務管理器和切入點表達式關聯起來 --> <aop:advisor advice-ref="interceptor" pointcut-ref="commonPointcut"/> </aop:config>
2、注解式:
1、配置事務管理器 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/></bean> 2、開啟對事務注解的支持 <!-- 開啟對事務注解的支持 --> <tx:annotation-driven/> 3、在要添加事物的類上添加注解:@Transactional
3、純注解式
@Configuration @ComponentScan("com.sample") // 相當於<tx:annotation-driven/> @EnableTransactionManagement @PropertySource("classpath:db.properties") public class AppConfig { @Value("${db.driver}") private String driver; @Value("${db.url}") private String url; @Value("${db.username}") private String username; @Value("${db.password}") private String password; @Bean public DruidDataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(){ return new JdbcTemplate(dataSource()); } @Bean public DataSourceTransactionManager transactionManager(){ return new DataSourceTransactionManager(dataSource()); } } ========== 在類上添加@Transactional注解即可
-
編程式事務管理:通過代碼去實現事務的管理,手動開啟事務、提交、回滾。