<meta charset="utf-8">
一、ApplicationContext
ApplicationContext被稱為Spring上下文,Application Context 是 Spring 中較高級(jí)的容器。和 BeanFactory 類似,它可以加載配置文件中定義的 bean,將所有的 bean 集中在一起,當(dāng)有請(qǐng)求的時(shí)候分配 bean。 另外,它增加了企業(yè)所需要的功能,比如,從屬性文件中解析文本信息和將事件傳遞給所指定的監(jiān)聽器。
1、BeanFactory容器
這是一個(gè)最簡(jiǎn)單的容器,它主要的功能是為依賴注入 (DI) 提供支持,這個(gè)容器接口在 org.springframework.beans.factory.BeanFactor 中被定義。BeanFactory 和相關(guān)的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍舊保留在 Spring 中,主要目的是向后兼容已經(jīng)存在的和那些 Spring 整合在一起的第三方框架。在資源寶貴的移動(dòng)設(shè)備或者基于 applet 的應(yīng)用當(dāng)中, BeanFactory 會(huì)被優(yōu)先選擇。否則,一般使用的是 ApplicationContext。
這個(gè)接口是Spring中最底層的接口,提供了最簡(jiǎn)單的容器的功能,只提供了實(shí)例化對(duì)象和取對(duì)象的功能。
2、ApplicationContext與BeanFactory的關(guān)系
2.1 聯(lián)系
在IDEA中可以查看兩者的繼承關(guān)系

從上圖可以看出,AllicationContext這個(gè)接口繼承了ListableBeanFactory與HierarchicalBeanFactory這兩個(gè)接口,而這兩個(gè)接口都繼承了BeanFactory這個(gè)接口。所以它們都可以用來配置XML屬性,也支持屬性的自動(dòng)注入。
2.2 區(qū)別
1)功能
BeanFactory是Spring中最底層的容器,提供的功能較為簡(jiǎn)單,ApplicationContext繼承了兩個(gè)接口,相對(duì)而言可以提供更多功能:
國(guó)際化(i18n)(MessageSource)
訪問資源,如URL和文件(ResourceLoader)
載入多個(gè)(有繼承關(guān)系)上下文 ,使得每一個(gè)上下文都專注于一個(gè)特定的層次,比如應(yīng)用的web層
消息發(fā)送、響應(yīng)機(jī)制(ApplicationEventPublisher)
AOP(攔截器)
2)裝載Bean
BeanFactory在啟動(dòng)的時(shí)候不會(huì)去實(shí)例化Bean,中有從容器中拿Bean的時(shí)候才會(huì)去實(shí)例化;ApplicationContext在啟動(dòng)的時(shí)候就把所有的Bean全部實(shí)例化了。它還可以為Bean配置lazy-init=true來讓Bean延遲實(shí)例化;
- 延遲實(shí)例化的優(yōu)點(diǎn):(BeanFactory)
應(yīng)用啟動(dòng)的時(shí)候占用資源很少;對(duì)資源要求較高的應(yīng)用,比較有優(yōu)勢(shì);
-
不延遲實(shí)例化的優(yōu)點(diǎn): (ApplicationContext)
- 所有的Bean在啟動(dòng)的時(shí)候都加載,系統(tǒng)運(yùn)行的速度快;
-
在啟動(dòng)的時(shí)候所有的Bean都加載了,我們就能在系統(tǒng)啟動(dòng)的時(shí)候,盡早的發(fā)現(xiàn)系統(tǒng)中的配置問題
- 建議web應(yīng)用,在啟動(dòng)的時(shí)候就把所有的Bean都加載了。(把費(fèi)時(shí)的操作放到系統(tǒng)啟動(dòng)中完成)
3 、重要的實(shí)現(xiàn)類

