1.Spring是什么,它的作用是什么?
Spring是一個(gè)IOC(DI)和AOP的輕量級(jí)容器框架读串,它集成了很多模塊裕寨,比如Web、AOP疚察、測(cè)試和一些基本的工具蒸走,而且Spring可以很方便的集成其他框架,比如Mybatis貌嫡、Hibernate等等比驻。它簡(jiǎn)化了java開發(fā),實(shí)現(xiàn)了代碼的解耦岛抄。IOC是控制反轉(zhuǎn)别惦,DI是依賴注入,就是將對(duì)象的創(chuàng)建夫椭、初始化掸掸、銷毀以及對(duì)象之間的依賴交給Spring來管理。AOP是面向切面編程蹭秋,我們可以通過XML配置或者注解的方式在方法執(zhí)行前扰付、執(zhí)行后、拋出異常后添加一些相應(yīng)的業(yè)務(wù)邏輯感凤,AOP經(jīng)常用來做事務(wù)悯周、日志、安全等相關(guān)功能陪竿。Spring主要是為了解決以前開發(fā)中的一些問題禽翼,從而提高開發(fā)效率屠橄,代碼解耦。
Spring為了降低Java開發(fā)的復(fù)雜性闰挡,采用了以下四種策略:
- 基于POJO的輕量級(jí)和最小侵入性編程锐墙。不需要強(qiáng)迫實(shí)現(xiàn)Spring規(guī)范的接口和類從而對(duì)代碼和框架造成強(qiáng)耦合,最壞的情況是长酗,即使一個(gè)類使用了Spring注解溪北,但它仍然是POJO(Plain old java object 簡(jiǎn)單java對(duì)象)
- 通過依賴注入和面向接口實(shí)現(xiàn)松耦合。將類之間的關(guān)系通過依賴注入關(guān)聯(lián)夺脾,將對(duì)象的創(chuàng)建和管理以及對(duì)象間的依賴交給Spring容器維護(hù)之拨,每個(gè)實(shí)例只需要負(fù)責(zé)執(zhí)行,執(zhí)行內(nèi)容由Spring決定
- 基于切面和管理進(jìn)行聲明式編程咧叭。應(yīng)用切面實(shí)現(xiàn)代碼復(fù)用(模板+骨架蚀乔,骨架上有卡槽,木塊可以插入骨架的卡槽中菲茬,實(shí)現(xiàn)代碼復(fù)用)吉挣,減少不必要功能對(duì)主邏輯的侵入和代碼污染。
- 通過切面和模板減少樣板式代碼婉弹。將樣板式代碼進(jìn)行封裝(比如JDBC本身存在大量樣板式代碼)睬魂,只需要寫核心邏輯即可镀赌。
2.什么是AOP氯哮?
答:AOP是面向切面編程,在不改變?cè)瓉矸椒I(yè)務(wù)邏輯的基礎(chǔ)上通過XML配置或者注解的方式給方法添加前置通知佩脊、后置通知蛙粘、異常通知、最終通知威彰、環(huán)繞通知出牧,從而將很多業(yè)務(wù)方法都要用到的業(yè)務(wù)邏輯或功能添加到切點(diǎn)。我們通常用AOP做日志歇盼、事務(wù)舔痕、權(quán)限等功能。
3.AOP的XML配置實(shí)現(xiàn)方式和注解實(shí)現(xiàn)方式豹缀?
使用XML的配置方式實(shí)現(xiàn)AOP
首先需要一個(gè)增強(qiáng)類伯复,這個(gè)類根據(jù)通知的類型去實(shí)現(xiàn)如MethodBeforeAdvice、AfterReturningAdvice 的接口邢笙,然后復(fù)寫相應(yīng)方法啸如。再在XML中配置需要增強(qiáng)的方法的類的bean,增強(qiáng)類的bean氮惯,然后配置切點(diǎn)叮雳,也就是需要增強(qiáng)的方法想暗,再將增強(qiáng)類和切點(diǎn)關(guān)聯(lián),再配置動(dòng)態(tài)代理帘不。
注解方式的實(shí)現(xiàn)AOP
首先導(dǎo)入包说莫,然后使用@aspect配置切面類,使用@pointcut定義切點(diǎn)寞焙,@before等定義通知储狭,最后在XML中開啟注解掃描,開啟aop注解捣郊。
4.AOP的實(shí)現(xiàn)原理辽狈?
AOP底層實(shí)現(xiàn)原理是代理設(shè)計(jì)模式。代理模式是通過代理控制對(duì)象的訪問呛牲,可以詳細(xì)訪問某個(gè)對(duì)象的方法稻艰,在這方法調(diào)用前后處理。
代理分類分為靜態(tài)代理和動(dòng)態(tài)代理侈净。
5.什么是靜態(tài)代理?什么是動(dòng)態(tài)代理僧凤?
靜態(tài)代理
靜態(tài)代理是指在代碼運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件畜侦,代理類和委托類的關(guān)系在運(yùn)行前就已經(jīng)確定了。說白了就是程序員自己編寫或者生成的代理類源碼躯保,再編譯代理類旋膳。
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao {
public void save() {
System.out.println("已經(jīng)保存數(shù)據(jù)...");
}
}
//代理類
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao iuserDao) {
this.target = iuserDao;
}
public void save() {
System.out.println("保存接口調(diào)用開始...");
target.save();
System.out.println("保存接口調(diào)用結(jié)束...");
}
}
動(dòng)態(tài)代理
動(dòng)態(tài)代理是在程序運(yùn)行時(shí)通過反射機(jī)制動(dòng)態(tài)生成字節(jié)碼文件。動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和Cglib動(dòng)態(tài)代理途事。
JDK動(dòng)態(tài)代理
/**
* JDK動(dòng)態(tài)代理
* 每次生成動(dòng)態(tài)代理類對(duì)象時(shí),實(shí)現(xiàn)了InvocationHandler接口的調(diào)用處理器對(duì)象
* <p>
* 1.通過實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);
* 2.通過為Proxy類指定ClassLoader對(duì)象和一組interface創(chuàng)建動(dòng)態(tài)代理類
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
* 3.通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù)验懊,其參數(shù)類型是調(diào)用處理器接口類型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
* 4.通過構(gòu)造函數(shù)創(chuàng)建代理類實(shí)例,此時(shí)需將調(diào)用處理器對(duì)象作為參數(shù)被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
*
* @author youyou
*/
public class InvocationHandlerImpl implements InvocationHandler {
/**
* 這其實(shí)業(yè)務(wù)實(shí)現(xiàn)類對(duì)象尸变,用來調(diào)用具體的業(yè)務(wù)方法
*/
private Object target;
/**
* 通過構(gòu)造函數(shù)傳入目標(biāo)對(duì)象
*
* @param target
*/
public InvocationHandlerImpl(Object target) {
this.target = target;
}
/**
* 代理具體功能在這里實(shí)現(xiàn)
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
System.out.println("調(diào)用開始處理");
result = method.invoke(target, args);
System.out.println("調(diào)用結(jié)束處理");
return result;
}
public static void main(String[] args) throws SecurityException, IllegalArgumentException {
//初始化委托類义图,構(gòu)造代理類
IUserDao userDao = new UserDao();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
ClassLoader loader = userDao.getClass().getClassLoader();
Class<?>[] interfaces = userDao.getClass().getInterfaces();
//對(duì)Proxy指定ClassLoader和一組interface創(chuàng)建動(dòng)態(tài)代理類
IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
newProxyInstance.save();
}
}
JDK動(dòng)態(tài)代理簡(jiǎn)單來說就是生成一個(gè)包裝類對(duì)象,由于代理對(duì)象是動(dòng)態(tài)的召烂,所以是動(dòng)態(tài)代理碱工。由于我們需要增強(qiáng),這個(gè)增強(qiáng)需要留給開發(fā)人員開發(fā)代碼的奏夫,因此代理對(duì)象不能直接包含被代理對(duì)象怕篷,而是一個(gè)InvocationHandler,該對(duì)象包含被代理對(duì)象酗昼,并負(fù)責(zé)分發(fā)請(qǐng)求給代理對(duì)象廊谓,分發(fā)前后均可增強(qiáng)。從原理上看麻削,JDK動(dòng)態(tài)代理是對(duì)“對(duì)象”的代理蒸痹。
JDK動(dòng)態(tài)代理要求被代理類必須實(shí)現(xiàn)接口(Proxy.newProxyInstance第二個(gè)參數(shù))春弥。
Cglib動(dòng)態(tài)代理
/**
* Cglib動(dòng)態(tài)代理實(shí)現(xiàn)
*/
public class CglibProxy implements MethodInterceptor {
/**
* 這里的目標(biāo)類型為Object,則可以接受任意一種參數(shù)作為被代理類电抚,實(shí)現(xiàn)了動(dòng)態(tài)代理
*/
public Object getInstance(Class<?> clazz) {
// 設(shè)置需要?jiǎng)?chuàng)建子類的類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開啟事物");
Object result = proxy.invokeSuper(obj, args);
System.out.println("關(guān)閉事物");
// 返回代理對(duì)象
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(UserDao.class);
userDao.save();
}
}
Cglib動(dòng)態(tài)代理只需要一個(gè)類型clazz就可以產(chǎn)生一個(gè)代理對(duì)象惕稻, 所以說是“類的代理”,且創(chuàng)造的對(duì)象通過打印類型發(fā)現(xiàn)也是一個(gè)新的類型蝙叛。
6. Spring的Bean的注入方式俺祠?
- 屬性注入:通過setXXX( )方法注入bean的屬性值或依賴對(duì)象
- 構(gòu)造函數(shù)注入
- 工廠方法注入(包括非靜態(tài)工廠或靜態(tài)工廠,區(qū)別就是在創(chuàng)建Bean實(shí)例的時(shí)候需不需要?jiǎng)?chuàng)建一個(gè)工廠類實(shí)例從而調(diào)用工廠類創(chuàng)建Bean的方法)
7. Spring中Bean的生命周期
Spring中bean的生命周期大致分為:實(shí)例化借帘、初始化蜘渣、運(yùn)行、銷毀
- 通過構(gòu)造器或工廠方法創(chuàng)建bean的實(shí)例肺然,此時(shí)會(huì)加載類蔫缸,執(zhí)行類加載的加載、驗(yàn)證际起、準(zhǔn)備拾碌、解析、初始化這幾個(gè)步驟
- 為bean的屬性設(shè)置值街望,并注入對(duì)其他bean的引用
- 調(diào)用bean的初始化方法
- 運(yùn)行:初始化完成后bean就可以使用了
- 銷毀:當(dāng)容器關(guān)閉校翔,調(diào)用bean的銷毀方法
8. Spring中的常用注解?
- @Controller:指明該類是一個(gè)controller
- @Service:指明該類是一個(gè)service
- @Repository:指明該類是一個(gè)dao
- @Componet:指明這是一個(gè)組件類灾前,交給Spring管理
- @RequestMapping:申明類或方法匹配的URL
- @Autowired:通過反射自動(dòng)裝配
9. 過濾器和攔截器的區(qū)別
過濾器
過濾器依賴Servlet容器防症,只能在容器初始化時(shí)調(diào)用一次,基于回調(diào)函數(shù)可以對(duì)幾乎所有請(qǐng)求進(jìn)行過濾哎甲。
應(yīng)用場(chǎng)景:可以用來統(tǒng)一字符編碼集蔫敲、過濾敏感字符、訪問權(quán)限控制炭玫。
實(shí)現(xiàn)方式:寫一個(gè)過濾器類實(shí)現(xiàn)filter接口奈嘿,復(fù)寫init、dofilter吞加、destory方法指么。
攔截器
攔截器依賴于Web框架,如果是使用SpringMVC就是依賴于SpringMVC框架榴鼎〔埽基于Java的反射機(jī)制,屬于AOP的一種應(yīng)用巫财,由于攔截器基于Web框架盗似,所以可以使用Spring的依賴注入進(jìn)行業(yè)務(wù)操作∑较睿可以在controller生命周期之內(nèi)多次調(diào)用赫舒。但是缺點(diǎn)是只能對(duì)controller請(qǐng)求進(jìn)行攔截悍及,對(duì)其他的一些比如直接訪問靜態(tài)資源的請(qǐng)求則沒辦法進(jìn)行攔截處理。
實(shí)現(xiàn)方式:實(shí)現(xiàn)HandlerInterceptor接口接癌,并復(fù)寫preHandle心赶、postHandle、afterCompletion方法缺猛。
區(qū)別
- 過濾器基于函數(shù)回調(diào)缨叫,攔截器基于反射
- 過濾器依賴于Servlet容器,攔截器依賴于Web框架
- 過濾器幾乎對(duì)所有請(qǐng)求起作用荔燎,攔截器只對(duì)action請(qǐng)求起作用
- 過濾器只能在容器初始化的時(shí)候被調(diào)用一次耻姥,攔截器可以在action的生命周期中被調(diào)用多次
- 攔截器可以獲取IOC容器中的各個(gè)bean,而過濾器就不行有咨,這點(diǎn)很重要琐簇,在攔截器里注入一個(gè)service,可以調(diào)用業(yè)務(wù)邏輯座享。
執(zhí)行順序
10. Spring中Bean的常用作用域
- Singleton(單例):使用此作用域的Bean在Spring容器中只有一個(gè)實(shí)例婉商,即無論多少個(gè)Bean引用它都指向同一個(gè)對(duì)象。(默認(rèn)作用域)
- Prototype(原型):每次通過Spring容器獲取Bean(注入或者通過應(yīng)用上下文獲取)的時(shí)候都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例渣叛。
- Session(會(huì)話):在Web應(yīng)用中据某,為每個(gè)回話創(chuàng)建一個(gè)bean實(shí)例(購物車)。
- Request(請(qǐng)求):在Web應(yīng)用中诗箍,為每個(gè)請(qǐng)求創(chuàng)建一個(gè)bean實(shí)例。
- global-session:全局會(huì)話挽唉,所有會(huì)話共享一個(gè)實(shí)例滤祖。
11. Spring中的核心對(duì)象
BeanFactory
BeanFactory產(chǎn)生一個(gè)新的實(shí)例,可以實(shí)現(xiàn)單例模式瓶籽,是Spring用來實(shí)例化匠童、配置和管理Bean的。它使用的是懶加載塑顺,如果某個(gè)Bean中的某個(gè)屬性沒有注入汤求,使用BeanFactory加載后要在第一次調(diào)用getBean方法才會(huì)拋出異常。
BeanWrapper
提供統(tǒng)一的get及set方法
ApplicationContext
ApplicationContext提供框架的實(shí)現(xiàn)严拒,是BeanFactory的實(shí)現(xiàn)類扬绪,是對(duì)它功能的一個(gè)拓展,ApplicationContext使用的是迫切加載裤唠,這個(gè)類會(huì)自動(dòng)解析我們配置的applicationContext.xml挤牛,然后根據(jù)我們配置的bean來new對(duì)象,將new好的對(duì)象放進(jìn)一個(gè)Map中种蘸,鍵就是我們bean的id,值就是new的對(duì)象墓赴。
12. Spring FactoryBean和BeanFactory 區(qū)別
BeanFactory是個(gè)bean 工廠竞膳,是一個(gè)工廠類(接口), 它負(fù)責(zé)生產(chǎn)和管理bean的一個(gè)工廠是ioc 容器最底層的接口诫硕,是個(gè)ioc容器坦辟,是spring用來管理和裝配普通bean的ioc容器(這些bean成為普通bean)。
FactoryBean是個(gè)bean章办,在IOC容器的基礎(chǔ)上給Bean的實(shí)現(xiàn)加上了一個(gè)簡(jiǎn)單工廠模式和裝飾模式锉走,是一個(gè)可以生產(chǎn)對(duì)象和裝飾對(duì)象的工廠bean,由spring管理后纲菌,生產(chǎn)的對(duì)象是由getObject()方法決定的(從容器中獲取到的對(duì)象不是“ FactoryBeanTest ” 對(duì)象)挠日。