1. 程式人生 > 實用技巧 >筆記9:spring

筆記9:spring

Spring

---程式的耦合

耦合:程式間的依賴關係
	類間依賴
	方法間依賴
解耦:降低程式間的依賴關係
	實際開發中應做到:編譯期不依賴,執行時才依賴
	第一步:使用反射建立物件,而避免使用new關鍵字
	第二步:通過讀取配置檔案來獲取要建立的物件全限定類名

----工廠模式解耦

工廠:一個建立Bean物件的工廠
Bean:在計算機英語中,有可從用元件的含義	
JavaBean:用java英語編寫的可重用元件
	javabean > 實體類
它就是建立文明的service和dao物件的
	第一個:需要一個配置檔案來配置我們的service和dao
		配置的內容:1-唯一標識=全限定類名(key=value)
	第二個:通過讀取配置檔案中配置的內容,反射建立物件
		配置檔案可以是xml也可以是properties
單例物件:物件被建立一次,類內成員只初始化一次,從始至終只有一個物件
	執行緒問題:多個執行緒訪問修改同一物件容易出問題
	----實現單例即在通過反射建立物件時只能呼叫一次
多例物件:物件被建立多次,
	效率問題:執行效率沒有單例物件高
****
	靜態程式碼塊隨著類載入只執行一次,後續通過類來建立物件時靜態程式碼塊不再執行,
	即在靜態程式碼塊中為類屬性賦值後,每次建立的物件對應的屬性都會先有相同的值。
****

----工廠模式
====使用例子---------------------------------------
	----------配置檔案
	accountService=com.xiaoai.t2.service.impl.AccountServiceImpl
	accountDao=com.xiaoai.t2.dao.impl.AccountDaoImpl
	----------類
	public class BeanFactory {
		//定義一個properties物件
		private  static Properties props;
		//定義一個map,用於存放我們要建立的物件,我們稱之為容器
		private static Map<String, Object> beans;
		//使用靜態程式碼塊為properties物件賦值
		static {
			try {
				//例項化物件
				 props = new Properties();
				 //獲取配置檔案流物件
				 InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
				 props.load(in);
				 
				 //例項化容器
				 beans = new HashMap<String, Object>();
				 //取出properties配置檔案中所有的key
				 Enumeration keys = props.keys();
				 //遍歷列舉
				 while (keys.hasMoreElements()) {
					String key = keys.nextElement().toString();
					//根據key獲取value
					String beanPath = props.getProperty(key);
					//反射建立物件
					Object value = Class.forName(beanPath).newInstance();
					//把key和value存入容器中
					beans.put(key, value);
				}
				 
			} catch (Exception e) {
				throw new ExceptionInInitializerError("初始化properties失敗!");
			}
		}
	//	/**
	//	 * 根據bean的名稱獲取bean物件(反射建立bean)即多例
	//	 * @param beanName
	//	 * @return
	//	 */
	//	public static Object getBean(String beanName) {
	//		Object bean = null;
	//		try {
	//			String beanPath = props.getProperty(beanName);
	//			bean = Class.forName(beanPath).newInstance();
	//		} catch (Exception e) {
	//			e.printStackTrace();
	//		}
	//		return bean;
	//	}
		/**
		 * 根據bean的名稱獲取bean物件(工廠(容器)獲取bean)即每次獲取的都是同一物件,單例
		 * @param beanName
		 * @return
		 */
		public static Object getBean(String beanName) {
			return beans.get(beanName);
		}
	}
====使用例子---------------------------------------

ioc(控制反轉)

----Inversion of Control 把建立物件的權利交給框架或交給工廠	
----作用:降低程式間的依賴關係也叫削減計算機程式的耦合
通過new 來建立物件即本身建立具有獨立自主控制權,
而通過工廠建立(即不通過new建立)物件,工廠是通過所給予的key所對應全限定類名控制,無法獨立自主控制,
控制權發生了轉移則成為控制反轉(ioc),把控制權交個了工廠來幫忙建立物件,帶來好處:降低程式間的依賴關係也叫削減計算機程式的耦合

----spring中Ioc入門

----基於XML的ioc配置
	----建立spring的xmp配置檔案引入依賴
		<?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>
	----配置bean的id(即key)class(即value==全限定類名)
		====使用例子---------------------------------------
			<!-- 把物件的建立交給spring來管理 -->
			<bean id="accountService" class="com.xiaoai.t3spring1.service.impl.AccountServiceImpl"></bean>
			<bean id="accountDao" class="com.xiaoai.t3spring1.dao.impl.AccountDaoImpl"></bean>
		====使用例子---------------------------------------
	----載入配置檔案
		/*
			獲取核心容器物件
			ApplicationContext三個常用實現類:
				---【ClassPathXmlApplicationContext()】		可載入類路徑下配置檔案,要求配置檔案必須在類路徑下,不在則無法載入
				---【FileSystemXmlApplicationContext()】 	可載入磁碟任意路徑下的配置檔案(必須有訪問許可權)
				---【AnnotationConfigApplicationContext()】	用於讀取註解建立容器的
		*/
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	----通過bean的id獲取物件
		IAccountService as = (AccountServiceImpl) ac.getBean("accountService");//獲取物件方法一
		IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);//獲取物件方法二
	
	掃描配置檔案:
		1、spring的xml配置檔案中新增標籤    
			<context:property-placeholder ignore-unresolvable="true" location="classpath:jdbcConfig.properties"/> 
		2、通過 util:properties 標籤實現配置檔案載入
			<util:properties id="util_Spring"  local-override="true" location="classpath:jdbcConfig.properties"/>
		3、通過註解 @PropertySource 註解實現配置檔案載入
			@PropertySource("classpath:jdbcConfig.properties")
		....等等
		
----基於註解的ioc配置
	----為需要管理的類加上相應的註解
		例如:@Component
	----在spring配置檔案中掃描對應的包
		<!--
			告知spring在建立容器時要掃描的包,即掃描註解。
			配置所需要的標籤不是在beans的約束中,而是一個 名稱為context名稱控制元件和約束中,
			所以需要先新增對應約束,然後在掃描。
		-->
		如:<context:component-scan base-package="com.xiaoai.t4annotation"></context:component-scan>
	----載入配置檔案
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	----通過bean的id獲取物件
		IAccountService as = (AccountServiceImpl) ac.getBean("accountService");
		如果註解value配置相應的值(即相當於id)則要通過其來獲取,沒有配置預設為類名首字母小寫

----核心容器兩個介面引發的問題:
	----ApplicationContext:構建核心容器時,建立物件採取策略時採用立即載入的方式,也就是說,只要一讀取完配置檔案馬上就建立配置檔案中配置的bean
		單例物件適用		開發中更多是採用此介面
	----BeanFactory:構建核心容器時,建立物件採取的策略時採用延遲載入的方式。也就是說,什麼時候根據id獲取物件時才真正建立物件
		多例物件適用

----springframework對bean的管理細節

----建立bean的三種方法	
	【類預設建構函式建立物件】 spring配置檔案中用bean標籤配以id和class屬性且沒有其他屬性和標籤時。
		採用的就是預設建構函式建立bean物件,此時如果類中沒有預設建構函式,則物件無法建立
		例子:<bean id="accountService" class="com.xiaoai.t3spring1.service.impl.AccountServiceImpl"></bean>
	【工廠中普通方法建立物件】即使用某類中方法建立物件,並存入spring容器。
		例子:<bean id="instanceFacroty" class="com.xioaai.factory.InstanceFacroty"></bean>
			  <bean id="accountService" factory-bean="instanceFacroty" factory-method="getAccountService"></bean>
	【工廠中靜態方法建立物件】使用某個類中的靜態方法建立物件並存入srping容器
		例子:<bean id="accountService" class="com.xioaai.factory.StaticFacroty" factory-method="getAccountService"></bean>
----bean的作用範圍
	bean標籤scope屬性:用於指定bean的作用範圍,scope屬性取值: 
		1-singleton 		單例的(預設值)
		2-prototype 		多例的(建立多個物件)
		3-request			作用於web應用的請求範圍
		4-session			作用於web應用的會話範圍
		5-global-session	作用於叢集環境的會話範圍(全域性會話範圍),當不是叢集環境時,它就是session
		
----bean物件的生命週期
	單例物件:
		出生==當容器建立時	
		活著==重要容器還活著,物件一直活著		
		死亡==容器銷燬,物件死亡
		總結:單例物件的生命週期和容器相同
	多例物件:
		出生==當我們使用物件時spring建立物件	
		活著==物件只要是在使用過程中就一直活著
		死亡==當物件長時間不用且沒有別的物件引用時,由java垃圾回收機制銷燬

----spring依賴注入(Dependency Injection)

依賴關係(在當前類需要用到其他類物件)的管理都交給spring來維護,
由spring為我們提供,我們只需在配置檔案中說明。依賴關係的維護即稱為依賴注入,

能注入資料有三類:
	1-基本型別和String
	2-其他bean型別(在配置檔案中或者註解配置過的bean)
	3-複雜型別/集合型別
		====使用例子---------------------------------------
			//set方法---------------------------------
			public void setMyStrs(String[] myStrs) {
				this.myStrs = myStrs;
			}
			public void setMyList(List<String> myList) {
				this.myList = myList;
			}
			public void setMySet(Set<String> mySet) {
				this.mySet = mySet;
			}
			public void setMyMap(Map<String, String> myMap) {
				this.myMap = myMap;
			}
			public void setMyProps(Properties myProps) {
				this.myProps = myProps;
			}
			<!-- spring配置檔案配置------------------------------------
				複雜資料型別(陣列/集合)的注入
				用於給lis結構集合注入的標籤:list array set
				用於給map結構集合注入的標籤:map props
				結論:結構相同,標籤可互換
					即:<property name="mySet"><!--list標籤給set型別注入-->
							<list>
								<value>setAAA</value>
								<value>setBBB</value>
								<value>setCCC</value>
							</list>
						</property>
						<property name="myProps"><!--map標籤可以給Properties型別注入-->
							<map>
								<prop key="prop1"><value>propAAA</value></prop>
								<prop key="prop2"><value>propBBB</value></prop>
							</map>
						</property>
			 -->
			<bean id="accountService3" class="com.xiaoai.t3spring1.service.impl.AccountServiceImpl3">
				<property name="myStrs">
					<!-- 陣列資料注入 -->
					<array>
						<value>AAA</value>
						<value>BBB</value>
						<value>CCC</value>
					</array>
				</property>
				<property name="myList">
					<!-- 集合 -->
					<list>
						<value>listAAA</value>
						<value>listBBB</value>
						<value>listCCC</value>
					</list>
				</property>
				<property name="mySet">
					<set>
						<value>setAAA</value>
						<value>setBBB</value>
						<value>setCCC</value>
					</set>
				</property>
				<property name="myMap">
					<map>
						<entry key="map1" value="mapAAA"></entry>
						<entry key="map2" value="mapBBB"></entry>
						<entry key="map3" value="mapCCC"></entry>
					</map>
				</property>
				<property name="myProps">
					<props>
						<prop key="prop1">propAAA</prop>
						<prop key="prop2">propBBB</prop>
					</props>
				</property>
			 </bean>
		====使用例子---------------------------------------

注入方式三種:
	----1-使用建構函式提供
		====使用例子---------------------------------------
			//構造方法
			public AccountServiceImpl(String name, Integer age, Date birthday) {
				this.name = name;
				this.age = age;
				this.birthday = birthday;
			}
			<!--配置檔案配置
				一、 建構函式注入
				 使用標籤:bonstructor-arg	屬性如下:
					type:用於指定要注入的資料的資料型別,該資料型別也是建構函式中某個或某些引數的型別
					index:用於指定要注入的資料給建構函式中指定索引位置的引數賦值。索引從0開始
					name:用於指定給建構函式中指定名稱的引數賦值		常用
					===========以上三個用於指定給構造中那個引數賦值============================
					value:用於提供基本型別和string型別的資料
					ref:用於指定其他的bean型別資料,它指的就是在spring的Ioc核心容器中出現過的bean物件
				 標籤出現位置:bean標籤內
				 優勢:在獲取bean物件時,注入資料時必須的操作,否則物件無法建立成功
				 弊端:改變了bean物件的例項化方式,使我們在建立物件時,如果用不到這些資料,也必須提供
			 -->
			 <bean id="accountService" class="com.xiaoai.t3spring1.service.impl.AccountServiceImpl">
				<constructor-arg type="java.lang.String" value="泰斯特" ></constructor-arg>
				<constructor-arg name="age" value="18" ></constructor-arg>
				<constructor-arg name="birthday" ref="now" ></constructor-arg>
			 </bean>
			<!-- 配置一個日期物件 -->
			<bean id="now" class="java.util.Date"></bean>
		====使用例子---------------------------------------
	----2-使用set方法提供
		====使用例子---------------------------------------
			//物件中的set方法
			public void setName(String name) {
				this.name = name;
			}
			public void setAge(Integer age) {
				this.age = age;
			}
			public void setBirthday(Date birthday) {
				this.birthday = birthday;
			}
			<!-- 
				二、set方法注入		更常用該方式注入資料
				使用標籤:property	屬性如下:
					name:用於指定注入資料時所呼叫的set方法名稱
					value:用於提供基本型別和String型別資料
					ref:用於指定其他bean型別資料。它指的是在spring的Ioc核心容器中出現過的bean物件
				出現位置:bean標籤內部
				優勢:建立物件時沒有明確的限制,可以直接使用預設的建構函式
				弊端:如果有某個成員必須有值,則獲取物件時有可能set方法沒有執行
			 -->
			 <bean id="accountService2" class="com.xiaoai.t3spring1.service.impl.AccountServiceImpl2">
				<property name="name" value="TEST"></property>
				<property name="age" value="19"></property>
				<property name="birthday" ref="now"></property>
			 </bean>
			<!-- 配置一個日期物件 -->
			<bean id="now" class="java.util.Date"></bean>
		====使用例子---------------------------------------
	----3-使用註解提供

----spring中Ioc常用註解

----ioc容中儲存型別:map型別。key==id,value=全限定類名(即物件)
曾經xml配置:<bean id="accountDao" class="com.xiaoai.t3spring1.dao.impl.AccountDaoImpl"
				scope="" init-method="" destroy-method="">
				<property name="" value=""/ref=""></property>
			</bean>
註解四類:
	1-建立物件:作用和xml配置檔案中編寫一個<bean>標籤實現功能一樣
		【@Component】:用於把當前類物件存入spring容器中。
			屬性value:相對於配置中的id	,不寫時預設值為當前類名,且首字母改小寫。
		【@Controller】:一般用於表現層
		【@Service】:一般用於服務層
		【@Repository】:一般用於持久層
		以上三個註解作用和屬性與Component一摸一樣。
		它們三個時spring框架為我們提供明確的三層使用的註解。
		使我們的三層物件更加清晰
	2-注入資料:作用和<bean>標籤內寫一個<property>標籤作用一樣
		【@Autowired】:自動按照型別注入。只要容器中有唯一的bean物件型別和要注入的變數型別匹配,就可以注入成功。
			出現位置:可以是成員變數,也可以是方法上。
			多匹配問題:如果ioc容器中有多個匹配的bean型別時,先按照型別圈定出匹配的bean物件,
					然後根據變數名稱在圈定出的bean物件查詢,如果找到也能注入,如果找不到則報錯。
			====使用例子---------------------------------------
				//使用註解將類交個ioc管理
				@Repository("accountDao")
				public class AccountDaoImpl implements IAccountDao {}
				//通過註解注入物件
				@Autowired
				private IAccountDao accountDao;//當有多個bean匹配時,由於上面有bean名稱叫accountDao,變數名也叫accountDao,所以可以注入
			====使用例子---------------------------------------
		【@Qualifier】:作用為按照類中注入的基礎之上再按照名稱注入。它在給類成員注入時不能單獨使用,
						要和@Autowired一起用。但是在給方法引數注入時可以(給方法引數注入相當於@Resource一樣可通過名稱直接)
			屬性value:指定注入bean物件的id
			====使用例子---------------------------------------
			@Autowired
			@Qualifier("accountDao2")
			private IAccountDao accountDao;
			---------給方法引數注入時
			@Bean(name = "runner")
			@Scope("prototype")
			public QueryRunner createQueryRunner(@Qualifier("ds2")DataSource dataSource) {
				return new QueryRunner(dataSource);
			}
			====使用例子---------------------------------------
		【@Resource】:直接按照bean的id給某物件注入資料,它可獨立使用。
			屬性name:用於指定注入bean物件的id
		***以上三個註解都只能注入其他bean型別的資料,而基本型別和String型別無法使用上述註解實現。另外集合型別的注入只能通過xml來實現。
		
		【@value】:用於注入基本型別和string型別的資料
			屬性value:用於指定資料的值。它可以使用spring中SpEl(也就是spring的el表示式)
				使用SpEl時可以先把配置檔案加入容器:如在配置類上加註解@PropertySource("classpath:jdbcConfig.properties")
				SpEl的寫法:${表示式}	
	3-改變作用範圍:作用和在<bean>標籤中寫一個scope屬性實現功能一樣
		【@Scope】:用於指定的bean的作用範圍
			位置:可寫在類上面、配置類方法上
			屬性value:指定範圍的取值。常用取值:singleton(預設)、prototype
	4-生命週期有關:作用和在<bean>標籤中使用 init-method和destroy-method屬性一樣
		寫在方法上
		【@PostConstruct】:用於指定初始化方法
		【@ProDestroy】:用於指定銷燬方法
		
