Spring和Springboot相關(guān)知識(shí)點(diǎn)整理

2.3.1 與Spring的關(guān)系

這是Spring AOP的兩種實(shí)現(xiàn)方式榴都。根據(jù)官方文檔:

默認(rèn)使用JdkProxy

對(duì)于被代理對(duì)象沒有實(shí)現(xiàn)任何接口汛闸,使用Cglib

可以強(qiáng)制指定使用Cglib氓扛。

這樣就可以解釋為什么有的bean實(shí)現(xiàn)了接口辖所,有的沒有琼梆,但是在同一個(gè)工程中可以并存了撕蔼。

2.3.2 示例代碼

本節(jié)代碼改寫自參考文獻(xiàn)[5]。

//用戶管理接口

public interface UserManager {

? ? //新增用戶抽象方法

? ? void addUser(String userName,String password);

? ? //刪除用戶抽象方法

? ? void delUser(String userName);

}

//用戶管理實(shí)現(xiàn)類,實(shí)現(xiàn)用戶管理接口

public class UserManagerImpl implements UserManager{

? ? @Override

? ? public void addUser(String userName) {

? ? ? ? System.out.println("調(diào)用了新增的方法或链!");

? ? ? ? System.out.println("傳入?yún)?shù)為 userName: "+userName+" password: "+password);

? ? }

? ? @Override

? ? public void delUser(String userName) {

? ? ? ? System.out.println("調(diào)用了刪除的方法惫恼!");

? ? ? ? System.out.println("傳入?yún)?shù)為 userName: "+userName);

? ? }?

}

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import com.lf.shejimoshi.proxy.entity.UserManager;

import com.lf.shejimoshi.proxy.entity.UserManagerImpl;

//JDK動(dòng)態(tài)代理實(shí)現(xiàn)InvocationHandler接口

public class JdkProxy implements InvocationHandler {

? ? private Object target ;//需要代理的目標(biāo)對(duì)象


? ? @Override

? ? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

? ? ? ? System.out.println("JDK動(dòng)態(tài)代理,監(jiān)聽開始澳盐!");

? ? ? ? Object result = method.invoke(target, args);

? ? ? ? System.out.println("JDK動(dòng)態(tài)代理祈纯,監(jiān)聽結(jié)束!");

? ? ? ? return result;

? ? }

? ? //定義獲取代理對(duì)象方法

? ? // 因?yàn)橹皇窃趍ain()里測試叼耙,聲明為private了

? ? private Object getJDKProxy(Object targetObject){

? ? ? ? this.target = targetObject;

? ? ? ? return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);

? ? }


? ? public static void main(String[] args) {

? ? ? ? JdkProxy jdkProxy = new JdkProxy();

? ? ? ? UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//獲取代理對(duì)象

? ? ? ? user.addUser("admin");

? ? }?

}

import java.lang.reflect.Method;

import com.lf.shejimoshi.proxy.entity.UserManager;

import com.lf.shejimoshi.proxy.entity.UserManagerImpl;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

//Cglib動(dòng)態(tài)代理腕窥,實(shí)現(xiàn)MethodInterceptor接口

public class CglibProxy implements MethodInterceptor {

? ? private Object target;//需要代理的目標(biāo)對(duì)象


? ? //重寫攔截方法

? ? @Override

? ? public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {

? ? ? ? System.out.println("Cglib動(dòng)態(tài)代理,監(jiān)聽開始筛婉!");

? ? ? ? Object invoke = method.invoke(target, arr);//方法執(zhí)行簇爆,參數(shù):target 目標(biāo)對(duì)象 arr參數(shù)數(shù)組

? ? ? ? System.out.println("Cglib動(dòng)態(tài)代理,監(jiān)聽結(jié)束!");

? ? ? ? return invoke;

? ? }

? ? //定義獲取代理對(duì)象方法

? ? public Object getCglibProxy(Object objectTarget){

? ? ? ? //為目標(biāo)對(duì)象target賦值

? ? ? ? this.target = objectTarget;

? ? ? ? Enhancer enhancer = new Enhancer();

? ? ? ? //設(shè)置父類,因?yàn)镃glib是針對(duì)指定的類生成一個(gè)子類入蛆,所以需要指定父類

? ? ? ? enhancer.setSuperclass(objectTarget.getClass());

? ? ? ? enhancer.setCallback(this);// 設(shè)置回調(diào)

? ? ? ? Object result = enhancer.create();//創(chuàng)建并返回代理對(duì)象

? ? ? ? return result;

? ? }


? ? public static void main(String[] args) {

? ? ? ? CglibProxy cglib = new CglibProxy();

? ? ? ? UserManager user =? (UserManager) cglib.getCglibProxy(new UserManagerImpl());

? ? ? ? user.delUser("admin");

? ? }


}

2.3.3 比較

比較一下兩者的區(qū)別响蓉,這也是常見的面試問題。

JdkProxy Cglib

依賴 被代理對(duì)象實(shí)現(xiàn)了接口(所有接口的方法數(shù)總和必須>0[4]) 引入asm哨毁、cglib jar 枫甲;不能是final類和方法

