【閱讀筆記】《輕量級JavaEE企業應用實戰》(第八章)

本章主要包含Spring的AOP和Spring和Hibernate和Struts整合

后處理器

Bean后處理器會在Bean實例創建成功之后,對Bean實體進行進一步的增強處理。
Bean后處理器必須實現BeanPostProcessor接口,該接口中包含兩個方法postProcessAfterInitialization和postProcessBeforeInitialization,分別代表初始化之后執行和初始化之前執行,以下是一個小例子

package com.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("Bean后處理器的postProcessAfterInitialization方法");
        System.out.println("beanName=" + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("Bean后處理器的postProcessBeforeInitialization方法");
        System.out.println("beanName=" + beanName);
        return bean;
    }

}

還可以通過bean標簽中init-method設置這個bean的初始化方法,用destory-method來設置這個bean的銷毀方法,這些方法是寫在這個bean所對應的類中的。

容器后處理器

除了創建Bean的后處理器之外還可以創建Spring容器的后處理器,我們只需要實現BeanFactoryPostProcessor接口即可,它里面有一個postProcessBeanFactory方法,方法參數ConfigurableListableBeanFactory就是Spring容器

屬性占位符配置器

想在xml文件中使用.propertiest文件中的內容就可以看看下面的代碼了。
在application.xml文件中加入

    <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
        <property name="locations">
            <list>
                <value>classpath:dbconn.properties</value>
            </list>
        </property>
    </bean>
    
    <!-- 數據庫相關配置 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close"
        p:driverClass="${dataSource.driverClass}"
        p:user="${dataSource.user}"
        p:password="${dataSource.password}"
        p:jdbcUrl="${dataSource.jdbcUrl}"
        p:maxPoolSize="${dataSource.maxPoolSize}"
        p:minPoolSize="${dataSource.minPoolSize}"
        p:initialPoolSize="${dataSource.initialPoolSize}"
        p:maxIdleTime="${dataSource.maxIdleTime}"
    />

之后在src文件夾下創建dbconn.properties,它的文件內容是

dataSource.driverClass = com.mysql.jdbc.Driver
dataSource.user = root
dataSource.password = root
dataSource.jdbcUrl = jdbc:mysql://localhost/spring
dataSource.maxPoolSize = 40
dataSource.minPoolSize = 2
dataSource.initialPoolSize=2
dataSource.maxIdleTime=30

Spring的“零配置”支持

如果不想用xml配置的話,也可以用annotation,在Spring中提供了一下幾個annotation來配置Spring Bean類

  • @Component:標注一個普通的Spring Bean類
  • @Controller:標注一個控制器組件類
  • @Service:標注一個業務邏輯組件類
  • @Repository:DAO組件類
    配置好了這些annotation之后只需要在application.xml文件中加入
    <context:component-scan base-package=""></context:component-scan>
    來指定Spring自動搜索這些包下的Java類,并以注解來生成Bean實例。使用的時候要注意的是:
  • @Component可以帶一個字符串類型的參數用作bean的id
  • @Scope可以帶“prototype”或者別的范圍來指定bean的使用范圍
  • @Resourceo(name="beanName")可以用來配置依賴
  • @PostConstruct和@PreDestroy來配置定制生命周期的行為,比如說init和close
  • @Lazy可以帶一個boolean類型的參數,如果為true就不會預初始化該Bean
  • @DependsOn({beanName1,beanName2})可以強制初始化bean1和bean2,就算bean1、bean2設置成@Lazy(true)
  • @Autowired,是用來自動裝配的,可以在bean的set方法上加入@Autowired或者在構造函數上加入,哪怕參數有多個也不要緊,Spring就會自動按照類型搜索出合適的bean。@Autowired可以精確的利用類型(泛型)執行自動裝配,但是如果有兩個不同的bean他們的類型是同樣的可這么辦呢?這時我們可以通過@Qualifier來通過bean的id自動裝配

資源訪問

訪問類加載路徑里的資源

book.xml文件放在src文件下

        ClassPathResource resource = new ClassPathResource("book.xml");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        SAXReader reader = new SAXReader();
        Document document = reader.read(resource.getFile());
        Element root = document.getRootElement();
        System.out.println("root="+root.getText());
        List list = root.elements();
        for(int i = 0; i < list.size(); i++) {
            Element book = (Element) list.get(i);
            System.out.println(i+":"+book.getText());
        }

訪問文件系統里的資源

book.xml在與WebContent文件夾同一級

FileSystemResource resource = new FileSystemResource("book.xml");

訪問網絡資源

網絡資源需要使用InputStream流來讀取數據,不可以直接getFile的

            UrlResource resource = new      UrlResource("http://hxu0170466.my3w.com/book.xml");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
        SAXReader reader = new SAXReader();
        Document document = reader.read(resource.getInputStream());
        Element root = document.getRootElement();
        System.out.println("root="+root.getText());
        List list = root.elements();
        for(int i = 0; i < list.size(); i++) {
            Element book = (Element) list.get(i);
            System.out.println(i+":"+book.getText());
        }

與之類似的還有ServletContextResource訪問相對于ServletContext路徑下的資源、InputStreamResource訪問輸入流資源、ByteArrayResource訪問字節數組資源。

將Resource作為屬性

如果某一個Bean中需要使用某個文件的內容,那么我們就可以在這個Bean里面自動注入一個Resource實例,在配置bean的<bean>標簽中加入 p:res="classpath:book.xml"(這個bean有一個Resource類型的res屬性)這樣在這個bean中就可以獲取到類加載路徑下的book.xml文件的內容了。

在ApplicationContext使用資源

Resource r = applicationContext.getResource("book.xml")