Spring中的新註解
	【@Configuration】 指定當前類為一個配置類
		細節:當配置類作為AnnotationConfigApplicationContext物件建立的引數時,該註解可以不寫
		====使用例子---------------------------------------
			//此時SpringConfiguration類上的@Configuration可省略不寫
			ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
		====使用例子---------------------------------------
	【@ComponentScan】 用於通過註解指定spring在建立容器時要掃描的包
		屬性basePackages:它和配置檔案掃描包中屬性basePackages的作用一樣,都是用於指定建立容器時要掃描的包
			我們使用此註解就等同於在xml中配置了:
			<context:component-scan base-package="com.xiaoai.t5dbutils"></context:component-scan>	
	【@Bean】 用於把當前方法的返回值作為bean物件存入spring的ioc容器中
		屬性name:用於指定bean的id,當不指定時預設值為當前方法名稱
		細節:當我們使用註解配置方法時,如果方法有引數,spring框架會去容器中查詢有沒有可用的bean物件
			查詢方式和@Autowired註解一樣
	【@Import】 匯入其他配置類
		即在主配置類上面通過該註解匯入其他小塊配置類,這樣在建立AnnotationConfigApplicationContext物件時,
		既不用把其他小塊配置類當引數傳入,也不需要在其他小塊配置類上新增@Configuration註解
		使用@Import註解後,有@Import註解的類就是父類,匯入的都是子配置類
		====使用例子---------------------------------------
			@Configuration
			@ComponentScan(basePackages = {"com.xiaoai.t5dbutils"})
			@Import(JdbcConfig.class)
			public class SpringConfiguration {}
			------------其他小塊配置類
			public class JdbcConfig {
				@Bean(name = "runner")
				@Scope("prototype")
				public QueryRunner createQueryRunner(DataSource dataSource) {
					return new QueryRunner(dataSource);
				}
				@Bean(name = "dataSource")
				public DataSource createDataSource() { 
					ComboPooledDataSource ds;
					try {
						ds = new ComboPooledDataSource();
						ds.setDriverClass("com.mysql.jdbc.Driver");
						ds.setJdbcUrl("jdbc:mysql://localhost:3306/jfs_mybatis");
						ds.setUser("root");
						ds.setPassword("root");
					} catch (Exception e) {
						throw new RuntimeException();
					}
					return ds;
				}
			}
			//獲取容器
			ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
		====使用例子---------------------------------------
	【@PropertySource】  用於指定properties檔案的位置
		屬性value:指定檔案的名稱和路徑
			關鍵字classpath:表示類路徑下
		@PropertySource("classpath:jdbcConfig.properties")//有包可以這樣寫:classpath:conf/spring/jdbcConfig.properties
		public class SpringConfiguration {}

----spring整合junit

1-匯入spring整合junit的jar	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>5.0.2.RELEASE</version>
	</dependency>
2-使用junit提供的一個註解把junit原有的main方法替換了,替換成spring提供的
	【@RunWith】把junit原有的main方法替換了,替換成spring提供的
	例如:@RunWith(SpringJUnit4ClassRunner.class)
3-告知spring的執行器,spring和ioc建立是基於xml還是註解的,並說明位置。
	【@ContextConfiguration】
		屬性locations:指定xml檔案位置,加上classpath關鍵字,表示在類路徑下
		屬性classes:指定註解類所在地位置
	例如:@ContextConfiguration(classes = SpringConfiguration.class)
當使用spring5.x版本的時候,要求junit的jar必須是4.12及以上

aop(Aspect Oriented Programming) 面向物件程式設計

  類似使用動態代理增強方法

實現方式:動態代理
spring配置中可以手動控制是通過基於介面還是基於子類的動態代理
----連線點(Joinpoint):指那些被攔截到的點。指方法,因為spring只支援方法型別的連線點
----切入點(Pointcut):被動態代理增強的方法(所有的切入點都是連線點,但不是所有連線點都是切入點)
----通知/增強(Advice):指攔截到Joinpoint之後要做的事情就是通知
	通知型別:前置通知,後置通知,異常通知,最終通知,環繞通知
	 public IAccountService getAccountService(){
		IAccountService proxyAccountService = (IAccountService) Proxy.newProxyInstance(
				accountService.getClass().getClassLoader(),
				accountService.getClass().getInterfaces(), new InvocationHandler() {
					@Override	//整個invoke方法在執行就是環繞通知
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					   Object rtValue = null;
						try {
							//開啟事務
							txManager.beginTransaction();				//即前置通知
							//執行操作
							rtValue = method.invoke(accountService,args);//切入點方法具體執行:環繞通知中有明確的切入點方法呼叫
							//提交事務
							txManager.commit();							//後置通知
							//返回結果
							return rtValue;
						}catch (Exception e){
							//回滾事務
							txManager.rollback();						//異常通知
							throw new RuntimeException(e);
						}finally {
							//關閉連線
							txManager.close();							//最終通知
						}
					}
				});
		return proxyAccountService;
	}
	後置通知和異常通知永遠只能執行一個
----引介(Introduction):特殊的通知,在不修改程式碼前提下,引介可以在執行期為類動態地新增一些方法Field。
----目標物件(Target):代理的目標物件
----織入(Weaving):指把增強應用到目標物件來建立新的代理物件的過程
----代理(Proxy):一個類被aop織入增強後,就產生一個結果代理類。
----切面(Asect):切入點和通知(引介)的結合
明確:
	開發階段	我們	編寫核心業務程式碼(開發主線)	公用程式碼抽取,製作成通知	配置檔案中,宣告切入點與通知關係,即切面
	執行階段	spring框架監控切入點方法執行,一執行代理機制建立物件,根據通知類別,在代理物件對應位置將通知織入,完成完整程式碼邏輯

