今天遇到一個問題,使用springMVC時 一不小心提供了一個private的方法,之前訪問都沒問題,今天給項目加監控(Metric),由於監控採用切面監控所有帶有註解 @RequestMapping的方法,再訪問私有方法時所有注入的bean全都是null導致空指針異常,那麼爲什麼會造成這種現象出現呢?這就要說到spring的代理模式了,代理模式的基本原理這裏就不細細闡述了,針對問題查閱了資料和源碼,原來都是spring 使用cglib代理造成的,那麼接下來就簡單分析一下原因。
首先查看工程中AOP配置:
得知是採用的cglib的代理方式,然後查看源碼中(AbstractAutoProxyCreator.class)
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, Object[] specificInterceptors, TargetSource targetSource) {
// ....
ProxyFactory proxyFactory = new ProxyFactory();
//複製當前類的一些屬性
proxyFactory.copyFrom(this);
// 如果在配置文件中配置的aop標籤的屬性proxy-target-class爲false,
if (!proxyFactory.isProxyTargetClass()) {
// 是否需要代理當前類而不是代理接口,根據preserveTargetClass屬性來判斷Boolean.TRUE.equals(bd.getAttribute("preserveTargetClass")
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 如果代理的是接口,則添加代理接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 對增強器進行包裝,有些增強是通過攔截器等方式來實現的,所以這裏統一封裝爲 Advisor 進行處理
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 加入增強器
proxyFactory.addAdvisors(advisors);
// 設置要代理的類
proxyFactory.setTargetSource(targetSource);
// 用戶自定義代理
customizeProxyFactory(proxyFactory);
// 該屬性用來控制代理工廠被配置以後,是否還允許修改通知,默認爲false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 創建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
// 添加接口代理
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
//....
if (hasReasonableProxyInterface) {
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}
proxyFactory.getProxy(getProxyClassLoader());
通過這行代碼我們可以看到有兩個實現類分別是:JdkDynamicAopProxy 和 CglibAopProxy ,通過前面的xml配置我們知道採用的是Cglib代理方式 所以我們又追溯到CglibAopProxy 源碼中
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
//設置需要創建子類的類
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Exception ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
return (this.constructorArgs != null ?
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
enhancer.create());
}
我們看到有一行代碼 enhancer.setSuperclass(proxySuperClass); 這說明什麼 cglib採用繼承的方式通過生成子類的方式創建代理類;生成代理類前,設置了CallbackFilter,CallbackFilter允許我們在方法層設置回調(callback),根據我們對方法處理的需求設置不同的回調(對cglib代理原理理解有些欠缺暫時留空 後續補上);callback纔是真正執行我們目標對象方法的地方;
有一點,private和final修飾的方法,不會被代理。也就是說private和final的方法不會進入callBack。如果進入不了callBack,那麼就進入不了被代理的目標對象。那麼只能在proxy對象上執行private或final修飾的方法。而proxy對象是由cglib實例化的,裏面沒有spring注入的對象。因些報空指針。
模擬代碼 :
public class AOPDemo {
public static void main(String[] args) throws Exception {
HelloWorld helloWorld = new HelloWorld();
ProxyFactory proxyFactory = new ProxyFactory();
HelloWorld hello = (HelloWorld) proxyFactory.createProxy(helloWorld);
hello.say();
}
}
/**
* 示例類
*/
class HelloWorld{
public HelloWorld() {
}
public void say() throws Exception{
System.out.println("hello world");
}
}
class ProxyFactory implements MethodInterceptor{
/**
* 被代理的對象
*/
private Object targetObj;
public Object createProxy(Object target){
this.targetObj = target;
Enhancer enhancer = new Enhancer();
//設置代理目標
enhancer.setSuperclass(this.targetObj.getClass());
//設置回調過濾器,過濾器返回值要與設置的回調處理數組保持下標一致
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
return 0;
}
});
//設置單一回調對象,在調用中攔截對目標方法的調用
//enhancer.setCallback(this);
//設置回調處理數組 要與CallbackFilter返回值保持一致
enhancer.setCallbacks(new Callback[]{new ProxyFactory()});
//設置類加載器
enhancer.setClassLoader(this.targetObj.getClass().getClassLoader());
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
//前置處理通知
System.out.println("before method invoke ...");
result = methodProxy.invokeSuper(obj, args);
//後置通知
System.out.println("after method invoke ...");
} catch (Exception e) {
//異常通知
System.out.println("deal exception ...");
} finally {
//方法返回前通知
System.out.println("before return ...");
}
return result;
}
}