Sping整理(一)
why spring
1.可解決大量的重複建立物件的過程,將物件的建立交給spring。
2.建立物件方式:
1.直接new
2.通過反射 Class class = Class.forName("com.xxx.xxx")
class.newInstance().var
spring 工廠模式
1.工廠模式出現原因:解耦合,由工廠幫助建立物件
2.原理:
-
配置applicationContext.xml檔案,在檔案當中定義需要被工廠進行管理的bean。
-
建立物件 ApplicationContext
ApplicationContext:介面(本質)
對應的實現類:1.非web環境下(main/junit):classPathXmlApplicationContext
2.web環境下:XmlWebApplicationContext
執行緒安全,可以多執行緒併發訪問
獲取有需要使用的bean物件
相關方法:
getBean()
方式一:直接通過id
UserService userServiceImpl = (UserService) applicationContext.getBean("userService");
方式二:指定id和型別,避免強制轉換
UserService userService1 = applicationContext.getBean("userService", UserService.class);
方式三:只通過型別,該型別必須只有一個bean
UserService userService2 = applicationContext.getBean(UserService.class);
getBeanDefinitionNames()
獲取xml定義的bean的id陣列
String[] names = applicationContext.getBeanDefinitionNames();
getBeanNamesForType()
根據型別獲得bean的id陣列
String[] names = applicationContext.getBeanNamesForType(UserService.class);
containsBeanDefinition()
是否包含bean的定義,只能判斷id,不能判斷別名name
boolean userService = applicationContext.containsBeanDefinition("userService");
3.基礎實現原理:
通過配置檔案獲得物件的class檔案,再反射呼叫無參構造進行建立,私有的無參構造也可以。
ps:實體類物件不用spring建立,因為需要資料--->持久層建立
spring注入
1.概念:通過spring工廠及配置檔案,對bean物件的成員屬性進行賦值。
2.價值:一般賦值方式:get/set ---->存在耦合 注入:解耦合,但還是需要get.set方法
3.方式:
<bean id="student" class="com.boss.leraning.springdemo.bean.Student">
<property name="age" value="18"></property>
</bean>
4.實現原理:
呼叫get set 方法:對應著property裡面的name 呼叫setName方法---------->所以也叫set注入
set注入
注入各種型別的成員變數方法
1.注入的成員變數的型別多種,不單隻有int 和string ------->用value賦值即可
2.JDK提供的資料型別
-
8種基本資料型別+String
<value>xxxx</value>
-
陣列型別
<list> <value> aa</value> </list>
-
set集合
<set> <value></value>//不一定是value,只有set的泛型是int/string <ref bean=""/>//自定義型別 </set>
-
list集合
<list> <value></value>//不一定是value,只有set的泛型是int/string <ref bean=""/>//自定義型別 </list>
-
map集合//鍵遍歷,值遍歷,鍵值遍歷
<map> <entry> <key>//鍵 <value> </value>//鍵的值 </key> <value></value>//值,不一定是value,看具體情況 </entry> </map>
-
properties
<props> <prop key="">value</prop>//value只能是字串型別 </props>
3.使用者自定義型別
* 一個property標籤呼叫一次set方式
方式一:直接在bean內部定義另一個bean
<bean id="useService "class=" ">
<property name="useDao">//呼叫set
<bean class="userdao"></bean>/建立物件
</property>
</bean>
//方式二:當userDao需要被別的物件使用
方式一問題:多次建立userDao浪費記憶體
<bean id="userDao" class=""></bean>
<bean id = "userService">
<property name="useDao">
<ref bean="userDao"/>
</property>
</bean>
//簡寫方式:
<bean id ="" class="">
<property name="" value=""></property>
<property name="" ref=""></property>
</bean>
構造注入
1.通過有參構造方法對成員變數進行賦值--->定義有參建構函式
2.方式
<bean id="",class="">
<constructor-arg>//一個該標籤呼叫一次構造方法,傳遞一次引數
<value></value>
</constructor-arg>
</bean>
3.構造引數過載
通過控制constructor標籤的數量,找到對應的建構函式
對於引數個數相同情況,要通過型別去區分,eg ,name和age
否則會按順序去構造
<bean id="",class="">
<constructor-arg type="int" ,value=""></constructor-arg>
</bean>
總結
1.一般多數採用SET注入
反轉控制(ioc inverse of Control)
1.控制的概念:對成員變數賦值的控制權-----程式碼/配置檔案+Spring工廠
2.反轉控制:將控制權從程式碼轉移到配置檔案當中。
依賴注入(dependency injection)
1.依賴:使用到它,需要它----->將它作為成員變數,通過配置檔案進行賦值注入
複雜物件
建立複雜物件的方式
-
FactoryBean介面:
1.實現介面
public class MyFactoryBean implments FactoryBean<欲建立的物件的泛型>() { //待重寫的方法 public Object getObject(){ //用於書寫建立複雜物件的程式碼,將其作為返回值返回 } public class getObjectType(){ //返回所建立物件的class物件 } public boolean isSingleton(){ //返回建立物件是否是單例模式 //false,每次回建立新的複雜物件 } }
2.配置檔案的配置
<bean id="conn" class="MyFactoryBean"></bean>
//通過getBean("conn")獲取到的不是MyFactoryBean,而是它所建立的複雜物件---connection
//通過getBean("&conn")獲取到是MyFactoryBean
3.依賴注入例子:
4.實現原理:1.id+class---->獲得ConectionFactoryBean----->通過getObject獲得conn物件。
-
例項工廠
1.使用原因:(1)spring框架侵入避免:離開了spring無法進行獲得物件。
(2)無法拿到ConnectFactoryBean的.java檔案
2.開發步驟:
(1)編寫ConnectionFacctory---->編寫獲得conn物件的程式碼
(2)編寫配置檔案,讓spring管理它
<bean id="connectionFactory" class=""></bean>//配置了例項 <bean id="conn" factory-bean="connectionFactory" factory-method="getConnection"></bean>
-
靜態工廠
與例項工廠的區別就是工廠的方法是靜態的。
控制物件的建立次數
1.複雜物件:isSingleton方法返回true/false
2.簡單物件:通過在bean注入的時候編寫scope屬性:singleton(預設值)/prototype
建立一次的物件:
1.sqlSessionFactory
2.Dao
3.UserService
需要建立多次的物件:
1.sqlSession
2.Connection
物件的生命週期
物件的各個階段
- 建立階段
1.對於singleton物件
在工廠建立的時候就進行建立
懶初始化:在bean標籤當中加入屬性lazy-inti="true",也能達到prototype的效果
2.prototype物件
在物件被獲取呼叫的時候,才被建立
-
初始化階段
1.什麼是初始化? 資料庫,IO,網路流等資源操作。應用一般比較少。 由程式設計師編寫初始化方法,經由spring進行呼叫進行初始化 形式一: 實現介面initializingBean介面當中的public void afterPropertiesSet()方法 形式二: 編寫初始化方法,在<bean id="" class="" ,init-method="初始化方法名"/> //建立物件-->注入-->初始化
-
銷燬階段
工廠關閉時,spring呼叫物件的銷燬方法。 ctx.close; 銷燬操作:釋放資源的操作。 //方式一: 實現DisposableBean當中的public void destory()方法,該方法存在異常,可丟擲 //方式二: 銷燬方法由程式設計師進行編寫,在<bean id="",class="" destory-method="銷燬方法名"> 注意: 銷燬操作只對sigleton物件有用。
配置檔案引數化
1.引數化的目地:能夠方便後期的維護,將一些經常修改的值,定義到一個property檔案當中。通過${kay名}獲得值。
例如:
1.新建小配置檔案:
2.配置檔案的內容:
3.將小配置檔案整合spring配置檔案
classpath:resouce 和同級的java資料夾下的內容的整合。
自定義型別轉換器
1.在注入的時候將String轉換為需要注入的屬性的型別。
2.在某些情況下Spring定義的型別轉換器不滿足我們的需求,例如將string型別轉化為Date 型別。
內建只支援---”yyyy/MM/dd"的格式
-----------》進行自定義型別轉換器
-
方式:
1.實現converter介面
//泛型當中的第一個表示原始型別,第二個表示要轉化型別 public class MyConverter implements Converter<String,Date>(){ @override public Date converter(String source)//source就是配置檔案中代表日期的字串 { SimpleDateFormat sdf = new SimpleDateFormatImpl("yyyy-MM-dd");//存在異常,try-catch Date date= sdf.parse(source); return date;//轉化後的返回值會被spring返回給待注入的物件--->介面回撥 } }
2.新增到配置檔案
spring提供了一個類ConversiobServiceFactoryBean,將自己寫的轉換器賦值給這個類的屬性converters
該屬性是set集合型別。該類命名中的id必須為conversionService
後置處理Bean
BeanPostProcessor(介面):對工廠建立的物件進行進一步的再加工
待重寫方法:
Object postProcessBeforeInitiallization(Object bean String BeanName){
//在建立完物件,待初始化之前進行呼叫
return bean;//返回加工之後的物件
}
Object postProcessAfterInitiallization(Object bean String BeanName){
//在初始化完成之後進行呼叫
return bean;//返回加工之後的物件
}
//如果沒有實現初始化操作的化,兩個方法沒有區別,但是不管有沒有進行加工都要將bean物件進行返回。
-
開發步驟:
1.實現BeanPostProcessor
2.配置檔案
<bean id ="" class=""></bean>
注意 beanPostProcessor會對工廠的所有物件都進行加工,所以前面程式碼要進行型別判斷,instanceof()
代理設計模式
1.javaEE的開發層次當中最重要的是service層
dao--->service--->controller
2.service的功能
效能:業務的開始時間和結束時間之間的差。
日誌:誰,何時,做何事
-
service層到底需不需要寫額外功能?
呼叫者:需要,事務等處理功能。
軟體設計者:不需要,避免程式碼入侵。
- 現實生活中例子
1.代理設計模式概念:通過代理類(proxy)來完成目標類的額外功能,利於目標類的維護。改動額外功能時不用改變目標類
2.
- 與目標類相同的名字
- 實現額外功能
- 有目標類的介面實現
以上代理方法為靜態代理為每一個原始類都編寫一個代理。
-
靜態代理缺點:
1.代理類數量過多,不利於專案管理。
2.額外功能維護性差。如果想改變日誌的格式,就得所有的代理類都修改。
spring 動態代理
本質與靜態代理相同
1.匯入相關的jar包
-
開發步驟:
1.建立原始物件
編寫原始物件---userService 建立物件--->spring的bean注入
2.額外功能
實現介面MethodBeforeAdvice中的Before方法 public class before implements MethodBeforeAdvice{ @override public void bofore(Method method Object[] args Object target ) //method:原始類中要增加額外功能的方法 //args:原始方法的引數 //target:額外功能所增加給的物件 } 在配置中進行bean注入 <bean id="before" class="xxxx.MyMethodBeforeAdvice"></bean>
3.定義切入點
切入點:定義額外功能加入的位置。
//通過定義切入點來指定你的Before方法需要加給那些方法 * 通過配置檔案進行定義: <aop:config> <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作為切入點 </aop:config>
4.組裝
<aop:config> <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作為切入點 <aop:advisor advice-ref="before" pointcut-ref="pc">//組裝,把切入點與before方法進行整合 </aop:config>
5.使用
通過getBean(原始物件)就可以獲得代理物件--->要用介面型別進行儲存 ApplicationContext ctx = new ClassPathXmlApplicationContext(); UserService userService = (UserServiece)ctx.getBean();
-
spring建立的動態代理類在哪裡?
spring通過動態位元組碼技術在jvm裡建立,在jvm使用結束後也消失。
-
-
好處:
1.簡化代理的操作,額外功能只寫一個額外功能類即可,然後加到方法前面
2.提升代理類的維護性---->開啟擴充套件,關閉修改。
不想要這個額外功能,寫個新的去繫結配置即可,省去舊的修改。
方式二(推薦,可以在原始方法的前後都新增額外功能):
實現介面MethodInterceptor中的invoke方法
pucblic class Arround implement MethodInterceptor{
@override
//該方法的引數就是所要新增額外功能的原始方法
public Object invoke(MethodInvocation invocation) Throws Throwsable{
sout"------"//按自己要求新增額外功能
Object ret= invocation.proceed();//表示讓原始方法執行,以便讓額外功能新增在其之後
return ret;
}
}
//後步驟與MethodBeforeAdvice相同
該介面的功能比MethodBeforeAdvice更加強大,並且可以通過改變ret,改變返回值。
-
何時在原始方法前後都新增額外功能?
事務 :原始方法執行前開啟事務,執行之後提交事務
異常:在原始方法丟擲異常的時候執行
切入點詳解
方法切入點
切入點的位置:決定額外功能加入的位置
<aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
//切入點函式:execution()
//切入點表示式:execution(* *(..))
第一個* --->修飾符,返回值
第二個*---->方法名
()中的..---->引數個數任意
eg.
* login(String)//方法login,引數一個,如果是非java.lang包中型別寫全限定名
* login(String,..)//表示只要第一個引數是String即可,後引數的個數和型別沒要求
-
精準的方法切入點的限定
在不同的包下的方法可能完全相同
解決方法:
類切入點
1.使用場景:對於某個類中的所有方法加入額外功能。
2.方式:
//所有包中的userServiceImpl類中的所有方法
<aop:pointcut id="pc" expression="execution(* *..uerServiceImpl.*(..))"></aop:pointcut>
包切入點
定義在某個包下的所有的類的切入點:
語法:
//該包中的所有類的所有的方法
<aop:pointcut id="pc" expression="execution(* com.xxx.xxxx.*.*(..))"></aop:pointcut>
//該包及其子包下的所有類的所有方法,包後多加一個點
<aop:pointcut id="pc" expression="execution(com.xxx.xxxx..*.*(..))></aop:pointcut>
//更具有實戰價值
切入點函式
1.作用:執行切入點表示式
2.常用的切入點函式:
execution:功能最為齊全,但是表示式書寫麻煩
----->其他的函式進行簡化,但是本質還是execution
* args(String String)//關心函式的引數。有2個String引數的都滿足
* within(*..userSericeImpl)//用於類或包的切入點,表示所有的UserService這個類
within(com.xxx.xxx..*)//包及其子包下的所有的類原表示式:execution(* com.xxx.xxx..*.*(..))
* annotation(com.xxx.xxx.log)//具有特殊註解的方法,例如自定義註解log,內部寫註解全限名
切入點函式邏輯運算
1.概念:講多個切入點函式之間進行and或者or運算以滿足更為複雜的需求。
and
案例:login 同時引數為2個String
execution(* login(..)) and args(String String)
注意:與操作不能用於同種型別的切入點函式,不能execution and execution
案例: login 和 register----->用and結果為空 ,應該用or操作
or
案例: login 和 register----->用and結果為空 ,應該用or操作
execution(* login(..)) or execution(* register(..))
AOP程式設計
AOP概念
本質就是Spring的動態代理,通過代理類為原始類增加額外功能。
好處:利於原始物件的維護。
AOP開發步驟
1.原始物件
2.額外功能
3.切入點
4.組裝
切面 :由具有相同性質的點所共同構成的面。
AOP底層實現原理
待思考的問題:
1.aop如何幫我們建立動態代理物件?
2.為何通過id值獲得的是代理物件而不是原始物件?
JDK動態代理
1.複習代理建立的三要素
- 原始物件
- 額外功能
- 代理物件與原始物件實現相同的介面
2.Proxy.NewProxyInstance()方法引數的分析:
3.具體編碼實現
public calss TestJDKProxy(){
public static void main(String[] args)
{
//建立原始物件
private UserService userService = new UserService();
//通過jdk生產代理物件
//通過匿名內部類實現介面
InvocationHandler handler = new InvocationHandler(){
@override
public Object invoke(Object proxy , Mehtod method , Object[] args) Throws Throwable{
//額外功能
sout."-----Proxy-log-------"
//讓原始方法執行
Object ret=method.invoke(userService,args);
retuen ret;
}
}
UserService userServiceProxy = (UserService)
Proxy.NewProxyInstance(TestJDKProxy.class.getClassloader,userService.getCalss.getInterface(),handler)//強制轉換獲得UserService
}
}
cglib動態代理
1.適用情形:原始類沒有實現介面,只一個原始類
2.解決方法:繼承原始類
3.編碼步驟:
public class TestCglib(){
public static void main(String [] args){
//建立原始物件
private UserService userService;
//Cglib包自帶的方法
Enhancer.SetClassLoader(TestCglib.class.getClassLoader())//設定建立代理物件時的類載入器,也是借的
Enhancer.SetSuperClass(userService.getClass())//設定父類的class檔案,在jdk當中使用的是介面,這裡是直接目標類
//用內部類實現MethodInterceptor介面,與spring當中的介面不是一個,所處的包位置不同
MehodInterceptor interceptor = new MethodInterceptor(){
@override
public Object interceptor(Object o, Method method,Object[] args,MethodProxy methodProxy) Throws Throwable{
sout."----cglib----log----"//實現額外功能
Object ret =method.invoke(userService,args);//執行原始類的方法
//方法執行的三要素:1.執行物件--->原始物件
// 2.執行的方法 3.執行方法的引數
return ret;
}
}
Enhancer.SetCallBack(interceptor)//通過實現介面的內部類設定額外功能;
UserService userServiceProxy = (UserService) Enhancer.create();
//之後就可以使用代理物件userServiceProxy了
}
}
spring如何建立代理物件
1.aop如何幫我們建立動態代理物件?
2.為何通過id值獲得的是代理物件而不是原始物件?
3.具體編碼
(1)編寫原始物件,並配置bean
(2)實現BeanPostProcessor這個介面,並將實現類配置bean
基於註解的AOP程式設計
1.基本步驟
- 原始物件
- 額外功能
- 切入點
- 組裝切面
區別:通過切面類定義額外功能和切入點
@Aspect
public class MyAspect {
@Around("execution(* *.*(..))")//表示所有方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect----log-------");
Object ret = joinPoint.proceed();
return ret;
}
}
在配置檔案當中進行配置
<bean id="userService" class="UserServiceImpl"/>
<bean id="myAspect" class="MyAspect"/>
<!-- 開啟aop註解程式設計,設定底層為cglib將proxy-target-class設定為true-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
細節:切入點複用
@Aspect
public class MyAspect {
//切入點的複用,避免對於各種around方法當中的切入點進行多次修改
//eg.將所有的login要該成register就要2個around函式都修改
@Pointcut("execution(* *.login(..))")
public void myPointCut(){}//空的函式體
@Around("myPointCut()")//直接寫自定義的切入點函式名即可
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect1----log-------");
Object ret = joinPoint.proceed();
return ret;
}
@Around("myPointCut()")
public Object around2(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect2----log-------");
Object ret = joinPoint.proceed();
return ret;
}
}
開發的時候可能存在的問題: