關於springMVC中controller中方法使用private和public問題

今天遇到一個問題,使用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;
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章