說明:本文主要內(nèi)容來自慕課網(wǎng)。配合視頻食用口味更佳虾攻。
主要是順著已經(jīng)學(xué)習(xí)的視頻順序總結(jié)一遍帽借,以深化理解和方便日后復(fù)習(xí)憔维。
本部分只包含IOC和AOP。
第一章 概述
Spring 入門課程簡介
本門課程包含的內(nèi)容:
Spring簡介
IOC(配置谋减、注解)
Bean(配置、注解)
AOP(配置、注解难裆、AspetJ、API)
Spring概況
Spring是什么
- Spring是一個開源框架镊掖,最初為了解決企業(yè)應(yīng)用開發(fā)的復(fù)雜性而創(chuàng)建的乃戈,但現(xiàn)在已經(jīng)不止應(yīng)用于企業(yè)應(yīng)用
- 是一個輕量級的控制反轉(zhuǎn)(IOC)和面向切面(AOP)的容器框架
- 從大小與開銷兩方面而言Spring都是輕量的
- 通過控制反轉(zhuǎn)(IOC)的技術(shù)達(dá)到松耦合的目的
- 提供了面向切面編程(AOP)的豐富支持,允許通過分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級服務(wù)進(jìn)行內(nèi)聚性的開發(fā)
- 包含并管理應(yīng)用對象的配置和生命周期堰乔,這個意義上是一種容器偏化。
- 將簡單的組件配置、組合成為復(fù)雜的應(yīng)用镐侯,這個意義上是框架
為什么是Spring
- 在Spring上開發(fā)應(yīng)用簡單
- 在Spring上開發(fā)應(yīng)用方便
- 在Spring上開發(fā)應(yīng)用快捷
Spring作用
- 容器
- 提供了多種技術(shù)的支持
--JMS
--MQ支持
--UnitTest - AOP(事務(wù)管理侦讨、日志等)
- 提供了眾多方便應(yīng)用的輔助類(JDBC Template等)
- 對主流應(yīng)用框架(Hibernate等)提供了良好的支持
適用范圍
- 構(gòu)建企業(yè)應(yīng)用(SpringMVC+Spring+Hibernate/MyBatis)
- 單獨(dú)使用Bean容器(Bean管理)
- 單獨(dú)使用AOP進(jìn)行切面處理
- 其他的Spring功能:如:對消息的支持等
- 在互聯(lián)網(wǎng)中的應(yīng)用
Spring框架
框架
什么是框架
維基百科:軟件框架,通常是指為了實(shí)現(xiàn)某個業(yè)界標(biāo)準(zhǔn)或者完成特定基本任務(wù)的軟件組件規(guī)范苟翻,也指為了實(shí)現(xiàn)某個軟件組織規(guī)范時韵卤,提供規(guī)范所要求之基礎(chǔ)功能的軟件產(chǎn)品。
通俗的說崇猫,框架就是制定一套規(guī)范或者規(guī)則(思想)沈条,大家(程序員)在該規(guī)范或者規(guī)程(思想)下工作。用現(xiàn)實(shí)生活中的比喻就是:使用別人搭好的舞臺诅炉,你來做表演蜡歹。
框架的特點(diǎn)
- 半成品
- 封裝了特定的處理流程和控制邏輯
- 成熟的屋厘、不斷升級的軟件
框架與類庫的區(qū)別
- 框架一般是封裝了邏輯、高內(nèi)聚的月而,類庫則是松散的工具組合汗洒。
- 框架專注于某一領(lǐng)域,類庫則是更通用的父款。
- 類庫通過不同的方式組裝成不同的框架
為什么使用框架
- 軟件系統(tǒng)日趨復(fù)雜
- 重用度高溢谤,開發(fā)效率和質(zhì)量提高
- 軟件設(shè)計(jì)人員要專注于對領(lǐng)域的了解,使需求分析更充分
- 易于上手憨攒、快速解決問題
第二章 Spring IOC 容器
接口與面向接口編程
接口
- 用于溝通的中介物的抽象化
- 實(shí)體把自己提供給外界的一種抽象化說明世杀,用以由內(nèi)部操作分離出外部溝通方式,使其能被修改內(nèi)部而不影響外界其他實(shí)體與其交互的方式肝集。
- 對應(yīng)Java接口即聲明瞻坝,聲明了哪些方法對外提供的。
- 在Java8中包晰,接口可以擁有方法體
面向接口編程
- 結(jié)構(gòu)設(shè)計(jì)中湿镀,分清層次及調(diào)用關(guān)系,每層只向外(上層)提供一組功能接口伐憾,各層間僅依賴接口而非實(shí)現(xiàn)類
- 接口實(shí)現(xiàn)的變動不影響各層間的調(diào)用勉痴,這一點(diǎn)在公共服務(wù)中尤為重要
- “面向接口編程”中的“接口”是用于隱藏具體實(shí)現(xiàn)和實(shí)現(xiàn)多態(tài)性的組件
什么是IOC
站在過程的角度看
IOC:控制反轉(zhuǎn),控制權(quán)的轉(zhuǎn)移树肃,不再是應(yīng)用程序本身-->依賴對象的創(chuàng)建和維護(hù)
蒸矛,而是由外部容器-->依賴對象的創(chuàng)建和維護(hù)
站在主體的角度看
DI(依賴注入)是其中一種實(shí)現(xiàn)方式
2004年,Martin Fowler探討了同一個問題胸嘴,既然IoC是控制反轉(zhuǎn)雏掠,那么 到底是“哪些方面被控制反轉(zhuǎn)了呢?”劣像,經(jīng)過詳細(xì)的分析和論證后乡话,他得出了答案:“獲得依賴對象的過程
被反轉(zhuǎn)了”《龋控制被反轉(zhuǎn)之后绑青,獲得依賴對象的過程由自身管理變成了由IOC容器的主動注入。于是屋群,他給“控制反轉(zhuǎn)” 取了一個更合適的名字“依賴注入(Dependency Injection)”闸婴。他的這個答案,實(shí)際上給出了實(shí)現(xiàn)IoC的方法:注入芍躏。
所謂依賴注入邪乍,就是由IoC容器 在運(yùn)行期間
,動態(tài)地將某種依賴關(guān)系注入到對象之中。
目的
創(chuàng)建對象并且組裝對象之間的關(guān)系
IOC簡單類比
通過中介找房子 | 通過IOC使用對象 |
---|---|
找中介 | 找IOC容器 |
中介介紹房子 | 容器返回對象 |
租房庇楞、入住 | 使用對象 |
Spring的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" >
//這是我們要配置的內(nèi)容
<bean id="oneInterface" class="com.imooc.ioc.interfaces.OneInterfaceImpl"></bean>
</beans>
Bean容器初始化
基礎(chǔ)
兩個包:
- org.springframework.beans
- org.springframework.context
容器:
- BeanFactory提供配置結(jié)構(gòu)和基本功能榜配,加載并初始化Bean
- ApplicationContext保存了Bean對象,并在Spring中廣泛使用
BeanFactory和ApplicationContext是Spring兩種很重要的容器,前者提供了最基本的依賴注入的支持姐刁,而后者在繼承前者的基礎(chǔ)進(jìn)行了功能的拓展芥牌,例如增加了事件傳播,資源訪問和國際化的消息訪問等功能聂使。
方式---ApplicationContext
1. 本地文件
FileSystemXmlApplicationContext
FileSystemXmlApplicationContext context = new
FileSystemXmlApplicationContext("F:/workspace/appcontext.xml");
2. 類路徑
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("classpath:spring-context.xml");
3. Web應(yīng)用
依賴servlet
或Listener
<listener>
<listener-class>org.springframework.web.context.ContextLoaderServlet</listener-class>
</listener>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>on</load-on-startup>
</servlet>
Spring注入方式
Spring注入是指在啟動Spring容器加載bean配置的時候,完成對變量的賦值行為谬俄。常用的兩種注入方式設(shè)值注入
與構(gòu)造注入
柏靶。
設(shè)值注入---property
通過setter注入。
<?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="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
構(gòu)造注入---constructor-arg
通過構(gòu)造器注入溃论。
<?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="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
第三章:Spring Bean裝配(上)
Bean的配置項(xiàng)及作用域
Bean配置項(xiàng)
屬性 | 描述 |
---|---|
id|name | 這個屬性指定唯一 的 bean 標(biāo)識符屎蜓。在基于 XML 的配置元數(shù)據(jù)中,可以使用 id 或 name 屬性來指定 bean 標(biāo)識符 |
class | 這個屬性是強(qiáng)制性的钥勋,并且指定用來創(chuàng)建 bean 的bean 類
|
scope | 這個屬性指定由特定的 bean 定義創(chuàng)建的對象的作用域
|
constructor-arg | 用來注入依賴關(guān)系 |
properties | 用來注入依賴關(guān)系 |
autowiring mode | 用來注入依賴關(guān)系 |
lazy-initialization mode | 延遲初始化的 bean 告訴 IoC 容器在它第一次被請求時 炬转,而不是在啟動時去創(chuàng)建一個 bean 實(shí)例 |
initialization() | 在 bean 的所有必需的屬性被容器設(shè)置 之后,調(diào)用回調(diào)方法 |
destruction() | 當(dāng)包含該 bean 的容器被銷毀 時算灸,使用回調(diào)方法 |
Bean的作用域
添加方式
<bean id=" " class=" " scope="singleton"></bean>
作用域 | 描述 |
---|---|
singleton | 單例扼劈,指一個Bean容器中只存在一份
|
prototype |
每次請求 (每次使用)創(chuàng)建新的實(shí)例,destroy方式不生效 |
request |
每次http請求 創(chuàng)建一個實(shí)例且僅在當(dāng)前request內(nèi)有效 |
session | 同上菲驴,每次http請求創(chuàng)建荐吵,當(dāng)前session內(nèi)有效 |
global session | 基于portlet的web中有效(portlet定義了global session),如果在web中赊瞬,同session |
Bean的生命周期
- 首先容器啟動后先煎,會對scope為singleton且非懶加載的bean進(jìn)行實(shí)例化
- 按照Bean定義信息配置信息,注入所有的屬性
- 如果Bean實(shí)現(xiàn)了BeanNameAware接口巧涧,會回調(diào)該接口的
setBeanName()方法
薯蝎,傳入該Bean的id,此時該Bean就獲得了自己在配置文件中的id - 如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,會回調(diào)該接口的
setBeanFactory()方法
谤绳,傳入該Bean的BeanFactory占锯,這樣該Bean就獲得了自己所在的BeanFactory - 如果Bean實(shí)現(xiàn)了ApplicationContextAware接口,會回調(diào)該接口的
setApplicationContext()方法
,傳入該Bean的ApplicationContext闷供,這樣該Bean就獲得了自己所在的ApplicationContext烟央, - 如果有Bean實(shí)現(xiàn)了BeanPostProcessor接口,則會回調(diào)該接口的
postProcessBeforeInitialzation()方法
- 如果Bean實(shí)現(xiàn)了InitializingBean接口歪脏,則會回調(diào)該接口的
afterPropertiesSet()方法
- 如果Bean配置了init-method方法疑俭,則會執(zhí)行
init-method配置的方法
- 如果有Bean實(shí)現(xiàn)了BeanPostProcessor接口,則會回調(diào)該接口的
postProcessAfterInitialization()方法
10.經(jīng)過流程9之后婿失,就可以正式使用該Bean了,對于scope為singleton的Bean,Spring的ioc容器中會緩存一份該bean的實(shí)例钞艇,而對于scope為prototype的Bean,每次被調(diào)用都會new一個新的對象啄寡,期生命周期就交給調(diào)用方管理了,不再是Spring容器進(jìn)行管理了
11.容器關(guān)閉后哩照,如果Bean實(shí)現(xiàn)了DisposableBean接口挺物,則會回調(diào)該接口的destroy()方法
12.如果Bean配置了destroy-method方法,則會執(zhí)行destroy-method配置的方法
至此飘弧,整個Bean的生命周期結(jié)束
創(chuàng)建與銷毀
方法1:實(shí)現(xiàn)InitializingBean和DisposableBean接口
這兩個接口都只包含一個方法识藤。
- 實(shí)現(xiàn)
InitializingBean
接口的afterPropertiesSet()
方法可以在Bean屬性值設(shè)置好之后做一些操作 - 實(shí)現(xiàn)
DisposableBean
接口的destroy()
方法可以在銷毀Bean之前做一些操作。
如下:
public class GiraffeService implements InitializingBean,DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("執(zhí)行InitializingBean接口的afterPropertiesSet方法");
}
@Override
public void destroy() throws Exception {
System.out.println("執(zhí)行DisposableBean接口的destroy方法");
}
}
這種方法比較簡單次伶,但是不建議使用痴昧。因?yàn)檫@樣會將Bean的實(shí)現(xiàn)和Spring框架耦合
在一起。
方法2:在bean的配置文件中指定init-method和destroy-method方法
Spring允許我們創(chuàng)建自己的init方法和destroy方法冠王,只要在Bean的配置文件中指定init-method
和destroy-method
的值就可以在Bean初始化時和銷毀之前執(zhí)行一些操作赶撰。
配置文件中的配置:
<bean name="giraffeService" class="com.giraffe.spring.service.GiraffeService"
init-method="initMethod" destroy-method="destroyMethod">
</bean>
代碼:
public class GiraffeService {
//通過<bean>的destroy-method屬性指定的銷毀方法
public void destroyMethod() throws Exception {
System.out.println("執(zhí)行配置的destroy-method");
}
//通過<bean>的init-method屬性指定的初始化方法
public void initMethod() throws Exception {
System.out.println("執(zhí)行配置的init-method");
}
}
需要注意的是自定義的init-method和post-method方法可以拋異常但是不能有參數(shù)。
這種方式比較推薦柱彻,因?yàn)榭梢宰约簞?chuàng)建方法豪娜,無需將Bean的實(shí)現(xiàn)直接依賴于spring的框架
。
方法3:使用@PostConstruct和@PreDestroy注解
- 除了xml配置的方式哟楷,Spring也支持用
@PostConstruct
和@PreDestroy
注解來指定init和destroy方法瘤载。 - 這兩個注解均在
javax.annotation
包中。 - 為了注解可以生效吓蘑,需要在配置文件中定義
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
或context:annotation-config
配置文件:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
代碼:
public class GiraffeService {
@PostConstruct
public void initPostConstruct(){
System.out.println("執(zhí)行PostConstruct注解標(biāo)注的方法");
}
@PreDestroy
public void preDestroy(){
System.out.println("執(zhí)行preDestroy注解標(biāo)注的方法");
}
}
方法4:配置全局默認(rèn)初始化惕虑、銷毀方法
default-init-method=" "
default-destroy-method=" "
<?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"
default-init-method="defautInit" default-destroy-method="defaultDestroy">
</beans>
Bean的生命周期示例
- 定義一個
Person
類 - 實(shí)現(xiàn)了
BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean
五個接口 - 在applicationContext.xml文件中配置了該Bean的
id
為person1,并且配置了init-method
和destroy-method
,為該Bean配置了屬性name
為jack的值 - 然后定義了一個MyBeanPostProcessor方法,該方法實(shí)現(xiàn)了BeanPostProcessor接口,且在applicationContext.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" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<bean id="person1" destroy-method="myDestroy"
init-method="myInit" class="com.test.spring.life.Person">
<property name="name">
<value>jack</value>
</property>
</bean>
<!-- 配置自定義的后置處理器 -->
<bean id="postProcessor" class="com.pingan.spring.life.MyBeanPostProcessor" />
</beans>
public class Person implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public Person() {
System.out.println("PersonService類構(gòu)造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("set方法被調(diào)用");
}
//自定義的初始化函數(shù)
public void myInit() {
System.out.println("myInit被調(diào)用");
}
//自定義的銷毀方法
public void myDestroy() {
System.out.println("myDestroy被調(diào)用");
}
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("destory被調(diào)用");
}
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("afterPropertiesSet被調(diào)用");
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("setApplicationContext被調(diào)用");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("setBeanFactory被調(diào)用,beanFactory");
}
public void setBeanName(String beanName) {
// TODO Auto-generated method stub
System.out.println("setBeanName被調(diào)用,beanName:" + beanName);
}
public String toString() {
return "name is :" + name;
}
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization被調(diào)用");
return bean;
}
public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization被調(diào)用");
return bean;
}
}
public class AcPersonServiceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("開始初始化容器");
ApplicationContext ac = new ClassPathXmlApplicationContext("com/test/spring/life/applicationContext.xml");
System.out.println("xml加載完畢");
Person person1 = (Person) ac.getBean("person1");
System.out.println(person1);
System.out.println("關(guān)閉容器");
((ClassPathXmlApplicationContext)ac).close();
}
}
我們啟動容器磨镶,可以看到整個調(diào)用過程:
開始初始化容器
九月 25, 2016 10:44:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
九月 25, 2016 10:44:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/test/spring/life/applicationContext.xml]
Person類構(gòu)造方法
set方法被調(diào)用
setBeanName被調(diào)用,beanName:person1
setBeanFactory被調(diào)用,beanFactory
setApplicationContext被調(diào)用
postProcessBeforeInitialization被調(diào)用
afterPropertiesSet被調(diào)用
myInit被調(diào)用
postProcessAfterInitialization被調(diào)用
xml加載完畢
name is :jack
關(guān)閉容器
九月 25, 2016 10:44:51 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
destory被調(diào)用
myDestroy被調(diào)用
Aware接口
有些時候我們需要在Bean的初始化中使用Spring框架自身的一些對象來執(zhí)行一些操作溃蔫,比如獲取ServletContext的一些參數(shù),獲取ApplicaitionContext中的BeanDefinition的名字琳猫,獲取Bean在容器中的名字等等伟叛。為了讓Bean可以獲取到框架自身的一些對象,Spring提供了一組名為___Aware
的接口脐嫂。
這些接口均繼承于org.springframework.beans.factory.Aware標(biāo)記接口统刮,并提供一個將由Bean實(shí)現(xiàn)的set___()
方法,Spring通過基于setter的依賴注入方式使相應(yīng)的對象可以被Bean使用。
一些重要的Aware接口:
接口 | 作用 |
---|---|
ApplicationContextAware | 獲得ApplicationContext對象,可以用來獲取所有Bean definition的名字 |
BeanFactoryAware | 獲得BeanFactory對象账千,可以用來檢測Bean的作用域 |
BeanNameAware | 獲得Bean在配置文件中定義的名字 |
ResourceLoaderAware | 獲得ResourceLoader對象侥蒙,可以獲得classpath中某個文件 |
ServletContextAware | 在一個MVC應(yīng)用中可以獲取ServletContext對象,可以讀取context中的參數(shù) |
ServletConfigAware | 在一個MVC應(yīng)用中可以獲取ServletConfig對象匀奏,可以讀取config中的參數(shù) |
自動裝配(Autowiring)
具體參考
博客
為什么Spring要支持Autowire(自動裝配)
什么是裝配
在spring中鞭衩,對象無需自己查找或創(chuàng)建與其關(guān)聯(lián)的其他對象,容器負(fù)責(zé)把需要相互協(xié)作的對象引用賦予各個對象。
而創(chuàng)建對象之間協(xié)作關(guān)系的行為通常稱為裝配论衍。
Spring 裝配的方式
- 隱式Bean的發(fā)現(xiàn)機(jī)制和自動裝配
所謂自動裝配瑞佩,就是將一個Bean
注入到其他Bean的Property
中,類似于以下:
<bean id="customer" class="com.lei.common.Customer" autowire="byName" />
- 顯式裝配:
- 在Java中顯式裝配
- 在XML中顯式裝配
為什么自動裝配
Spring引入Autowire(自動裝配)機(jī)制就是為了解決<bean>
標(biāo)簽下<property>
標(biāo)簽過多的問題
5種自動裝配模式
模式 | 描述 |
---|---|
no | 默認(rèn)情況下坯台,不自動裝配炬丸,通過“ref”attribute手動設(shè)定 |
byName | 由屬性名自動裝配。根據(jù)Property的Name自動裝配蜒蕾,如果一個bean的name稠炬,和另一個bean中的Property的name相同,則自動裝配這個bean到Property中 |
byType | 由屬性數(shù)據(jù)類型自動裝配咪啡。根據(jù)Property的數(shù)據(jù)類型(Type)自動裝配酸纲,如果一個bean的數(shù)據(jù)類型,兼容另一個bean中Property的數(shù)據(jù)類型瑟匆,則自動裝配 |
constructor | 根據(jù)構(gòu)造函數(shù)參數(shù)的數(shù)據(jù)類型,進(jìn)行byType 模式的自動裝配 |
autodetect | 如果發(fā)現(xiàn)默認(rèn)的構(gòu)造函數(shù)栽惶,用constructor 模式愁溜,否則,用byType 模式 |
Resource
簡介
在Spring內(nèi)部實(shí)現(xiàn)機(jī)制外厂,針對于資源文件(配置的xml文件
)有一個統(tǒng)一的接口Resource
常用方法
方法 | 說明 |
---|---|
exists() | 用于判斷對應(yīng)的資源是否存在
|
isReadable() | 用于判斷對應(yīng)資源的內(nèi)容是否可讀 冕象。需要注意的是當(dāng)其結(jié)果為true的時候,其內(nèi)容未必真的可讀汁蝶,但如果返回false渐扮,則其內(nèi)容必定不可讀。 |
isOpen() | 用于判斷當(dāng)前資源是否代表一個已打開的輸入流掖棉,如果結(jié)果為true墓律,則表示當(dāng)前資源的輸入流不可多次讀取,而且在讀取以后需要對它進(jìn)行關(guān)閉幔亥,以防止內(nèi)存泄露耻讽。該方法主要針對于InputStreamResource,實(shí)現(xiàn)類中只有它的返回結(jié)果為true帕棉,其他都為falses |
getURL() | 返回當(dāng)前資源對應(yīng)的URL针肥。如果當(dāng)前資源不能解析為一個URL則會拋出異常。如ByteArrayResource就不能解析為一個URL |
getFile() | 返回當(dāng)前資源對應(yīng)的File香伴。如果當(dāng)前資源不能以絕對路徑解析為一個File則會拋出異常慰枕。如ByteArrayResource就不能解析為一個File。 |
getInputStream() | 獲取當(dāng)前資源代表的輸入流即纲。除了InputStreamResource以外具帮,其它Resource實(shí)現(xiàn)類每次調(diào)用getInputStream()方法都將返回一個全新的InputStream。 |
常用實(shí)現(xiàn)類
實(shí)現(xiàn)類 | 說明 |
---|---|
ClassPathResource | 獲取類路徑下 的資源文件。假設(shè)有一個資源文件test.txt在類路徑下匕坯,new ClassPathResource("test.txt")
|
FileSystemResource | 獲取文件系統(tǒng) 里面的資源束昵。 |
UrlResource | 可用來代表URL對應(yīng)的資源 ,它對URL做了一個簡單的封裝葛峻。通過給定一個URL地址锹雏,我們就能構(gòu)建一個UrlResource。 |
ByteArrayResource | 針對于字節(jié)數(shù) 組封裝的資源术奖,它的構(gòu)建需要一個字節(jié)數(shù)組礁遵。 |
ServletContextResource | ServletContextResource持有一個ServletContext的引用,其底層是通過ServletContext的getResource()方法和getResourceAsStream()方法來獲取資源的 |
InputStreamResource | 針對于輸入流封裝的資源采记,它的構(gòu)建需要一個輸入流 |
第四章 注解
Spring的一個核心功能是IOC佣耐,就是將Bean初始化加載到容器中,Bean是如何加載到容器的唧龄,可以使用Spring注解方式或者Spring XML配置方式兼砖。
Spring注解方式減少了配置文件內(nèi)容,更加便于管理既棺,并且使用注解可以大大提高了開發(fā)效率讽挟。
下面安裝分類講解Spring中常用的一些注解。
將普通類加入容器形成Bean
spring使用配置文件或者注解的方式進(jìn)行標(biāo)識需要處理的java類丸冕,從而知道哪些Java類當(dāng)bean類處理
耽梅。
注解 | 標(biāo)注 |
---|---|
@Component | 標(biāo)準(zhǔn)一個普通的spring Bean類 |
@Repository | 標(biāo)注一個DAO組件類 |
@Service | 標(biāo)注一個業(yè)務(wù)邏輯組件類 |
@Controller | 標(biāo)注一個控制器組件類 |
其中@Component、@Repository胖烛、@Service眼姐、@Controller實(shí)質(zhì)上屬于同一類注解,用法相同佩番,功能相同众旗,區(qū)別在于標(biāo)識組件的類型。
從容器中取Bean(裝配bean)
注解 | 標(biāo)注 |
---|---|
@Autowired | 屬于Spring 的org.springframework.beans.factory.annotation包下,可用于為類的屬性答捕、構(gòu)造器逝钥、方法進(jìn)行注值 |
@Resource | 不屬于spring的注解,而是來自于JSR-250位于java.annotation包下拱镐,使用該annotation為目標(biāo)bean指定協(xié)作者Bean艘款。 |
@PostConstruct 和 @PreDestroy 方法 | 實(shí)現(xiàn)初始化和銷毀bean之前進(jìn)行的操作 |
Spring MVC模塊注解
@Controller---與前端交互
表明該類會作為與前端作交互的控制層組件
- 通過服務(wù)接口定義的提供訪問應(yīng)用程序的一種行為,
解釋
用戶的輸入沃琅,將其轉(zhuǎn)換
成一個模型然后將試圖呈獻(xiàn)給用戶
@Controller
public class HappyController {
//do something
}
@RequestMapping---url --> 類或方法
- 這個注解用于將url
映射
到整個處理類或者特定的處理請求的方法 - 可以只用通配符哗咆!
- @RequestMapping中可以使用
method =
屬性標(biāo)記其所接受的方法類型,如果不指定方法類型的話益眉,可以使用 HTTP GET/POST 方法請求數(shù)據(jù)晌柬,但是一旦指定方法類型姥份,就只能使用該類型獲取數(shù)據(jù)
@Controller
@RequestMapping("/happy")
public class HappyController {
@Autowired
private HappyService happyService;
@RequestMapping(/hello/ *)
public void sayHello(){
//請求為 /happy/hello/ * 都會進(jìn)入這個方法!
//例如:/happy/hello/123 /happy/hello/adb
//可以通過get/post 請求
}
@RequestMapping(value="/haha",method=RequestMethod.GET)
public void sayHaHa(){
//只能通過get請求
}
}
第五章:Spring AOP
具體參考博客
AOP基本概念及特點(diǎn)
什么是AOP
AOP(Aspect Oriented Programming)
意為:面向切面編程年碘,通過預(yù)編譯
方式和運(yùn)行期動態(tài)代理
實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)澈歉。
主要功能是:日志記錄,性能統(tǒng)計(jì)屿衅,安全控制埃难,事務(wù)處理,異常處理等
AOP實(shí)現(xiàn)方式
- 預(yù)編譯
AspectJ - 運(yùn)行期間動態(tài)代理(JDK動態(tài)代理涤久、CGLib動態(tài)代理)
SpringAOP涡尘、JbossAOP
AOP幾個關(guān)鍵概念
名稱 | 說明 | 舉例 |
---|---|---|
切面(Aspect) | 散落在系統(tǒng)各處的通用的非業(yè)務(wù)代碼
|
日志模塊,權(quán)限模塊响迂,事務(wù)模塊 |
連接點(diǎn)(Joinpoint) | 被攔截到的點(diǎn) | Spring只支持方法類型的連接點(diǎn)考抄,所以在Spring中連接點(diǎn)指的就是被攔截到的方法 |
通知(Advice) | 攔截到連接點(diǎn)之后要執(zhí)行的代碼 | 分為前置、后置蔗彤、異常川梅、最終、環(huán)繞通知五類 |
切入點(diǎn)(Pointcut) | 攔截的方法然遏,連接點(diǎn)攔截后變成切入點(diǎn) | 帶有通知的連接點(diǎn) |
織入(weave) | 通過切入點(diǎn)切入挑势,將切面應(yīng)用到目標(biāo)對象并導(dǎo)致代理對象創(chuàng)建的過程 | ? |
目標(biāo)對象(Target Object) | 代理的目標(biāo)對象,指要織入的對象模塊 | 業(yè)務(wù)代碼 |
AOP代理(AOP Proxy) | AOP框架創(chuàng)建的對象(包括通知方法執(zhí)行等功能) | Spring中的AOP代理可以使JDK動態(tài)代理啦鸣,也可以是CGLIB代理,前者基于接口来氧,后者基于子類 |
各關(guān)鍵概念具體介紹
Advice
說明:
類型 | 說明 |
---|---|
前置通知(Before advcie) | 在某連接點(diǎn)(join point)之前執(zhí)行的通知诫给,但不能阻止連接點(diǎn)前的執(zhí)行(除非它拋出一個異常) |
返回后通知(After returning advice) | 在某連接點(diǎn)(join point)正常執(zhí)行完成后執(zhí)行的通知 |
拋出異常后通知(After throwing advice) | 在方法拋出異常退出時執(zhí)行的通知 |
后通知(After(finally)advice) | 當(dāng)某個連接點(diǎn)退出的時候執(zhí)行的通知(不論是正常返回還是異常退出) |
環(huán)繞通知(Around Advice) | 包圍一個連接點(diǎn)(join point)的通知 |
使用方式:
名稱 | 配置中使用 | 注解中使用 |
---|---|---|
前置通知(Before advcie) | <aop:aspect>里面使用<aop:before> | @Before |
返回后通知(After returning advice) | <aop:aspect>里面使用<aop:after-returning> | @AfterReturning |
拋出異常后通知(After throwing advice) | <aop:aspect>里面使用<aop:after-throwing> | @AfterThrowing |
后通知(After(finally)advice) | aop:aspect>里面使用<aop:after>元素 | @After |
環(huán)繞通知(Around Advice) | <aop:aspect>里面使用<aop:around> | @Around |
通知執(zhí)行順序:
前置通知→環(huán)繞通知連接點(diǎn)之前→連接點(diǎn)執(zhí)行 1→ 環(huán)繞通知連接點(diǎn)之后→返回通知→后通知
2→ (如果發(fā)生異常)異常通知→后通知
pointcut
切入點(diǎn)表達(dá)式 | 說明 |
---|---|
execution | 用于匹配方法執(zhí)行的連接點(diǎn)
|
within | 用于匹配指定類型內(nèi) 的方法執(zhí)行 |
this | 用于匹配當(dāng)前AOP代理對象類型 的執(zhí)行方法;this中使用的表達(dá)式必須是完整類名啦扬,不支持通配符 |
target | 用于匹配當(dāng)前目標(biāo)對象類型 的執(zhí)行方法中狂;注意是目標(biāo)對象的類型匹配,這樣就不包括引入接口也類型匹配扑毡;注意target中使用的表達(dá)式必須是完整類名胃榕,不支持通配符; |
args | 用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)為指定類型 的執(zhí)行方法瞄摊;參數(shù)類型列表中的參數(shù)必須是完整類名勋又,通配符不支持;args屬于動態(tài)切入點(diǎn)换帜,這種切入點(diǎn)開銷非常大楔壤,非特殊情況最好不要使用; |
@within | 用于匹配所有持有指定注解類型內(nèi)的方法惯驼;注解類型也必須是完整類名蹲嚣; |
@target | 用于匹配當(dāng)前目標(biāo)對象類型的執(zhí)行方法递瑰,其中目標(biāo)對象持有指定的注解;注解類型也必須是完整類名隙畜; |
@args | 用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)持有指定注解的執(zhí)行抖部;注解類型也必須是完整類名; |
@annotation | 用于匹配當(dāng)前執(zhí)行方法持有指定注解的方法议惰;注解類型也必須是完整類名慎颗; |
bean | Spring AOP擴(kuò)展的,AspectJ沒有對于指示符换淆,用于匹配特定名稱的Bean對象的執(zhí)行方法哗总; |
reference pointcut | 表示引用其他命名切入點(diǎn),只有注解風(fēng)格支持倍试,XML風(fēng)格不支持讯屈。 |
定義
一個切入點(diǎn)通過一個普通的方法定義來提供,使用@Pointcut注解
方法返回類型必須為void
eg.
定義一個名為‘a(chǎn)nyOldTransfer’县习,這個切點(diǎn)將匹配任何名為”transfer“的方法執(zhí)行@Pointcut("execution(* transfer(..))")//the pointcut expression
@Pointcut("execution(* transfer(..))")//the pointcut expression
private void anyOldTransfer(){}//the pointcut signature
組合pointcut
- 切入點(diǎn)表達(dá)式可以通過
&&涮母、||、!
進(jìn)行組合躁愿,也可以通過名字引入切入點(diǎn)表達(dá)式 - 通過組合叛本,可以建立更加復(fù)雜的切入點(diǎn)表達(dá)式
@Pointcut("execution(public * (..))")
private void anyPublicOperation(){}
@Pointcut("within(com.xyz.someapp.trading...)")
private void inTrading(){}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation(){}
匹配語法
符號 | 匹配 |
---|---|
* | 匹配任何數(shù)量字符
|
.. | 匹配任何數(shù)量字符的重復(fù) ,如在類型模式中匹配任何數(shù)量子包彤钟;而在方法參數(shù)模式中匹配任何數(shù)量參數(shù)来候。 |
+ | 匹配指定類型的子類型 ;僅能作為后綴放在類型模式后邊逸雹。 |
例子 | 匹配 | 不匹配 |
---|---|---|
java.lang.String | 匹配String類型 | ? |
java.*.String | 匹配java包下的任何“一級子包”下的String類型营搅;如匹配java.lang.String | 但不匹配java.lang.ss.String |
java..* | 匹配java包及任何子包下的任何類型;如匹配java.lang.String梆砸、java.lang.annotation.Annotation | ? |
java.lang.*ing | 匹配任何java.lang包下的以ing結(jié)尾的類型 | ? |
java.lang.Number+ | 匹配java.lang包下的任何Number的自類型转质;如匹配java.lang.Integer,也匹配java.math.BigInteger | ? |
實(shí)現(xiàn)手法
環(huán)境
Spring中AOP代理由Spring的IOC容器負(fù)責(zé)生成帖世、管理休蟹,其依賴關(guān)系也由IOC容器負(fù)責(zé)管理,要在Spring 中使用AOP日矫,還需要加入這兩個jar包
1赂弓、aopalliance.jar
2、aspectjweaver.jar
Spring中 AOP中的兩種代理
- Java動態(tài)代理
默認(rèn)使用 哪轿〖鹫梗可以為任何接口實(shí)例創(chuàng)建代理了 - CGLIB
當(dāng)需要代理的類不是代理接口的時候,Spring會切換為使用CGLIB代理缔逛,也可強(qiáng)制使用CGLIB
Spring AOP的使用步驟
- 定義具體業(yè)務(wù)邏輯模塊(目標(biāo)對象)
- 定義切面(即實(shí)現(xiàn)通知邏輯)
- 實(shí)現(xiàn)切面邏輯
兩種方式的例子
兩種方式备埃,基于Schema或@AspectJ姓惑。
一. 基于Schema的Spring AOP
第一步、定義具體業(yè)務(wù)模塊(目標(biāo)對象)
兩個業(yè)務(wù)模塊都是基于接口
TestAOPDaoImpl .java
public class TestAOPDaoImpl implements TestAOPDao{
@Override
public void addUser() {
System.out.println("添加成功");
}
}
TestAOPServiceImpl.java
public class TestAOPServiceImpl implements TestAOPService{
@Autowired
private TestAOPDao testAOPDao;
@Override
public void addUser() {
testAOPDao.addUser();
}
}
第二步和第三步按脚、 定義切面(即實(shí)現(xiàn)通知邏輯)
aop創(chuàng)建代理后會返回一個 連接點(diǎn)JointPoint
于毙,然后在通知中可以通過該連接點(diǎn)實(shí)現(xiàn)我們的切面邏輯
日志切面
public class LogAdivice{
public void myBeforeAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——前置通知——" + methodname);
}
public void myAfterAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——后置通知——" + methodname);
}
//環(huán)繞通知將決定要不要執(zhí)行連接點(diǎn)
public void myAroundAdivice(ProceedingJoinPoint point) throws Throwable{
System.out.println("環(huán)繞通知,執(zhí)行代碼前");
//選擇執(zhí)行
point.proceed();
System.out.println("環(huán)繞通知辅搬,執(zhí)行代碼后");
}
}
時間切面:
public class TimeAdvice {
public void timeBefore(){
System.out.println("beforeTime = " + System.currentTimeMillis());
}
public void timeAfter(){
System.out.println("afterTime = " + System.currentTimeMillis());
}
}
在applicationContext中配置切面:
<context:annotation-config/>
<bean id="testAOPDao" class="com.ssh.dao.impl.TestAOPDaoImpl"/>
<bean id="testAOPService" class="com.ssh.service.impl.TestAOPServiceImpl"/>
<bean id="logAdivice" class="com.ssh.adivice.LogAdivice"/>
<bean id="timeAdvice" class="com.ssh.adivice.TimeAdvice"/>
<aop:config>
<!-- 配置一個切面 -->
<aop:aspect id="logaop" ref="logAdivice" order="2">
<!-- 定義切入點(diǎn)唯沮,表示對service的所有方法都進(jìn)行攔截 -->
<aop:pointcut expression="execution(* com.ssh.service.TestAOPService.*(..))" id="testpointcut"/>
<!-- 定義前置通知 -->
<aop:before method="myBeforeAdivice" pointcut-ref="testpointcut"/>
<!-- 定義后置通知 -->
<aop:after-returning method="myAfterAdivice" pointcut-ref="testpointcut"/>
<!-- 定義環(huán)繞通知 -->
<aop:around method="myAroundAdivice" pointcut-ref="testpointcut"/>
</aop:aspect>
<!-- 定義另一個切面 -->
<aop:aspect id="timeaop" ref="timeAdvice" order="1">
<!-- 定義切入點(diǎn),表示對service的所有方法都進(jìn)行攔截 -->
<aop:pointcut expression="execution(* com.ssh.service.TestAOPService.*(..))" id="testpointcut"/>
<!-- 定義前置通知 -->
<aop:before method="timeBefore" pointcut-ref="testpointcut"/>
<!-- 定義后置通知 -->
<aop:after-returning method="timeAfter" pointcut-ref="testpointcut"/>
</aop:aspect>
</aop:config>
當(dāng)有多個切面時堪遂,Spring默認(rèn)是按照切面定義的順序來執(zhí)行介蛉,也可以通過order屬性來配置切面的執(zhí)行屬性,order=1 早于 order=2執(zhí)行
測試
public class AOPTest {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
TestAOPService service = (TestAOPService) context.getBean("testAOPService");
service.addUser();
}
}
二. 基于@AspectJ注解的AOP實(shí)現(xiàn)
啟用@AsjectJ支持
在applicationContext.xml中配置下面一句:
<aop:aspectj-autoproxy />
第一步溶褪、定義具體業(yè)務(wù)邏輯模塊(目標(biāo)對象)
第一步和基于Schema的一樣
TestAOPDaoImpl .java
public class TestAOPDaoImpl implements TestAOPDao{
@Override
public void addUser() {
System.out.println("添加成功");
}
}
TestAOPServiceImpl.java
public class TestAOPServiceImpl implements TestAOPService{
@Autowired
private TestAOPDao testAOPDao;
@Override
public void addUser() {
testAOPDao.addUser();
}
}
第二步和第三步 定義切面(即實(shí)現(xiàn)通知邏輯)
重點(diǎn)是定義切入點(diǎn)
@Aspect
public class LogAdivice{
//定義一個方法作為切入點(diǎn)id
@Pointcut("execution(* com.ssh.service.TestAOPService.*(..))")
private void allMethod(){}
@Before("allMethod()")
public void myBeforeAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——前置通知——" + methodname);
}
@AfterReturning("allMethod()")
public void myAfterAdivice(JoinPoint joinPoint){
String classname = joinPoint.getTarget().getClass().getSimpleName();
String methodname = joinPoint.getSignature().getName();
System.out.println(classname + " ——后置通知——" + methodname);
}
//環(huán)繞通知將決定要不要執(zhí)行連接點(diǎn)
@Around("allMethod()")
public void myAroundAdivice(ProceedingJoinPoint point) throws Throwable{
System.out.println("環(huán)繞通知币旧,執(zhí)行代碼前");
//執(zhí)行
point.proceed();
System.out.println("環(huán)繞通知,執(zhí)行代碼后");
}
}
在applicationContext的配置:
<!-- 打開自動掃描(隱式打開注解管理器) -->
<!-- <context:component-scan base-package="com.ssh"/> -->
<context:annotation-config/>
<bean id="testAOPDao" class="com.ssh.dao.impl.TestAOPDaoImpl"/>
<bean id="testAOPService" class="com.ssh.service.impl.TestAOPServiceImpl"/>
<bean id="logAdivice" class="com.ssh.adivice.LogAdivice"/>
<bean id="timeAdvice" class="com.ssh.adivice.TimeAdvice"/>
<!-- 打開aop注解管理器 -->
<aop:aspectj-autoproxy/>
參考文章在各個小結(jié)中有鏈接