就可以獲得到book.xml的文件內容了,這是以applicationContext的訪問策略來判定的,applicationContext是通過classpath來創建的那么book.xml就應該放在class加載路徑下。

Spring的AOP

AOP(Aspect Orient Programming),是指面向切面編程。與面向對象編程做對比的話可以這樣說,面向對象是從靜態的角度考慮程序結構,面向切面編程則是從動態角度考慮程序的運行過程。

為什么需要AOP

AOP可以在不修改源代碼的前提下增加系統的功能,如果只是用OOP的話如果要在某一個模塊上添加一個用戶合法驗證的話就需要在需要驗證的類里面添加驗證代碼,如果這種類有很多的話那么修改起來是很麻煩的,這時我們就可以通過使用AOP的方式添加一些新的功能(雖然我覺得攔截器也可以做到同樣的效果),常常用AOP來處理一些具有橫切性質的系統級服務,如事物管理、安全檢查、緩存和對象池管理等。總之,AOP要達到的效果是:保證在程序員不修改源代碼的前提下,位系統中業務組件的多個業務方法添加某種通用的功能。但AOP的本質是,依然要去修改業務組件的多個業務方法的源代碼——只是這個修改由AOP框架完成,而不是程序員。

AOP的基本概念

AOP框架的兩個特征
  • 各步驟之間的良好隔離性
  • 源代碼無關性
面向切面編程的一些術語
  • 切面(Aspect):切面用于組織多個Advice,Advice放在切面中定義
  • 連接點(Joinpoint):程序執行過程中明確的點,如方法的調用或者異常的拋出。<font color="red">Spring AOP中,連接點總是方法的調用</font>
  • 增強處理(Advice):AOP框架在特定的切入點執行的增強處理。處理有“around”、“before”和“after”等類型
  • 切入點(Pointcut):可以插入增強處理的連接點。簡而言之,當某個連接點滿足指定要求時,該連接點將被添加增強處理,該連接點也就變成了切入點。
  • 引入:將方法或字段添加到被處理的類中。
  • 目標對象:被AOP框架進行增強處理的對象。
  • AOP代理:AOP框架創建的對象,簡單的說,代理就是目標對象的加強。
  • 織入(Weaving):將增強處理添加到目標對象中,并創建一個被增強的對象(AOP代理)的過程就是織入。

Spring的AOP支持

Spring AOP采用的是基于代理的AOP方案,這與AspectJ采用的編譯時增強的解決方案不同。
AOP代理的方法 = 增強處理 + 目標對象的方法

基于注解的“零配置”方式(小示例)

beans.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"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    <bean id="steelAxe" class="service.SteelAxe"/>
    <bean id="british" class="service.British" p:axe-ref="steelAxe"/>
    <context:component-scan base-package="service,com.aspect">
        <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
    </context:component-scan>
    <aop:aspectj-autoproxy/>
    <aop:config proxy-target-class="true"/>
</beans>

Birtish.java

package service;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class British implements Person{
    private Axe axe;
    
    public void setAxe(Axe axe) {
        System.out.println("Spring調用setAxe執行依賴注入");
        this.axe = axe;
    }
    @Override
    public String useAxe() {
        // TODO Auto-generated method stub
        System.out.println(axe.chop());
        return "Slience";
    }
}

Aspect類

package com.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AuthAspect {
    @Before("execution(* service.*.*(..))")
    public void before() {
        System.out.println("before");
    }
    @After("execution(* service.*.*(..))")
    public void after() {
        System.out.println("after");
    }
    @AfterReturning(returning="rvt", pointcut="execution(* service.*.*(..))") 
    public void afterReturning(Object rvt) {
        System.out.println("afterReturning,返回值是:" + rvt);
    }
    @Around("execution(* service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around,執行之前");
        Object rvt = joinPoint.proceed();
        System.out.println("around,執行之后,返回值是:" + rvt);
        //around還可以對返回值進行修改
        rvt = "Hello World";
        return rvt;
    }
}

其中功能比較強大的就是@Around了,它既可以在目標方法之前織入也可以在目標方法之后織入而且可以決定目標方法在什么情況下執行,甚至可以完全阻斷目標方法的執行,但是如果要使用@Around最好在線程安全的情況下使用,平時還是使用@After和@Before,但是如果要改變返回值的話只有@Around可以實現了。
如果有多個Aspect可以使用@Order(value=1)來實現按照順序執行,value越大就越先執行。
如果需要獲取目標方法的調用參數可以這樣寫@AfterReturning("execution(* service.*.*(..)) && args(arg0, arg1)")之后在下面的方法這樣就可以獲得那個方法的第一個和第二個參數了public void before(Object rvt, String arg0, String arg1)
如果覺得每次寫execution(* service.*.*(..)) && args(arg0, arg1)太麻煩了可以在Aspect類里面寫上這個

    @Pointcut("execution(* service.*.*(..))")
    public void myPointcut(){};

之后調用這個execution可以這樣@Before("myPointcut()"),如果別的Aspect類也想用這個Aspect類的myPointcut()所定義的execution的話,可以這樣寫@Before("AuthAspect.myPointcut()")用類名.方法名的方式實現

基于XML配置文件的管理方式

如果將上面使用注解的例子改成使用XML配置的話就是這樣的

    <bean id="authAspect" class="com.aspect.AuthAspect"/>
    <aop:config proxy-target-class="true">
        <aop:aspect id="authAspect" ref="authAspect">
            <aop:before method="before" pointcut="execution(* service.*.*(..))"/>
            <aop:after method="after" pointcut="execution(* service.*.*(..))"/>
        </aop:aspect>
    </aop:config>

其中的method指的是方法的名字

Spring3.1新增的緩存機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容