-----基於xml配置aop

spring中基於xml的aop配置步驟:
----把通知的Bean交給spring來管理
----使用<aop:config>標籤名開始aop配置
----使用<aop:aspect>標籤名配置切面
	屬性id:是給切面提供一個唯一標識
	屬性ref:指定通知類bean的id
----<aop:aspect>標籤內部使用對應標籤配置通知型別並繫結切入點方法
	屬性method:用於指定通知bean那個方法時相應的通知
	屬性pointcut:用於指定切入點表示式,該表示式含義指的是對代理物件中那些方法增強
		jar包:org.aspectj可以用來幫我們解析切入點表示式
		切入點表示式寫法:
			--關鍵字execution(表示式[寫法:訪問修飾符 返回值 包名.包名...類名.方法名(引數列表)])
				如表示式:public void com.xiaoai.t8aop.service.impl.AccountServiceImpl.saveAccount()
			--訪問修飾符可省
				void com.xiaoai.t8aop.service.impl.AccountServiceImpl.saveAccount()
			--返回值可以使用萬用字元*,表示任意返回值 
				* com.xiaoai.t8aop.service.impl.AccountServiceImpl.saveAccount()
			--包名可以使用萬用字元*,但有幾級包就需要.幾個*
				* *.*.*.*.*.AccountServiceImpl.saveAccount()
			--包名可以使用..表示當前包及其子包
				* *..AccountServiceImpl.saveAccount()
			--類名和方法名都可以使用萬用字元*來表示
				* *..*.*()
			--引數列表可以直接寫型別:基本型別直接寫名稱(如:int)	引用型別寫包名.類名	(如:java.lang.String)
						* *..*.*(int)或* *..*.*(java.lang.String)
					可以使用萬用字元表示任意型別引數,但必須有引數(即*無法匹配沒有引數的方法)
						* *..*.*(int)或* *..*.*(*)
					可以使用..表示有無引數都可以
						* *..*.*(int)或* *..*.*(..)
			--全通配寫法
				* *..*.*(..)
				
		實際開發中切入點表示式的通常寫法:切到代理物件類下的所有方法
			如:* com.xiaoai.t8aop.service.impl.*.*(..)
	====使用例子---------------------------------------
		<!--配置spirng的ioc,把service物件配置進來-->
		<bean id="accountService" class="com.xiaoai.t8aop.service.impl.AccountServiceImpl" ></bean>

		<!--配置Logger類-->
		<bean id="logger" class="com.xiaoai.t8aop.utils.Logger"></bean>
		
		<!--配置aop-->
		<aop:config>
			<!--配置切面-->
			<aop:aspect id="logAdvice" ref="logger">
				<!--配置前置通知,並且建立通知方法和切入點方法的關聯-->
				<aop:before method="printLog" pointcut="execution(public void com.xiaoai.t8aop.service.impl.AccountServiceImpl.saveAccount())"></aop:before>

			</aop:aspect>
		</aop:config>
	====使用例子---------------------------------------

----可以在切面內配置切入點表示式並通過pointcut-ref屬性引用
	====使用例子---------------------------------------
		<!--配置aop-->
		<aop:config>
			<!--
				配置切入點表示式
					屬性id:用於指定表示式唯一標識
					屬性expression:用於指定表示式內容
				此標籤寫在 <aop:aspect>標籤內部只能當前切面使用。
				它還可以寫在<aop:aspect>標籤外面,此時變成了所有切面都可使用,但必須放置在切面前面
			-->
			<aop:pointcut id="pt1" expression="execution(* com.xiaoai.t8aop.service.impl.*.*(..))"/>
			<!--配置切面-->
			<aop:aspect id="logAdvice" ref="logger">
				<!--通過pointcut—ref屬性引用切入點表示式-->
				<aop:before method="before" pointcut-ref="pt1"></aop:before>
				<!--後置通知-->
				<aop:after-returning method="afterReturning" pointcut-ref="pt1"></aop:after-returning>
				<!--異常通知-->
				<aop:after-throwing method="afterThrowing" pointcut-ref="pt1"></aop:after-throwing>
				<!--最終通知-->
				<aop:after method="after" pointcut-ref="pt1"></aop:after>
				
			</aop:aspect>
		</aop:config>
	====使用例子---------------------------------------

----環繞通知
	<aop:pointcut id="pt1" expression="execution(* com.xiaoai.t8aop.service.impl.*.*(..))"/>
	<!--配置切面-->
	<aop:aspect id="logAdvice" ref="logger">
		<!--環繞通知-->
		<aop:around method="around" pointcut-ref="pt1"></aop:around>
	</aop:aspect>
	
	問題:配置了環繞通知後,切入點方法沒有執行,而通知方法執行了
	分析:通過對比動態代理中的環繞通知,發現動態代理的環繞通知有明確的切入點方法呼叫,而環繞通知方法沒有
	解決:spring框架提供了一個介面,ProceedingJoinPoint。該介面有一個方法proceed(),此方法就相當於明確呼叫切入點方法。
		該介面可以作為環繞通知的方法引數,在程式執行時,spirng框架會為我們提供該介面的實現類供我們使用
	----spring中環繞通知:是spring框架為我們提供一種可以在程式碼中手動控制增強方法如何執行的方式
	====使用例子---------------------------------------
		/**
		 * 環繞通知
		 */
		public Object around(ProceedingJoinPoint pjp){
			System.out.println("環繞通知開始。。。。。。。。。。。。");
			Object rtValue = null;
			try {
				System.out.println("前置通知。。。。。。。。。。。。");//寫這裡表示前置通知
				Object[] args = pjp.getArgs();//得到方法執行時所需的引數
				rtValue = pjp.proceed();	//明確呼叫代理方法(切入點方法) 
				System.out.println("後置通知。。。。。。。。。。。。");//寫這裡表示後置通知
				return rtValue;
			} catch (Throwable throwable) {
				System.out.println("異常通知。。。。。。。。。。。。");//寫這裡表示異常通知
				throw  new RuntimeException();
			}finally {
				System.out.println("最終通知。。。。。。。。。。。。");//寫這裡表示最終通知
			}
		}
	====使用例子---------------------------------------

-----基於註解配置aop

----spring配置中配置掃描註解的包以及開啟註解支援
	<!--配置spring建立容器時要掃描的包-->
	<context:component-scan base-package="com.xiaoai.t9aopAnnotation"></context:component-scan>
	<!--配置spring開啟註解aop的支援-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
----把相應Bean配置進ioc容器,@Aspect註解配置aop切面類,定義切入點表示式以及相應通知方法配置相應註解
	@Component("logger")
	@Aspect//表示當前類是一個切面類
	public class Logger {
		
		//定義一個切入點表示式
		@Pointcut("execution(* com.xiaoai.t9aopAnnotation.service.impl.*.*(..))")
		private void pt1(){}

		@Before("pt1()")//前置通知		引用切入點表示式時必須帶有括號
		public void before(){System.out.println("前置通知。。。。。。。。。。。");}
		
		@AfterReturning("pt1()")//後置通知
		public void afterReturning(){System.out.println("後置通知。。。。。。。。。。。");}

		@AfterThrowing("pt1()")//異常通知
		public void afterThrowing(){System.out.println("異常通知。。。。。。。。。。。");}

		@After("pt1()")//最終通知
		public void after(){System.out.println("最終通知。。。。。。。。。。。");}

		//@Around("pt1()")//環繞通知
		public Object around(ProceedingJoinPoint pjp){
			System.out.println("環繞通知開始。。。。。。。。。。。。");
			Object rtValue = null;
			try {
				System.out.println("前置通知。。。。。。。。。。。。");
				Object[] args = pjp.getArgs();//得到方法執行時所需的引數
				rtValue = pjp.proceed();//明確呼叫代理方法(切入點方法)
				System.out.println("後置通知。。。。。。。。。。。。");
				return rtValue;
			} catch (Throwable throwable) {
				System.out.println("異常通知。。。。。。。。。。。。");
				throw  new RuntimeException();
			}finally {
				System.out.println("最終通知。。。。。。。。。。。。");//寫這裡表示最終通知
			}
		}
	}
問題:基於註解配置的前置通知、後置通知、異常通知、最終通知呼叫順序有點問題,需要慎重考慮是否直接註解配置。
	而基於註解配置的環繞通知是沒有該問題的。

----純註解配置	即掃描包和開啟aop支援也用註解配置
	掃描包:@ComponentScan(basePackages = {"com.xiaoai.t9aopAnnotation"})
	開啟aop支援:@EnableAspectJAutoProxy

spring中的JdbcTemplate

當有多個dao,程式碼重複部分很多時可以繼承一個類,該類中把dao中公共程式碼提取出來。
例如:dao類獲取template物件:可以在dao類中繼承相關獲取template的類,在相關類中建立template物件
	template物件在spring配置中dao類的Bean可以不再注入template而是直接注入DataSource
	比如dao實現類繼承JdbcDaoSupper類,通過該類獲取jdbctempte物件,
	繼承和xml配置兩者區別:通過繼承,由於該類為jar包中的類,再想通註解進行則變得麻煩了

