1. 程式人生 > 實用技巧 >Sping整理(一)

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;
    }
}

開發的時候可能存在的問題:

AOP總結