原理 反射,實(shí)現(xiàn)被代理對(duì)象接口的匿名內(nèi)部類扼褪,通過InvocationHandler.invoke()包圍被代理對(duì)象的方法 引入asm言秸、cglib jar,代理類實(shí)現(xiàn)MethodInterceptor迎捺,通過底層重寫字節(jié)碼來實(shí)現(xiàn)

效率 創(chuàng)建快举畸,運(yùn)行慢(見下方說明2) 創(chuàng)建慢,運(yùn)行快

說明

Cglib是如何修改字節(jié)碼凳枝,從代碼上是看不出來的抄沮。使用的是ASM技術(shù),修改class文件岖瑰,可以自行查閱叛买。

JDK1.8及以后,JdkProxy的運(yùn)行速度已經(jīng)比Cglib快了(之前則是慢于Cglib)蹋订,測試代碼可見參考文獻(xiàn)[6]率挣。

2.3.4 關(guān)于org.apoalliance.intercept.MethodInterceptor

我讀了一下之前所用的日志攔截器源碼,發(fā)現(xiàn)其實(shí)現(xiàn)的是這節(jié)標(biāo)題的接口:

class CommonInterceptor implements MethodInterceptor {

? ? ? @Override

? ? ? public Object invoke(MethodInvocation invocation) throws Throwable {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? // 攔截器內(nèi)部邏輯

? ? ? ? ? ? ? ? ? result = invoication.proceed();

? ? ? ? ? ? catch(Throwable e) {

? ? ? ? ? ? ? ? ? // 異常處理

? ? ? ? ? ? }

? ? ? ? ? ? return result;

? ? ? }

}

聲明代理鏈

@Configuration

public class InterceptorConfig {

? ? ? @Bean

? ? ? public CommonInterceptor serviceInterceptor() {

? ? ? ? ? ? CommonInterceptor bean = new CommonInterceptor();

? ? ? ? ? ? return bean;

? ? ? }

? ? ? // 代理名稱后綴為servie的實(shí)現(xiàn)類

? ? ? @Bean

? ? ? public BeanNameAutoProxyCreator servieBeanNameAutoProxyCreator() {

? ? ? ? ? ? BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();

? ? ? ? ? ? creator.setName("*ServieImpl");

? ? ? ? ? ? creator.setInterceptorNames("serviceInterceptor");

? ? ? ? ? ? creator.setProxyTargetClass(true);

? ? ? ? ? ? return creator;

? ? ? }

}

查了一些資料露戒,apoalliance包下只是aop的接口規(guī)范椒功,不是具體的實(shí)現(xiàn),不要把這里的MethodInterceptor和cglib的MethodInterceptor搞混智什。

2.4 構(gòu)造方法注入和設(shè)值注入的區(qū)別

注:設(shè)值注入指的是通過setter注入动漾。

之前看參考文獻(xiàn)[7],感覺很難懂荠锭,試著改換了一種說法:

如果只設(shè)置基本類型(int旱眯、long等)的值,建議設(shè)置默認(rèn)值而不是通過任何一種注入完成

構(gòu)造注入不支持大部分的依賴注入证九。構(gòu)造注入僅在創(chuàng)建時(shí)執(zhí)行删豺,設(shè)值注入的值在后續(xù)也可以變化。

設(shè)值注入可以支持尚未完整的被依賴的對(duì)象愧怜,構(gòu)造注入則不行呀页。可以通過構(gòu)造注入決定依賴關(guān)系叫搁,因此如果依賴關(guān)系不會(huì)發(fā)生變更也可以選擇依賴注入赔桌。

2.5 ApplicationContext事件

可以通過實(shí)現(xiàn)ApplicationEvent類和ApplicationListener接口,進(jìn)行ApplicationContext的事件處理渴逻。這是標(biāo)準(zhǔn)的發(fā)送者-監(jiān)聽者的模型疾党,可以用來處理業(yè)務(wù)邏輯,將代碼解耦惨奕。

但是雪位,發(fā)送和接收實(shí)際上是同步的,如果有事務(wù)梨撞,會(huì)在同一個(gè)事務(wù)內(nèi)雹洗,并不能作為異步處理機(jī)制[8]。

示例代碼見參考文獻(xiàn)[9]卧波。

3. SpringBoot

注:工作中我對(duì)SpringBoot是偏應(yīng)用的时肿,研究并不是很深入。

3.1 如何快速搭建一個(gè)SpringBoot項(xiàng)目

見參考文獻(xiàn)[10]港粱。實(shí)際的過程是借助Spring Initializer這個(gè)網(wǎng)絡(luò)應(yīng)用程序來生成SpringBoot項(xiàng)目螃成。

3.2 SpringBoot的關(guān)鍵注解

所謂核心注解,這里指的是相對(duì)Spring本身新增的一些注解查坪,來看看它們有什么作用寸宏。

恰好這里提到的注解,都可以打在SpringBoot的啟動(dòng)類(不限于啟動(dòng)類)偿曙,用下面的代碼片段來進(jìn)行說明氮凝。

3.2.1 代碼示例

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Import;

import org.springframework.context.annotation.PropertySource;

