目錄
一、簡介
1、什么是spring?
2、目的
3、功能及使用范圍
二、spring IOC
1、ioc的理解
2、開發(fā)人員可達到的目的
3、分析實現(xiàn)
4、bean配置
三、spring IOC的注入方式
1、set方法屬性注入
2、構造注入
3、自動裝配
四、spring與tomcat的整合/spring與web容器的整合
五、spring AOP
1、aop的特點
2、AOP中關鍵性概念 ?
3、前置通知
1.1、準備工作
?1.2、前置通知
4、后置通知
5、環(huán)繞通知
6、異常通知
7、過濾通知
8、AOP的總結
8.1、概念
8.2、應用
六、spring中bean的生命周期
1、spring管理JavaBean初始化過程
1.1、思維導圖
1.2、spring容器管理JavaBean的初始化過程(spring中bean的生命周期)。
2、spring的JavaBean管理中單例模式及原型模式
2.1、spring中JavaBean是單例還是多例。
一、簡介
1、什么是spring?
Spring是一個開源的輕量級Java應用開發(fā)框架,它提供了一種簡單、高效、靈活的方式來構建企業(yè)級應用程序。Spring框架的核心特點是依賴注入(Dependency Injection)和面向切面編程(Aspect-Oriented Programming),它通過一組模塊化的組件提供全面的支持,使開發(fā)人員能夠快速搭建可擴展、可維護的應用。
2、目的
學習Spring的目的可以總結為以下幾點:
- 簡化開發(fā):Spring提供了一系列的工具和抽象,簡化了企業(yè)級應用開發(fā)過程。它通過依賴注入來解決對象間的依賴關系,使得代碼更加靈活、可測試和可維護。
- 高效開發(fā):Spring框架提供了諸多模塊,如Spring MVC、Spring Boot等,可以快速構建Web應用和微服務。它提供了一種約定優(yōu)于配置的開發(fā)方式,提高了開發(fā)效率。
- 提高可擴展性:Spring框架基于接口和模塊化設計,能夠方便地集成其他開源框架和庫。它的松耦合特性使得應用程序更易于擴展和維護。
- 豐富的功能:Spring擁有豐富的功能和特性,包括事務管理、安全性、緩存、消息隊列、調度等。它為開發(fā)人員提供了一系列解決方案,使得應用程序開發(fā)更加便捷。
3、功能及使用范圍
Spring的功能范圍非常廣泛,包括但不限于以下方面:
- 依賴注入和控制反轉:通過依賴注入,Spring容器能夠將對象之間的依賴關系管理起來,使得對象的創(chuàng)建和使用解耦,提高代碼的靈活性和可測試性。
- 面向切面編程:Spring提供了面向切面編程(AOP)的支持,可以將與核心業(yè)務邏輯無關的功能(如事務管理、日志記錄等)進行橫切,提高了代碼的重用性和可維護性。
- Web開發(fā)支持:Spring提供了Spring MVC模塊,用于開發(fā)Web應用程序。它能夠處理HTTP請求和響應,進行URL路由、數(shù)據(jù)綁定、表單驗證、視圖解析等操作,簡化了Web開發(fā)過程。
- 數(shù)據(jù)訪問支持:Spring框架提供了對各種數(shù)據(jù)訪問技術的支持,包括JDBC、ORM(如Hibernate、MyBatis)、JPA等。它通過抽象出一套統(tǒng)一的數(shù)據(jù)訪問接口,使得數(shù)據(jù)庫訪問更加方便和可替換。
- 安全性支持:Spring提供了一套強大的安全性框架,可以進行認證和授權管理。它支持各種身份驗證方式,如基于表單的認證、基于角色的訪問控制等,保護應用程序的安全性。
總之,Spring框架是Java開發(fā)領域最流行的框架之一,它提供了豐富的功能和特性,幫助開發(fā)人員構建可靠、高效的企業(yè)級應用程序。
一句話概括,Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器框架。
二、spring IOC
1、ioc的理解
IoC(Inversion of Control,控制反轉)是Spring框架的核心概念之一,也是實現(xiàn)依賴注入的基礎。它通過解耦對象之間的依賴關系,使得對象的創(chuàng)建和管理由框架來負責,而不是由開發(fā)人員手動管理。
在傳統(tǒng)的程序設計中,對象之間的依賴關系通常由開發(fā)人員在代碼中進行硬編碼,對象直接通過關鍵字(如new)來創(chuàng)建和管理。這種方式存在一些問題,如對象之間的耦合度高、可測試性差、擴展性差等。
而IoC則是一種思想上的轉變,它將對象的創(chuàng)建和管理權利交給了框架。具體來說,IoC的核心思想是:通過配置或注解,告訴框架需要創(chuàng)建哪些對象,以及對象之間的依賴關系。然后,框架在應用程序運行時根據(jù)配置信息動態(tài)地創(chuàng)建和組裝對象。
Spring通過IoC容器來實現(xiàn)控制反轉。IoC容器是一個用于管理對象的容器,它負責創(chuàng)建、組裝、初始化和銷毀對象。開發(fā)人員只需要配置對象的創(chuàng)建和依賴關系,然后通過容器來獲取需要的對象即可。
2、開發(fā)人員可達到的目的
- 解耦對象之間的依賴關系:通過IoC容器,開發(fā)人員只需要關注對象的功能實現(xiàn),而不需要關心對象是如何創(chuàng)建和組裝的。對象之間的依賴關系由容器負責管理,降低了對象之間的耦合度。
- 提高代碼的可測試性:由于對象的創(chuàng)建和組裝由容器來完成,開發(fā)人員可以很容易地對對象進行替換或模擬,從而實現(xiàn)單元測試和集成測試。
- 增強代碼的可擴展性:當需要添加新的功能或模塊時,只需要配置新的對象和依賴關系,而不需要修改現(xiàn)有的代碼。通過配置方式,可以方便地在不影響現(xiàn)有代碼的情況下進行擴展。
- 提高代碼的靈活性:IoC容器使得對象的創(chuàng)建完全可配置化,可以在運行時根據(jù)需要創(chuàng)建不同的實例。同時,框架提供了生命周期管理和依賴解析等功能,使得對象的管理更加便捷。
總而言之,IoC是Spring框架的核心特性之一,它通過控制反轉的思想,將對象的創(chuàng)建和依賴關系的管理交給了框架,減少了對象之間的耦合度,提高了代碼的可測試性和可擴展性。
3、分析實現(xiàn)
首先創(chuàng)建好我們的maven項目
?設置我們的pom.xml
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- 2、導入spring依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!-- 5.1、junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- 5.2、servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet.version}</version> <scope>provided</scope> </dependency>
新建
我們用原來的方式寫一下
- 編寫一個接口
public interface UserService { public void updUser(); }
- 再編寫一個實現(xiàn)類
public class UserServiceImpl1 implements UserService { public void updUser() { System.out.println("修改SQL用戶數(shù)據(jù)"); }
我們原來的方式是這樣寫的
-
現(xiàn)在我們添加一個實現(xiàn)類
public class UserServiceImpl2 implements UserService { public void updUser() { System.out.println("修改SQL用戶數(shù)據(jù)"); //修改用戶姓名 System.out.println("修改用戶姓名"); } }
-
當我們測試調方法的時候只需要改動后面的實現(xiàn)類
-
手動實例化的弊端:
1、一旦依賴的接口實現(xiàn)需要大批量改動,迭代,維護成本高
?2、當接口的實現(xiàn)類不統(tǒng)一,維護成本更高
我們怎么解決這個問題呢?
4、bean配置
首先我們要在maven項目里面的resources文件里面建立一個基于spring的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>
?我們在web文件里面建立兩個Class類
public class UserAction { private UserService us; public UserService getUs() { return us; } public void setUs(UserService us) { this.us = us; } public String updUser() { us.updUser(); return "list"; } }
public class GoodsAction { private UserService us; public UserService getUs() { return us; } public void setUs(UserService us) { this.us = us; } public String updUser() { us.updUser(); return "list"; } }
編寫一個測試類
public class demo1 { public static void main(String[] args) { // 加載spring核心配置文件(建模),獲取spring的上下文對象,上下文對象中可以獲取任何JavaBean對象 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); UserAction userAction = (UserAction) context.getBean("userAction"); userAction.updUser(); GoodsAction goodsAction = (GoodsAction) context.getBean("goodsAction"); goodsAction.updUser(); } }
在我們的新建的這個spring-context.xml文件里面添加<bean>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--凡是在spring核心配置文件spring-context.xml中配置,那么該類JavaBean就交給了spring容器管理--> <bean class="com.tgq.ioc.web.UserAction" id="userAction"> <property name="us" ref="userService1"/> </bean> <bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction"> <property name="us" ref="userService2"/> </bean> <bean class="com.tgq.ioc.service.impl.UserServiceImpl1" id="userService1"/> <bean class="com.tgq.ioc.service.impl.UserServiceImpl2" id="userService2"/> </beans>
?
三、spring IOC的注入方式
spring的ioc有哪些注入方式呢?
- set方法屬性注入
- 構造注入
- 接口注入/自動裝配
1、set方法屬性注入
在我們前面的GoodsAction新添幾個屬性和get、set方法,一個輸出打印的方法toPrint()
public class GoodsAction { private UserService us; private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getPeoples() { return peoples; } public void setPeoples(List<String> peoples) { this.peoples = peoples; } private List<String> peoples; public UserService getUs() { return us; } public void setUs(UserService us) { this.us = us; } public String updUser() { us.updUser(); return "list"; } public void toPrint() { System.out.println(this.name); System.out.println(this.age); System.out.println(this.peoples); } }
在print-context.xml里面設置值,使用set方法屬性注入用property
<bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction"> <property name="us" ref="userService2"> </property> <property name="name" value="旺財"/> <property name="age" value="20"/> <property name="peoples"> <list> <value>留守兒童</value> <value>情侶</value> <value>留守老人</value> </list> </property> </bean>
在測試類里面調用
goodsAction.toPrint();
2、構造注入
在我們的User Action里面添加屬性及有參無參的構造方法,一個輸出打印的方法toPrint()
public class UserAction { private UserService us; private String name; private int age; private List<String> hobby; public UserAction() { } public UserAction(String name, int age, List<String> hobby) { this.name = name; this.age = age; this.hobby = hobby; } public UserService getUs() { return us; } public void setUs(UserService us) { this.us = us; } public String updUser() { us.updUser(); return "list"; } public void toPrint() { System.out.println(this.name); System.out.println(this.age); System.out.println(this.hobby); } }
在print-context.xml里面設置值,使用構造輸入需要用constructor-arg標簽
<bean class="com.tgq.ioc.web.UserAction" id="userAction"> <property name="us" ref="userService1"/> <constructor-arg name="name" value="l"/> <constructor-arg name="age" value="18"/> <constructor-arg name="hobby"> <list> <value>唱</value> <value>跳</value> <value>rap</value> </list> </constructor-arg> </bean>
繼續(xù)在測試類里面調用toPrint方法
3、自動裝配
自動裝配中byName和byType的區(qū)別:
byName:在配置好的文件中變量名不更改就不會報錯。按照屬性的名稱進行自動裝配。
- 當一個bean的屬性名稱與其他bean的id匹配時,Spring容器會自動將該bean注入到對應的屬性中。
- 如果找不到匹配的bean,則會拋出異常。
- 在XML配置中,可以使用
autowire="byName"
或@Autowired
注解實現(xiàn)byName自動裝配。byType:會在整個spring中尋找JavaBean,按照屬性的類型進行自動裝配。
- 當一個bean的屬性的類型與其他bean的class匹配時,Spring容器會自動將該bean注入到對應的屬性中。
- 如果有多個匹配的bean,則會拋出異常,需要進行更具體的限定或使用
@Qualifier
注解來解決。- 在XML配置中,可以使用
autowire="byType"
或@Autowired
注解實現(xiàn)byType自動裝配。
四、spring與tomcat的整合/spring與web容器的整合
我們的xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byName" 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"> <!--凡是在spring核心配置文件spring-context.xml中配置,那么該類JavaBean就交給了spring容器管理--> <bean class="com.tgq.ioc.web.UserAction" id="userAction"> <property name="us" ref="userService1"/> </bean> <bean class="com.tgq.ioc.web.GoodsAction" id="goodsAction"> <property name="us" ref="userService2"/> </bean> <bean class="com.tgq.ioc.service.impl.UserServiceImpl1" id="userService1"/> <bean class="com.tgq.ioc.service.impl.UserServiceImpl2" id="userService2"/> </beans>
編寫一個過濾器
@WebListener public class sprintListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("初始化"); //spring的上下文 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); //tomcat上下文 ServletContext servletContext = sce.getServletContext(); servletContext.setAttribute("sprintContext", context); } }
編寫servlet類調用它
@WebServlet("userlist") public class Userservlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取到sprint的上下文對象 ClassPathXmlApplicationContext Context = (ClassPathXmlApplicationContext) req.getServletContext().getAttribute("sprintContext"); UserService bean = (UserService) Context.getBean("userService1"); System.out.println(bean); bean.updUser(); } }
啟動服務器就完成了整合
五、spring AOP
Spring AOP(Aspect-Oriented Programming,面向切面編程)是Spring框架的一個重要模塊,提供了在應用程序中進行橫切關注點的模塊化支持。
1、aop的特點
- 非侵入性:Spring AOP采用編譯期間的代理方式實現(xiàn)切面功能,不需要修改原始類的代碼。使用者可以在不侵入原有代碼的情況下,將切面邏輯插入到目標對象的方法中,實現(xiàn)對橫切關注點的處理。
- 松耦合:Spring AOP通過使用切面來處理橫切關注點,將關注點的實現(xiàn)與主要業(yè)務邏輯進行分離。切面可以獨立于應用程序進行開發(fā)和測試,并且可以通過配置和織入的方式來將切面應用到不同的目標對象上,實現(xiàn)代碼的松耦合性。
- 橫切關注點的集中化管理:Spring AOP支持將相同的橫切關注點集中定義在一個切面中,并在需要的地方進行切入。這使得橫切關注點的管理更加方便,可以避免代碼的重復。
- 與IoC容器的集成:Spring AOP與Spring IoC容器無縫集成,可以借助IoC容器來管理切面和目標對象,實現(xiàn)AOP配置的靈活性和可擴展性。通過注解或XML配置,可以方便地將切面和目標對象注入到容器中并進行織入操作。
- 支持豐富的切點表達式:Spring AOP支持使用切點表達式對目標對象的方法進行精確選擇。切點表達式可以根據(jù)方法名稱、參數(shù)、返回類型等條件來定義切點,靈活地進行切面織入。
- 支持不同類型的通知:Spring AOP提供了多種類型的通知,如前置通知、后置通知、環(huán)繞通知、異常通知和最終通知。用戶可以選擇合適的通知類型,并定義切面的處理邏輯。
- 可擴展性:Spring AOP對切面的實現(xiàn)支持繼承和組合的機制,可以通過實現(xiàn)Advice接口或繼承現(xiàn)有的Advice類來進行切面的定制和擴展。這樣使得開發(fā)者能夠根據(jù)具體需求來實現(xiàn)自定義的切面邏輯。
Spring AOP提供了一種靈活、非侵入性的橫切關注點處理機制,通過將關注點的實現(xiàn)與主要業(yè)務邏輯分離,實現(xiàn)代碼的解耦和管理的集中化。同時,與Spring IoC容器的集成以及豐富的切點表達式和通知類型,使得Spring AOP具有很高的擴展性和靈活性。
我們一般會用到系統(tǒng)的日志功能
2、AOP中關鍵性概念 ?
名稱 | 描述 | 注釋 |
---|---|---|
連接點(Joinpoint) | 程序執(zhí)行過程中明確的點,如方法的調用,或者異常的拋出. | |
目標對象(Target) | 被通知(被代理)的對象 | 完成具體的業(yè)務邏輯 |
通知(Advice) | 在某個特定的連接點上執(zhí)行的動作,同時Advice也是程序代碼的具體實現(xiàn),例如一個實現(xiàn)日志記錄的代碼(通知有些書上也稱為處理) | 完成切面編程 |
代理(Proxy) | 將通知應用到目標對象后創(chuàng)建的對象(代理=目標+通知),例子:外科醫(yī)生+護士 | 只有代理對象才有AOP功能,而AOP的代碼是寫在通知的方法里面的 |
切入點(Pointcut) | 多個連接點的集合,定義了通知應該應用到那些連接點。 | 也將Pointcut理解成一個條件 ,此條件決定了容器在什么情況下將通知和目標組合成代理返回給外部程序 |
適配器(Advisor) | 適配器=通知(Advice)+切入點(Pointcut) |
3、前置通知
1.1、準備工作
如果需要可以創(chuàng)建如圖一樣的項目結構
- 編寫接口
?public interface IBookBiz { // 購物 public boolean buy(String userName, String bookName, Double price); // 發(fā)表評論 public void comment(String userName, String comments); }
- 實現(xiàn)上面的接口
public class BookBizImpl implements IBookBiz { public BookBizImpl() { super(); } public boolean buy(String userName, String bookName, Double price) { // 通過控制臺的輸出方式模擬購書 if (null == price || price <= 0) { throw new PriceException("book price exception"); } System.out.println(userName + " buy " + bookName + ", spend " + price); return true; } public void comment(String userName, String comments) { // 通過控制臺的輸出方式模擬發(fā)表書評 System.out.println(userName + " say:" + comments); } }
編寫一個異常類
public class PriceException extends RuntimeException { public PriceException() { super(); } public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public PriceException(String message, Throwable cause) { super(message, cause); } public PriceException(String message) { super(message); } public PriceException(Throwable cause) { super(cause); } }
配置spring-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" 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"> <!--aop的javabean--> <!--對象--> <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean> </beans>
編寫一個測試類
public class demo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); bookBiz.buy("小牛子", "鋼鐵是怎樣煉成的", 99.9d); bookBiz.comment("小牛子", "睡不著,來勁了"); } }
測試結果
?1.2、前置通知
添加一個通知類,需要繼承MethodBeforeAdvice 重寫方法
public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 在這里,可以獲取到目標類的全路徑及方法及方法參數(shù),然后就可以將他們寫到日志表里去 String target = arg2.getClass().getName(); String methodName = arg0.getName(); String args = Arrays.toString(arg1); System.out.println("【前置通知:系統(tǒng)日志】:"+target+"."+methodName+"("+args+")被調用了"); } }
配置通知類,代理,在代理里面配置目標對象和通知
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" 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"> <!--aop的javabean--> <!--對象--> <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean> <!--通知--> <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean> <!-- 代理 --> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean"> <!-- 配置目標對象 --> <property name="target" ref="bookBiz"></property> <!-- 配置代理接口,目標對象接口 --> <property name="proxyInterfaces"> <list> <value>com.tgq.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知 --> <property name="interceptorNames"> <list> <value>beforeAdvice</value> </list> </property> </bean> </beans>
編寫測試類
public class demo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); IBookBiz bookBiz = (IBookBiz) context.getBean("factoryBean"); bookBiz.buy("小牛子", "鋼鐵是怎樣煉成的", 99.9d); bookBiz.comment("小牛子", "睡不著,來勁了"); } }
結果
4、后置通知
?在前面的基礎上再寫一個后置通知的類,需要實現(xiàn)AfterReturningAdvice ,重寫afterReturning方法
public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { String target = arg3.getClass().getName(); String methodName = arg1.getName(); String args = Arrays.toString(arg2); System.out.println("【后置通知:買書返利】:"+target+"."+methodName+"("+args+")被調用了,"+"該方法被調用后的返回值為:"+arg0); } }
配置xml
<!--aop的javabean--> <!--對象--> <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean> <!-- 前置通知--> <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean> <!-- 后置通知 --> <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <!-- 代理 --> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean"> <!-- 配置目標對象 --> <property name="target" ref="bookBiz"></property> <!-- 配置代理接口,目標對象接口 --> <property name="proxyInterfaces"> <list> <value>com.tgq.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知 --> <property name="interceptorNames"> <list> <value>beforeAdvice</value> <value>myAfterReturningAdvice</value> </list> </property> </bean>
測試結果
5、環(huán)繞通知
編寫一個環(huán)繞通知類,實現(xiàn)MethodInterceptor接口,重寫方法
public class MyMethodInterceptor implements MethodInterceptor {//Interceptor攔截器的意思 @Override public Object invoke(MethodInvocation arg0) throws Throwable { String target = arg0.getThis().getClass().getName(); String methodName = arg0.getMethod().getName(); String args = Arrays.toString(arg0.getArguments()); System.out.println("【環(huán)繞通知調用前:】:" + target + "." + methodName + "(" + args + ")被調用了"); // arg0.proceed()就是目標對象的方法 Object proceed = arg0.proceed(); System.out.println("【環(huán)繞通知調用后:】:該方法被調用后的返回值為:" + proceed); return proceed; } }
配置xml
<!--aop的javabean--> <!--對象--> <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean> <!-- 前置通知--> <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean> <!-- 后置通知 --> <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <!-- 環(huán)繞通知--> <bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/> <!-- 代理 --> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean"> <!-- 配置目標對象 --> <property name="target" ref="bookBiz"></property> <!-- 配置代理接口,目標對象接口 --> <property name="proxyInterfaces"> <list> <value>com.tgq.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知 --> <property name="interceptorNames"> <list> <!-- <value>beforeAdvice</value>--> <!-- <value>myAfterReturningAdvice</value>--> <value>myMethodInterceptor</value> </list> </property> </bean>
測試結果
6、異常通知
編寫一個異常通知類,實現(xiàn)ThrowsAdvice?接口,編寫方法,方法名必須是afterThrowing
public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("【異常通知】:當價格發(fā)生異常,那么執(zhí)行此處代碼塊!??!"); } }
配置xml文件
<!--aop的javabean--> <!--對象--> <bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean> <!-- 前置通知--> <bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean> <!-- 后置通知 --> <bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <!-- 環(huán)繞通知--> <bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/> <!--異常通知--> <bean class="com.tgq.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"/> <!-- 代理 --> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean"> <!-- 配置目標對象 --> <property name="target" ref="bookBiz"></property> <!-- 配置代理接口,目標對象接口 --> <property name="proxyInterfaces"> <list> <value>com.tgq.aop.biz.IBookBiz</value> </list> </property> <!-- 配置通知 --> <property name="interceptorNames"> <list> <!-- <value>beforeAdvice</value>--> <!-- <value>myAfterReturningAdvice</value>--> <value>myMethodInterceptor</value> <value>myThrowsAdvice</value> </list> </property> </bean>
測試,如果我們的價格有問題會出現(xiàn)異常通知
public class demo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); // IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz"); IBookBiz bookBiz = (IBookBiz) context.getBean("factoryBean"); bookBiz.buy("小牛子", "鋼鐵是怎樣煉成的", -99.9d); bookBiz.comment("小牛子", "睡不著,來勁了"); } }
測試結果
7、過濾通知
我們配置我們的過濾通知,需要在xml里面進行配置
<!--aop的javabean-->
<!--對象-->
<bean class="com.tgq.aop.biz.impl.BookBizImpl" id="bookBiz"></bean>
<!-- 前置通知-->
<bean class="com.tgq.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
<!-- 后置通知 -->
<bean class="com.tgq.aop.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean>
<!-- 環(huán)繞通知-->
<bean class="com.tgq.aop.advice.MyMethodInterceptor" id="myMethodInterceptor"/>
<!--異常通知-->
<bean class="com.tgq.aop.advice.MyThrowsAdvice" id="myThrowsAdvice"/>
<!-- 過濾通知-->
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor">
<!-- 給我們的后置通知做過濾-->
<property name="advice" ref="myAfterReturningAdvice"/>
<!-- 給我們的購物做過濾-->
<property name="pattern" value=".*buy"/>
</bean><!--正則匹配我們的切入點-->
<!-- 代理 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="factoryBean">
<!-- 配置目標對象 -->
<property name="target" ref="bookBiz"></property>
<!-- 配置代理接口,目標對象接口 -->
<property name="proxyInterfaces">
<list>
<value>com.tgq.aop.biz.IBookBiz</value>
</list>
</property>
<!-- 配置通知 -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
<!-- <value>myAfterReturningAdvice</value>-->
<!-- 使用過濾通知需要把原有的配置注釋或者刪除-->
<value>regexpMethodPointcutAdvisor</value>
<value>myMethodInterceptor</value>
<value>myThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
?測試結果,我們的評論后就沒有返利了,我們只有購物后評論前才有返利
8、AOP的總結
8.1、概念
aop是面向切面編程,程序是由上至下執(zhí)行,但是aop面向切面編程不是,aop的程序執(zhí)行,首先當程序執(zhí)行到目標對象的目標方法時,如果連接點上由前置通知,則先執(zhí)行前置通知,再執(zhí)行目標方法,如果沒有前置通知,則先執(zhí)行前置通知,再執(zhí)行目標方法,如果沒有前置通知,則繼續(xù)執(zhí)行目標方法;再查看目標方法上是否有后置通知,如果有,則再執(zhí)行后置通知代碼。
8.2、應用
不管是前置通知、后置通知、環(huán)繞通知、異常通知還是過濾通知,代碼都是非業(yè)務核心代碼,比如日志和事務的管理。
我們還可以去學習一下:jdk代理與cglib代理,aop數(shù)據(jù)字典等其他
六、spring中bean的生命周期
- 初始化 init
- 使用 service
- 銷毀 destroy
1、spring管理JavaBean初始化過程
1.1、思維導圖
BeanDefinition:包含了很多屬性和方法。例如:id、class(類名)、
scope、ref(依賴的bean)等等。其實就是將bean(例如<bean>)的定義信息
存儲到這個對應BeanDefinition相應的屬性中
例如:<bean id="" class="" scope=""> -----> BeanDefinition(id/class/scope)
Aware感知接口:在實際開發(fā)中,經(jīng)常需要用到Spring容器本身的功能資源。
例如:BeanNameAware、ApplicationContextAware等等
BeanDefinition 實現(xiàn)了 BeanNameAware、ApplicationContextAware
?BeanPostProcessor:后置處理器。在Bean對象實例化和引入注入完畢后,
在顯示調用初始化方法的前后添加自定義的邏輯。(類似于AOP的繞環(huán)通知)
前提條件:如果檢測到Bean對象實現(xiàn)了BeanPostProcessor后置處理器才會執(zhí)行
Before和After方法
BeanPostProcessor文章來源:http://www.zghlxwxcb.cn/news/detail-652635.html
?BeanDefinitionReader:解析Bean的定義。在Spring容器啟動過程中,會將Bean解析成Spring內部的BeanDefinition結構;
理解為:將spring.xml中的<bean>標簽轉換成BeanDefinition結構
有點類似于XML解析
?BeanPostProcessor:后置處理器。在Bean對象實例化和引入注入完畢后,
在顯示調用初始化方法的前后添加自定義的邏輯。(類似于AOP的繞環(huán)通知)
前提條件:如果檢測到Bean對象實現(xiàn)了BeanPostProcessor后置處理器才會執(zhí)行
Before和After方法
BeanPostProcessor
1.2、spring容器管理JavaBean的初始化過程(spring中bean的生命周期)。
- xml/annotation/configuation配置JavaBean
- BeanDefinitionReader解析配置的JavaBean得到BeanDefinition,最終得到List<BeanDefinition>集合。
- 觸發(fā)BeanFactoryPoatProcessor,JavaBean初始化之前執(zhí)行自己的業(yè)務。
- spring中beanFactory,會通過List<BeanDefinition>集合遍歷初始化所有的JavaBean對象。
- 如果自己的JavaBean需要調動spring上下文中的資源,那么需要實現(xiàn)*Aware感知接口
- 如果自己的JavaBean已經(jīng)初始化好了,還需要擴展功能,那么需要借助BeanPostProcessor來實現(xiàn)。
2、spring的JavaBean管理中單例模式及原型模式
2.1、spring中JavaBean是單例還是多例。
- 默認是單例的,但是可以配置多例的。
- 單例的優(yōu)點:節(jié)約內存;弊端:有變量污染;
多例的優(yōu)點:無變量污染;弊端:消耗內存;?public class ParamAction { private int age; private String name; private List<String> hobby; private int num = 1;//初始值 // private UserBiz userBiz = new UserBizImpl1(); public ParamAction() { super(); } public ParamAction(int age, String name, List<String> hobby) { super(); this.age = age; this.name = name; this.hobby = hobby; } public void execute() { // userBiz.upload(); // userBiz = new UserBizImpl2(); System.out.println("this.num=" + this.num++); System.out.println(this.name); System.out.println(this.age); System.out.println(this.hobby); } }
?xml配置@org.junit.Test public void test1() { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml"); // ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-context.xml"); ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction"); ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction"); System.out.println("p1 == p2:" + (p1 == p2)); if (p1 != null && p2 != null) { p1.execute(); p2.execute(); } applicationContext.close(); // 單例時,容器銷毀instanceFactory對象也銷毀;多例時,容器銷毀對象不一定銷毀; }
<!-- spring的生命周期--> <!-- 默認單例模式 --> <!--多例模式:prototype--><!--單例模式:singleton--> <bean id="paramAction" class="com.tgq.beanLife.ParamAction" scope="singleton"> <constructor-arg name="name" value="三豐"></constructor-arg> <constructor-arg name="age" value="21"></constructor-arg> <constructor-arg name="hobby"> <list> <value>抽煙</value> <value>燙頭</value> <value>大保健</value> </list> </constructor-arg> </bean>
![]()
- 單例:JavaBean是跟著spring上下文初始化的;(容器生對象生,容器死對象死)
多例:JavaBean是使用的時候才會創(chuàng)建,銷毀跟著jvm走。
?xml配置public class InstanceFactory { public void init() { System.out.println("初始化方法"); } public void destroy() { System.out.println("銷毀方法"); } public void service() { System.out.println("業(yè)務方法"); } }
?<bean id="instanceFactory" class="com.tgq.beanLife.InstanceFactory" scope="singleton" init-method="init" destroy-method="destroy"></bean>
@org.junit.Test public void test2() {// // 多例不會調用我們的初始化方法 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml"); //多例模式的時候,只要調用就會初始化 InstanceFactory instanceFactory = (InstanceFactory) applicationContext.getBean("instanceFactory"); }
![]()
// BeanFactory會初始化bean對象,但會根據(jù)不同的實現(xiàn)子類采取不同的初始化方式
// 默認情況下bean的初始化,單例模式立馬會執(zhí)行,但是此時XmlBeanFactory作為子類,單例模式下容器創(chuàng)建,bean依賴沒有初始化,只有要獲取使用bean對象才進行初始化
@org.junit.Test
public void test3() {
// ClassPathXmlApplicationContext applicationContext = new
// ClassPathXmlApplicationContext("/spring-context.xml");
Resource resource = new ClassPathResource("/spring-context.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
// 調用會初始化
// InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
}
什么情況下會使用多例:一個類里面的屬性值有變化,會使用多例文章來源地址http://www.zghlxwxcb.cn/news/detail-652635.html
到了這里,關于spring ico容器 spring注入方式 spring與tomcat整合的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!