----spring自帶事務管理
	----spring中基於xml的宣告式事務控制配置步驟
        1-配置事務管理器(自己寫或spring提供的)
        2-配置事務通知
            匯入事務約束 tx名稱空間和約束,
			使用<tx:advice標籤配置事務通知>
                屬性id:事務通知起唯一的標識
                屬性transaction-manager:給事務通知提供一個事務管理器引用
			<tx:advice id="txAdvice" transaction-manager="transactionManager"></tx:advice>
		3-配置AOP中的通用切入點表示式
		4-建立事務通知和切入點表示式的對應關係
		5-配置事務屬性
			是在事務的通知<tx:advice>標籤的內部
		====使用例子---------------------------------------
			<!--1-配置事務管理器-->
			<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
				<property name="dataSource" ref="dataSource"></property>
			</bean>
			<!--2-配置事務通知-->
			<tx:advice id="txAdvice" transaction-manager="transactionManager">
				<!--5-配置事務屬性-->
				<tx:attributes>
					<!--
						<tx:method>標籤屬性
							name        表示服務層的方法即切入點
							isolation=""        用於指定事務的隔離級別,預設值為DEFAULT,表示資料庫的預設隔離級別
							propagation=""      用於指定事務的傳播行為。預設值為REQUIRED,表示一定會有事務,增刪改的選擇,查詢方法可以選擇SUPPORTS.
							read-only=""        用於指定事務是否只讀,只有查詢方法才能設定為true,預設值為false,表示讀寫
							timeout=""          用於指定事務的超時時間,預設值時-1,表示永不超時。如果指定了數值,以秒為單位
							rollback-for=""     用於指定一個異常,當產生該異常時,事務回滾,產生其他異常時,事務不回滾。無預設值,表示任何異常都回滾
							no-rollback-for=""  用於指定一個異常,當產生該異常時,事務不回滾,產生其他異常時,事務回滾。無預設值,表示任何異常都回滾
					-->
					<tx:method name="transfer"  propagation="REQUIRED" read-only="false"/>
					<tx:method name="find*"  propagation="SUPPORTS" read-only="true"/>
				</tx:attributes>
			</tx:advice>
			<!--3-配置aop-->
			<aop:config>
				<!--3-1-配置切入點表示式-->
				<aop:pointcut id="pt1" expression="execution(* com.xiaoai.t2tx.service.impl.*.*(..))"/>
				<!--4-建立切入點表示式和事務通知的對應關係-->
				<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
			</aop:config>
		====使用例子---------------------------------------

	----spring中基於註解的宣告式事務控制配置步驟
		1-配置事務管理器
		2-開啟spring對註解事務的支援
		3-在需要事務支援的地方使用@Transactional註解(比如服務層類上面,配置在方法上也行)
			如果要配置事務屬性可以在@Transactional註解後面跟上引數即可
		====使用例子---------------------------------------
			<!--配置spring建立容器時掃描的包,以便把註解bean放入容器-->
			<context:component-scan base-package="com.xiaoai.t3txAnnotation"></context:component-scan>

			<!--1-配置事務管理器-->
			<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
				<property name="dataSource"  ref="dataSource"></property>
			</bean>

			<!--開啟spring對註解事務的支援-->
			<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
			
			----------使用註解
			@Transactional
			public class AccountServiceImpl implements IAccountService {.....}
			------------------------------------------------------------------------
			----------配置事務屬性例子
			@Service("accountService")
			@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
			public class AccountServiceImpl implements IAccountService {
				@Autowired
				private IAccountDao accountDao;
				public void setAccountDao(IAccountDao accountDao) {
					this.accountDao = accountDao;
				}
				@Override
				public Account findAccountById(Integer accoountId) {
					Account account = accountDao.findAccountById(accoountId);
					return account;
				}
				//由於該方法的屬性與類上配置的屬性不同,所以要單獨配置,其他不一致的方法也一樣要單獨配
				@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
				@Override
				public void transfer(String sourceName, String targetName, Float money) {
					//執行操作
					//根據名稱查詢轉出賬戶
					Account source = accountDao.findAccountByName(sourceName);
					//根據名稱查詢轉入賬戶
					Account target = accountDao.findAccountByName(targetName);
					//轉出賬戶減錢
					source.setMoney(source.getMoney()-money);
					//轉入賬戶加錢
					target.setMoney(target.getMoney()+money);
					//更新轉出賬戶
					accountDao.updateAccount(source);
					int i = 2/0; //出現錯誤,由於每次連線不同所以下面的操作會失敗
					//更新轉入賬戶
					accountDao.updateAccount(target);
				}
			}
		====使用例子---------------------------------------
	
	開啟spring註解事務的支援註解為:@EnableTransactionManagement //開啟事務註解的支援

spring基於程式設計式事務控制

在實際開發過程中,使用非常少

spring5新特性