@PropertySource(value = "classpath:application.properties")

@MapperScan("com.example.demo.dal")

@SpringBootApplication(scanBasePackages = {"com.example.demo"})

@Import({DemoConfig1.class, DemoConfig2.class,})

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

3.2.2 @PropertySource

從指定的文件讀取變量。示例代碼會(huì)從resource目錄下讀取application.properties變量的值望忆。文件的格式如下罩阵,既可以用常量,也可以用變量替換启摄,來對(duì)不同環(huán)境的值作區(qū)分永脓。

name=value

env.name=$env.value

如何使用這個(gè)值?在要使用的地方獲取即可鞋仍。

@PropertySource(value = "classpath:application.properties")

class TestClass {

@Resource

private Environment environment;

? ? ? @Test

? ? ? public void test() {

? ? ? ? ? ? String value = environment.getProperty("name"));

? ? ? }

}

3.2.2.1 與@Value配合使用

使用@Value可以把配置文件的值直接注入到成員變量中常摧。

@PropertySource("classpath:application.properties")

public class PropertyConfig {

? ? @Value("${name}")

? ? private String value;

? ? ...

}

3.2.2.2 通過@Import引用

3.2.1的示例代碼中,如果類上沒有@PropertySource威创,但DemoConfig1或DemoConfig2中有@PropertySource落午,通過@Import可以將它們加載的變量也讀出來。

@Import的作用在下文會(huì)繼續(xù)介紹肚豺。

3.2.2.3 .properties和.yml配置文件

@PropertySource只能導(dǎo)入.properties配置文件里的內(nèi)容溃斋,對(duì)于.yml是不支持的∥辏看了一些文章梗劫,得出結(jié)論是yml文件是不需要注解就能導(dǎo)入享甸,但是需要路徑。

Springboot有兩種核心配置文件梳侨,application和bootstrap蛉威,都可以用properties或yml格式。區(qū)別在于bootstrap比application優(yōu)先加載走哺,并且不可覆蓋蚯嫌。

3.2.3 @MapperScan

這實(shí)際上是一個(gè)mybatis注解,作用是為指定路徑下的DAO接口丙躏,通過sqlmapping.xml文件择示,生成實(shí)現(xiàn)類。

3.2.4 @SpringBootApplication

@SpringBootApplication是由多個(gè)注解組合成的晒旅。源碼如下:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

? ? ? // 略

}

簡單介紹一下這些注解栅盲。

#### 3.2.4.1 元注解

最上面四行都是元注解》狭担回憶一下它們的作用<sup>[12]</sup>:

* @Target 注解可以用在哪剪菱。TYPE表示類型,如類拴签、接口孝常、枚舉

* @Retention 注解的保留時(shí)間期限。只有RUNTIME類型可以在運(yùn)行時(shí)通過反射獲取其值

* @Documented 該注解在生成javadoc文檔時(shí)是否保留

* @Inherited 被注解的元素蚓哩,是否具有繼承性构灸,如子類可以繼承父類的注解而不必顯式的寫下來。

#### 3.2.4.2 @SpringBootConfiguration

標(biāo)注這是一個(gè)SpringBoot的配置類岸梨,和@Configuration功能是相通的喜颁,從源碼也可以看出它直接使用了@Configuration。

#### 3.2.4.3 @EnableAutoConfiguration

這個(gè)注解是實(shí)現(xiàn)自動(dòng)化配置的核心注解曹阔,定義如下

```java

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {

? ? ? // 略

}

龍華大道1號(hào)?www.kinghill.cn/LongHuaDaDao1Hao/index.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末半开,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赃份,更是在濱河造成了極大的恐慌寂拆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抓韩,死亡現(xiàn)場離奇詭異纠永,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谒拴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門尝江,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人英上,你說我怎么就攤上這事炭序∑「玻” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵惭聂,是天一觀的道長窗声。 經(jīng)常有香客問我,道長彼妻,這世上最難降的妖魔是什么嫌佑? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任豆茫,我火速辦了婚禮侨歉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘揩魂。我一直安慰自己幽邓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布火脉。 她就那樣靜靜地躺著牵舵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪倦挂。 梳的紋絲不亂的頭發(fā)上畸颅,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音方援,去河邊找鬼没炒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛犯戏,可吹牛的內(nèi)容都是我干的送火。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼先匪,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼种吸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呀非,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤坚俗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后岸裙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坦冠,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年哥桥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辙浑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拟糕,死狀恐怖判呕,靈堂內(nèi)的尸體忽然破棺而出倦踢,到底是詐尸還是另有隱情,我是刑警寧澤侠草,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布辱挥,位于F島的核電站,受9級(jí)特大地震影響边涕,放射性物質(zhì)發(fā)生泄漏晤碘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一功蜓、第九天 我趴在偏房一處隱蔽的房頂上張望园爷。 院中可真熱鬧,春花似錦式撼、人聲如沸童社。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扰楼。三九已至,卻和暖如春美浦,著一層夾襖步出監(jiān)牢的瞬間弦赖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國打工浦辨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹬竖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓荤牍,卻偏偏與公主長得像案腺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子康吵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361