ApplicationContext的實(shí)現(xiàn)類如上圖所示,重要的實(shí)現(xiàn)類有兩個(gè):ClassPathXmlApplicationContext與FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext:從classpath(resources目錄等同于classpath)中讀取xml文件加載已經(jīng)被定義的bean。(注意,使用這個(gè)類的對(duì)象讀取xml文件后需要手動(dòng)調(diào)用close()(繼承自抽象類AbstractApplicationContext)方法,否則會(huì)引發(fā)警告)
- FileSystemXmlApplicationContext:從系統(tǒng)文件中讀取xml文件加載已經(jīng)被定義的bean。
二、Spring AOP
AOP(Aspect Oriented Programming)稱為面向切面編程,在程序開發(fā)中主要用來解決一些系統(tǒng)層面上的問題,比如日志,事務(wù),權(quán)限等待,Struts2的攔截器設(shè)計(jì)就是基于AOP的思想,是個(gè)比較經(jīng)典的例子。
在不改變?cè)械倪壿嫷幕A(chǔ)上,增加一些額外的功能。代理也是這個(gè)功能,讀寫分離也能用aop來做。與AOP有關(guān)的兩個(gè)概念是OOP(面向?qū)ο缶幊?與POP(面向過程編程)。AOP的核心技術(shù)是代理。在介紹AOP之前有必要了解一下Java的動(dòng)態(tài)代理。
1、動(dòng)態(tài)代理
在程序運(yùn)行過程中產(chǎn)生的這個(gè)對(duì)象,而程序運(yùn)行過程中產(chǎn)生對(duì)象其實(shí)就是我們剛才反射講解的內(nèi)容,所以,動(dòng)態(tài)代理其實(shí)就是通過反射來生成一個(gè)代理對(duì)象,動(dòng)態(tài)代理可以讓我們?cè)诓恍薷脑创a的情況下增加新的功能。Java的動(dòng)態(tài)代理有兩種方式
1.1 使用java.lang.reflect包
這種方式要求被代理的類必須實(shí)現(xiàn)某個(gè)接口,實(shí)現(xiàn)原理是利用對(duì)象的類的字節(jié)碼接口,寫出一個(gè)新的類到本地區(qū),通過編譯器直接編譯成.class文件,再通過類加載器加載進(jìn)來。最重要的一步是:
MyInterface p = (MyInterface) Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好");
method.invoke(student);
return null;
}
});
其中,Student類實(shí)現(xiàn)了MyInterface這個(gè)接口,student是被代理的對(duì)象。
1.2 使用cglib
這是非Java原生的動(dòng)態(tài)代理,效率更高,限制更小,而且不需要被代理的類實(shí)現(xiàn)接口。
public static void main(String[] args) {
//導(dǎo)入包 cglib-core asm ant ant-launcher
//創(chuàng)建運(yùn)行器
MethodInterceptor mi = new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("運(yùn)行前");
arg3.invokeSuper(arg0, arg2);
System.out.println("運(yùn)行后");
return null;
}
};
//獲取代理類
Enhancer enhancer = new Enhancer();
//設(shè)置父類
enhancer.setSuperclass(Demo.class);
//運(yùn)行任務(wù)
enhancer.setCallback(mi);
//創(chuàng)建代理對(duì)象
Demo d = (Demo)enhancer.create();
d.method();
}
cglib實(shí)現(xiàn)動(dòng)態(tài)代理的原理是代理類去繼承目標(biāo)類,然后重寫其中目標(biāo)類的方法,這樣每次調(diào)用代理類的方法都會(huì)被方法攔截器攔截,在攔截器中才調(diào)用目標(biāo)類的該方法。
2、第一種實(shí)現(xiàn)方式
第一種實(shí)現(xiàn)方式就是使用Java原生的動(dòng)態(tài)代理,需要四個(gè)類一個(gè)接口:
Person接口,這個(gè)接口是目標(biāo)類的實(shí)現(xiàn)接口
package com.qianfeng.aop01;
public interface Person {
void eat();
void drink();
}
Student類,這個(gè)類是目標(biāo)類
package com.qianfeng.aop01;
public class Student implements Person {
@Override
public void eat() {
System.out.println("I can eat");
}
@Override
public void drink() {
System.out.println("I can run");
}
}
MyAspect類,我們想要進(jìn)行切面的類,可以擴(kuò)展我們想要增加的功能
package com.qianfeng.aop01;
public class MyAspect {
public void before(){
System.out.println("---------before----------");
}
public void after(){
System.out.println("---------after----------");
}
}
AOP01Test類,測(cè)試類
package com.qainfeng.aop01;
import com.qianfeng.aop01.Person;
import com.qianfeng.aop01.PersonFactory;
import org.junit.Test;
public class AOP01Test {
@Test
public void testStudent(){
Person p = PersonFactory.getPerson();
p.eat();
p.drink();
}
}
PersonFactory類,工廠類,用于生成Person對(duì)象
package com.qianfeng.aop01;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class PersonFactory {
public static Person getPerson(){
//創(chuàng)建被代理對(duì)象(目標(biāo)對(duì)象)
Person p = new Student();
//創(chuàng)建自定義切面類對(duì)象
MyAspect ma = new MyAspect();
//創(chuàng)建代理對(duì)象
Person p1 = (Person) Proxy.newProxyInstance(PersonFactory.class.getClassLoader(), p.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ma.before();
Object obj = method.invoke(p,args);
ma.after();
return obj;
}
});
//返回代理對(duì)象
return p1;
}
}
需要重點(diǎn)說明的是PersonFactory類中的Proxy.newProxyInstance()方法,這個(gè)方法用來實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的代理,這個(gè)方法需要三個(gè)參數(shù),第一個(gè)參數(shù)是目標(biāo)類的加載器、第二個(gè)參數(shù)是目標(biāo)類的所有接口,第三個(gè)參數(shù)是一個(gè)實(shí)現(xiàn)了InvocationHandler接口的類的對(duì)象,首先,我們查看InvocationHandler的注釋:
/**
* {@code InvocationHandler} is the interface implemented by
* the <i>invocation handler</i> of a proxy instance.
*
* <p>Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
*/
InvocationHanler是被一個(gè)代理實(shí)例的調(diào)用處理程序?qū)崿F(xiàn)的接口,每一個(gè)代理實(shí)例都有一個(gè)關(guān)聯(lián)的調(diào)用處理程序,當(dāng)一個(gè)代理實(shí)例的方法被調(diào)用的時(shí)候,方法調(diào)用被編碼分派到它的調(diào)用處理程序的invoke方法。通過閱讀注釋我們知道每次調(diào)用代理實(shí)例的方法時(shí)都會(huì)調(diào)用invoke方法。再來看一下invoke方法的注釋:
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*/
這個(gè)方法處理一個(gè)代理對(duì)象的方法調(diào)用然后返回結(jié)果。參數(shù)proxy是方法被調(diào)用的代理實(shí)例。參數(shù)method是與接口實(shí)例中被調(diào)用的接口方法一直的方法實(shí)例,參數(shù)args是傳過來的參數(shù),是一個(gè)對(duì)象數(shù)組。最后再來看一下Proxy類中的newProxyInstance方法:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
*/
這個(gè)方法返回指定接口的代理類的實(shí)例。參數(shù)loader是定義代理類的類加載器,參數(shù)interfaces是代理類將要實(shí)現(xiàn)的接口集合。參數(shù)h是一個(gè)調(diào)用處理程序。
3、第二種實(shí)現(xiàn)方式
第二種實(shí)現(xiàn)方式使用了cglib,由于不需要實(shí)現(xiàn)接口,這里只需要四個(gè)類:Student、AOP02Test、MyAspect、StudentFactory,其中前三個(gè)類與第一種實(shí)現(xiàn)方式基本一致,只不過不需要實(shí)現(xiàn)接口,這里詳細(xì)解釋一下第四個(gè)類
package com.qianfeng.aop02;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class StudentFactory {
public static Student getStudent(){
Enhancer en = new Enhancer();
//設(shè)置父類
en.setSuperclass(Student.class);
Student student = new Student();
MyAspect ma = new MyAspect();
//設(shè)置回調(diào)方法
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
ma.before();
Object obj = method.invoke(student,objects);
ma.after();
return obj;
}
});
//將動(dòng)態(tài)生成的代理類的對(duì)象強(qiáng)轉(zhuǎn)為目標(biāo)類的對(duì)象
Student student1 = (Student) en.create();
return student1;
}
}
Enhancer類是一個(gè)字節(jié)碼增強(qiáng)器,用來動(dòng)態(tài)生成代理類的對(duì)象,這里看一下注釋:
/**
* Generates dynamic subclasses to enable method interception. This
* class started as a substitute for the standard Dynamic Proxy support
* included with JDK 1.3, but one that allowed the proxies to extend a
* concrete base class, in addition to implementing interfaces. The dynamically
* generated subclasses override the non-final methods of the superclass and
* have hooks which callback to user-defined interceptor
* implementations.
*/
生成動(dòng)態(tài)的子類用來確保方法攔截,這個(gè)類作為一種標(biāo)準(zhǔn)動(dòng)態(tài)代理支持的替代,但是允許代理去擴(kuò)展一個(gè)具體的基類,并且實(shí)現(xiàn)接口。動(dòng)態(tài)生成的子類重寫了父類的非final方法。
MethodInterceptor是一個(gè)接口,實(shí)現(xiàn)MethodInterceptor 接口,在調(diào)用目標(biāo)對(duì)象的方法時(shí),就可以實(shí)現(xiàn)在調(diào)用方法之前、調(diào)用方法過程中、調(diào)用方法之后對(duì)其進(jìn)行控制。intercept方法和第一種實(shí)現(xiàn)方式中的invoke方法有點(diǎn)類似,不同的是多了一個(gè)參數(shù)MethodProxy,這個(gè)參數(shù)是對(duì)當(dāng)前被調(diào)用方法的代理。
4、第三種實(shí)現(xiàn)方式
第三種實(shí)現(xiàn)方式基于xml文件,使用了昨天學(xué)過的IoC,需要四個(gè)文件:Student.java、MyAspect.java、Person.java(這個(gè)是接口)、AOC03Test.java與beans.xml,最重要的是beans.xml和MyAspect類,這個(gè)類需要實(shí)現(xiàn)一個(gè)接口:
package com.qianfeng.aop03;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor {
public void before(){
System.out.println("---------before----------");
}
public void after(){
System.out.println("---------after----------");
}
//重寫invoke方法,注意實(shí)現(xiàn)類
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
before();
Object obj = invocation.proceed();
after();
return obj;
}
}
AOC03Test.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AOP03Test {
@Test
public void testStudent(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans2.xml");
Student student = ac.getBean("proxy",Student.class);
student.eat();
student.drink();
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="st" class="com.qianfeng.aop03.Student" />
<bean id="my" class="com.qianfeng.aop03.MyAspect" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.qianfeng.aop03.Person" />
<property name="target" ref="st" />
<property name="interceptorNames" value="my" />
<property name="optimize" value="true" />
</bean>
</beans>
ProxyFactoryBean代理的FactoryBean對(duì)象,我們現(xiàn)在要代理的是st包含四個(gè)屬性注入:
- interfaces: 接口對(duì)象們
<list>
<value>com.qfedu.aop03.IUserService</value>
<value>com.qfedu.aop03.IUserService</value>
<value>com.qfedu.aop03.IUserService</value>
</list> - target:目標(biāo)對(duì)象,哪個(gè)對(duì)象將被以代理的方式創(chuàng)建
- interceptorNames:攔截對(duì)象的名稱,自定義的MethodInterceptor對(duì)象,注意它的包結(jié)構(gòu)組成,和第二種方法中的不一樣。注意使用的是value因?yàn)檫@里要的是名稱而不是對(duì)象,所以不使用ref。
- optimize:boolean類型的值:
true:強(qiáng)制使用cglib的動(dòng)態(tài)代理方式
false:使用jdk自帶的動(dòng)態(tài)代理
cglib:code generation library,代碼生成庫(kù),性能更高