問題描述:Controller方法誤寫成了private而導致500錯誤的問題。
找原因,找了大半天, 也翻了Spring MVC的源碼。。。終于,靈機一動,看到了 private :
原因分析
當某個Controller類中含有private方法,并且該類有需要spring注入的對象時,且在該私有方法有中使用spring注入的對象時會報空指針。
那么為什么會報空指針了。咱們看一下動態代理的過程,這里使用的是cglib代理。
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new MyInteceptor());
enhancer.setSuperclass(A1.class);
A1 proxy = (A1)enhancer.create();
上面是一個創建cglib代理的類。在CglibAopProxy中代碼就是:
這里有一點要記住,private和final修飾的方法,不會被代理。也就是說private和final的方法不會進入callBack。如果進入不了callBack,那么就進入不了被代理的目標對象。那么只能在proxy對象上執行private或final修飾的方法。而proxy對象是由cglib實例化的,里面沒有spring注入的對象。因些報空指針。
接下來分析下不能寫private的原因。
實際上SpringMVC本身對這個沒有限制,會找到所有用戶聲明(ReflectionUtils.USER_DECLARED_METHODS)的方法。
/**
* Pre-built MethodFilter that matches all non-bridge methods
* which are not declared on {@code java.lang.Object}.
* @since 3.0.5
*/
public static final MethodFilter USER_DECLARED_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return (!method.isBridge() && method.getDeclaringClass() != Object.class);
}
};
但是,如果用到了切面(恰巧,Controller中經常用到切面,我們也用了。。。):
<aop:aspectj-autoproxy proxy-target-class="true" />
或者
@EnableAspectJAutoProxy(proxyTargetClass = true)
這種,那么實際上最后會使用Cglib做代理,而生成的代理類會代理所有能夠代理的方法,換句話說,private方法肯定就沒有了。
被 private 和 final 修飾的方法不會進入callback,如果進入不了callback,那么就進入不了被代理的目標對象。那么只能在proxy對象上執行private或final修飾的方法。而proxy對象是由cglib實例化的,里面沒有spring注入的對象。因此,報錯:空指針異常NPE。
這就難怪,同一個Controller里面的兩個方法,請求過來,Controller的對象的地址居然是不同的,而且,可以看到,請求 private 方法的那次,Controller 對象是明顯經過CGLib增強代理的:
解決方案
tomcat啟動后使用 @Autowired注入bean成功,但調用方法的時候使用注入的bean對象都是null,最后發現我把這個方法的修飾符寫成了private,所以請求的方法不能用private修飾,得使用public。
另外的解決辦法,是通過實現一個ApplicationContext工具類進行手動注入。獲取這個ApplicationContext對象,我們就能手動從Spring獲取所需要的 bean :
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private ApplicationContextUtils(){}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static <T> T getBean(Class<T> beanClass) {
return applicationContext.getBean(beanClass);
}
public static <T> T getBean(String beanName, Class<T> beanClass) {
return applicationContext.getBean(beanName, beanClass);
}
}
在切面類中使用:
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SysOperateLogAspect {
private SysOperateLogService sysOperateLogService;
public SysOperateLogAspect() {
this.sysOperateLogService = ApplicationContextUtils.getBean(SysOperateLogService.class);
}
}
相關源碼
源碼:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
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。追溯到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),根據我們對方法處理的需求設置不同的回調;callback才是真正執行我們目標對象方法的地方。
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
CGLIB動態代理實現原理
一、 CGLIB介紹
CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫,
它可以在運行期擴展Java類與實現Java接口。Hibernate用它來實現PO(Persistent Object 持久化對象)字節碼的動態生成。
CGLIB是一個強大的高性能的代碼生成包。它廣泛的被許多AOP的框架使用,例如Spring AOP為他們提供
方法的interception(攔截)。CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼并生成新的類。
除了CGLIB包,腳本語言例如Groovy和BeanShell,也是使用ASM來生成java的字節碼。當然不鼓勵直接使用ASM,
因為它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
二、 CGLIB動態代理實例
實現一個業務類,注意,這個業務類并沒有實現任何接口:
package com.jpeony.spring.proxy.cglib;
public class HelloService {
public HelloService() {
System.out.println("HelloService構造");
}
/**
* 該方法不能被子類覆蓋,Cglib是無法代理final修飾的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
自定義MethodInterceptor:
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定義MethodInterceptor
*/
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理對象
* method:被代理對象方法
* objects:方法入參
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}
生成CGLIB代理對象調用目標方法:
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 代理類class文件存入本地磁盤方便我們反編譯查看源碼
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
// 通過CGLIB動態代理獲取代理對象的過程
Enhancer enhancer = new Enhancer();
// 設置enhancer對象的父類
enhancer.setSuperclass(HelloService.class);
// 設置enhancer的回調對象
enhancer.setCallback(new MyMethodInterceptor());
// 創建代理對象
HelloService proxy= (HelloService)enhancer.create();
// 通過代理對象調用目標方法
proxy.sayHello();
}
}
運行結果:
三、 CGLIB動態代理源碼分析
實現CGLIB動態代理必須實現MethodInterceptor(方法攔截器)接口,源碼如下:
/*
* Copyright 2002,2003 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.cglib.proxy;
/**
* General-purpose {@link Enhancer} callback which provides for "around advice".
* @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
* @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
*/
public interface MethodInterceptor
extends Callback
{
/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
* @param obj "this", the enhanced object
* @param method intercepted Method
* @param args argument array; primitive types are wrapped
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
* @see MethodProxy
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
這個接口只有一個intercept()方法,這個方法有4個參數:
1)obj表示增強的對象,即實現這個接口類的一個對象;
2)method表示要被攔截的方法;
3)args表示要被攔截方法的參數;
4)proxy表示要觸發父類的方法對象;
在上面的Client代碼中,通過Enhancer.create()方法創建代理對象,create()方法的源碼:
/**
* Generate a new class if necessary and uses the specified
* callbacks (if any) to create a new object instance.
* Uses the no-arg constructor of the superclass.
* @return a new instance
*/
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
該方法含義就是如果有必要就創建一個新類,并且用指定的回調對象創建一個新的對象實例,
使用的父類的參數的構造方法來實例化父類的部分。核心內容在createHelper()中,源碼如下:
private Object createHelper() {
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
preValidate()方法校驗callbackTypes、filter是否為空,以及為空時的處理。
通過newInstance()方法創建EnhancerKey對象,作為Enhancer父類AbstractClassGenerator.create()方法
創建代理對象的參數。
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}
真正創建代理對象方法在nextInstance()方法中,該方法為抽象類AbstractClassGenerator的一個方法,簽名如下:
abstract protected Object nextInstance(Object instance) throws Exception;
在子類Enhancer中實現,實現源碼如下:
protected Object nextInstance(Object instance) {
EnhancerFactoryData data = (EnhancerFactoryData) instance;
if (classOnly) {
return data.generatedClass;
}
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
return data.newInstance(argumentTypes, arguments, callbacks);
}
其中,data.newInstance(argumentTypes, arguments, callbacks)方法,
第一個參數為代理對象的構成器類型,第二個為代理對象構造方法參數,第三個為對應回調對象。
最后根據這些參數,通過反射生成代理對象,源碼如下:
/**
* Creates proxy instance for given argument types, and assigns the callbacks.
* Ideally, for each proxy class, just one set of argument types should be used,
* otherwise it would have to spend time on constructor lookup.
* Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
* with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
*
* @see #createUsingReflection(Class)
* @param argumentTypes constructor argument types
* @param arguments constructor arguments
* @param callbacks callbacks to set for the new instance
* @return newly created proxy
*/
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
setThreadCallbacks(callbacks);
try {
// Explicit reference equality is added here just in case Arrays.equals does not have one
if (primaryConstructorArgTypes == argumentTypes ||
Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
// If we have relevant Constructor instance at hand, just call it
// This skips "get constructors" machinery
return ReflectUtils.newInstance(primaryConstructor, arguments);
}
// Take a slow path if observing unexpected argument types
return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
} finally {
// clear thread callbacks to allow them to be gc'd
setThreadCallbacks(null);
}
}
最后生成代理對象:
將其反編譯后代碼如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.jpeony.spring.proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class HelloService$$EnhancerByCGLIB$$be45efdd extends HelloService implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$sayHello$0$Method;
private static final MethodProxy CGLIB$sayHello$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService$$EnhancerByCGLIB$$be45efdd");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "()V"}, (var1 = Class.forName("com.jpeony.spring.proxy.cglib.HelloService")).getDeclaredMethods())[0];
CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$0");
}
final void CGLIB$sayHello$0() {
super.sayHello();
}
public final void sayHello() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$4$Proxy;
}
break;
case 1535311470:
if (var10000.equals("sayHello()V")) {
return CGLIB$sayHello$0$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$1$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$2$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$3$Proxy;
}
}
return null;
}
public HelloService$$EnhancerByCGLIB$$be45efdd() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
HelloService$$EnhancerByCGLIB$$be45efdd var1 = (HelloService$$EnhancerByCGLIB$$be45efdd)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
HelloService$$EnhancerByCGLIB$$be45efdd var10000 = new HelloService$$EnhancerByCGLIB$$be45efdd();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
HelloService$$EnhancerByCGLIB$$be45efdd var10000 = new HelloService$$EnhancerByCGLIB$$be45efdd();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
HelloService$$EnhancerByCGLIB$$be45efdd var10000 = new HelloService$$EnhancerByCGLIB$$be45efdd;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
重點關注代理對象的sayHello方法:
public final void sayHello() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
}
從代理對象反編譯源碼可以知道,代理對象繼承于HelloService,攔截器調用intercept()方法,
intercept()方法由自定義MyMethodInterceptor實現,所以,最后調用MyMethodInterceptor中的intercept()方法,從而完成了由代理對象訪問到目標對象的動態代理實現。
參考:https://blog.csdn.net/yhl_jxy/article/details/80633194
其他的常見的 Spring @Autowired npe example
另外,也有其他的常見的 Spring @Autowired npe example, 特摘錄如下:
The Spring framework makes heavy use of Inversion of Control (IoC) to let you inject classes without having to worry about their scope, lifetime or cleanup.
A common error people hit is when they autowire a class and when they try to call a method on it find that it is null and they get a NullPointerException. So why didn’t Spring auto-wire your class for you? Here’s two possible reasons:
YOU INSTANTIATED THE A CLASS MANUALLY
Hi, 2005 called and asked for their code back. Yeah, OK, IoC is like the cool kid on the block and if you are using Spring then you need to be using it all the time. Here’s a code snippet of a Controller, Service and Repository that will result in a NullPointerException.
@Controller
public class Controller {
@GetMapping("/example")
public String example() {
MyService my = new MyService();
my.doStuff();
}
}
@Service
public class MyService() {
@Autowired
MyRepository repo;
public void doStuff() {
repo.findByName( "steve" );
}
}
@Repository
public interface MyRepository extends CrudRepository<My, Long> {
List<My> findByName( String name );
}
This will throw a NullPointerException in the service class when it tries to access the MyRepository auto-wired Repository, not because there is anything wrong with the wiring of the Repository but because you instantiated MyService() manually with MyService my = new MyService(). To fix this auto-wire the Service as well:
@Controller
public class Controller {
@Autowired
MyService service;
@GetMapping("/example")
public String example() {
service.doStuff();
}
}
@Service
public class MyService() {
@Autowired
MyRepository repo;
public void doStuff() {
repo.findByName( "steve" );
}
}
@Repository
public interface MyRepository extends CrudRepository<My, Long> {
List<My> findByName( String name );
}
YOU FORGOT TO ANNOTATE A CLASS AS A COMPONENT OR ONE OF ITS DESCENDANTS
Spring uses component scanning to find the classes that it needs to auto-wire and insert into classes for IoC. Basically, Spring is going to scan the project’s classpath (or paths you specified), find all of the @Component classes and make them available for auto-wiring. So if you forget to annotate a class it will not be auto-wired and when you try and use it you will get a null and a NullPointerException.
@Service, @Repository and @Controller are all specializations of @Component, so any class you want to auto-wire needs to be annotated with one of them. So auto-wiring this will cause a null:
public class MyService {
public void doStuff() {
}
}
but this will work fine
@Service
public class MyService {
public void doStuff() {
}
}
參考資料
[1].https://blog.csdn.net/liruichuan/article/details/101367819
[2].https://github.com/chrylis/spring_autowired_npe_example
[3].https://github.com/chrylis/spring_autowired_npe_example/tree/nonworking
[4].https://www.moreofless.co.uk/spring-mvc-java-autowired-component-null-repository-service/
[5].https://blog.csdn.net/yhl_jxy/article/details/80633194
Kotlin開發者社區
專注分享 Java、 Kotlin、Spring/Spring Boot、MySQL、redis、neo4j、NoSQL、Android、JavaScript、React、Node、函數式編程、編程思想、"高可用,高性能,高實時"大型分布式系統架構設計主題。
High availability, high performance, high real-time large-scale distributed system architecture design。
分布式框架:Zookeeper、分布式中間件框架等
分布式存儲:GridFS、FastDFS、TFS、MemCache、redis等
分布式數據庫:Cobar、tddl、Amoeba、Mycat
云計算、大數據、AI算法
虛擬化、云原生技術
分布式計算框架:MapReduce、Hadoop、Storm、Flink等
分布式通信機制:Dubbo、RPC調用、共享遠程數據、消息隊列等
消息隊列MQ:Kafka、MetaQ,RocketMQ
怎樣打造高可用系統:基于硬件、軟件中間件、系統架構等一些典型方案的實現:HAProxy、基于Corosync+Pacemaker的高可用集群套件中間件系統
Mycat架構分布式演進
大數據Join背后的難題:數據、網絡、內存和計算能力的矛盾和調和
Java分布式系統中的高性能難題:AIO,NIO,Netty還是自己開發框架?
高性能事件派發機制:線程池模型、Disruptor模型等等。。。
合抱之木,生于毫末;九層之臺,起于壘土;千里之行,始于足下。不積跬步,無以至千里;不積小流,無以成江河。
Kotlin 簡介
Kotlin是一門非研究性的語言,它是一門非常務實的工業級編程語言,它的使命就是幫助程序員們解決實際工程實踐中的問題。使用Kotlin 讓 Java程序員們的生活變得更好,Java中的那些空指針錯誤,浪費時間的冗長的樣板代碼,啰嗦的語法限制等等,在Kotlin中統統消失。Kotlin 簡單務實,語法簡潔而強大,安全且表達力強,極富生產力。
Java誕生于1995年,至今已有23年歷史。當前最新版本是 Java 9。在 JVM 生態不斷發展繁榮的過程中,也誕生了Scala、Groovy、Clojure 等兄弟語言。
Kotlin 也正是 JVM 家族中的優秀一員。Kotlin是一種現代語言(版本1.0于2016年2月發布)。它最初的目的是像Scala那樣,優化Java語言的缺陷,提供更加簡單實用的編程語言特性,并且解決了性能上的問題,比如編譯時間。 JetBrains在這些方面做得非常出色。
Kotlin語言的特性
用 Java 開發多年以后,能夠嘗試一些新的東西真是太棒了。如果您是 Java 開發人員,使用 Kotlin 將會非常自然流暢。如果你是一個Swift開發者,你將會感到似曾相識,比如可空性(Nullability)。 Kotlin語言的特性有:
1.簡潔
大幅減少樣板代碼量。
2.與Java的100%互操作性
Kotlin可以直接與Java類交互,反之亦然。這個特性使得我們可以直接重用我們的代碼庫,并將其遷移到 Kotlin中。由于Java的互操作性幾乎無處不在。我們可以直接訪問平臺API以及現有的代碼庫,同時仍然享受和使用 Kotlin 的所有強大的現代語言功能。
3.擴展函數
Kotlin 類似于 C# 和 Gosu, 它提供了為現有類提供新功能擴展的能力,而不必從該類繼承或使用任何類型的設計模式 (如裝飾器模式)。
4.函數式編程
Kotlin 語言一等支持函數式編程,就像Scala一樣。具備高階函數、Lambda 表達式等函數式基本特性。
5.默認和命名參數
在Kotlin中,您可以為函數中的參數設置一個默認值,并給每個參數一個名稱。這有助于編寫易讀的代碼。
6.強大的開發工具支持
而由于是JetBrains出品,我們擁有很棒的IDE支持。雖然Java到Kotlin的自動轉換并不是100% OK 的,但它確實是一個非常好的工具。使用 IDEA 的工具轉換Java代碼為 Kotlin 代碼時,可以輕松地重用60%-70%的結果代碼,而且修改成本很小。
Kotlin 除了簡潔強大的語法特性外,還有實用性非常強的API以及圍繞它構建的生態系統。例如:集合類 API、IO 擴展類、反射API 等。同時 Kotlin 社區也提供了豐富的文檔和大量的學習資料,還有在線REPL。
A modern programming language that makes developers happier. Open source forever