在上一篇中我用一个比较详细的案例描述了一下代理的概念,在这篇文章中,主要来看一下JDK动态代理和cglib子类代理java
首先咱们以一个简单的案例来讲一下面试
如今有这样一个需求,为一个短信功能提供入参日志打印、异常打印和处理、返回结果打印、方法调用结束打印。先来看一下短信功能的代码:spring
首先是短信接口ISmsSupport
编程
package proxy;
/** * 短信功能支持 * * @author 猪弟 * @since v1.0.0 */
public interface ISmsSupport {
boolean sendMsg(String content, String phoneNumber);
}
复制代码
而后是一个短信功能的实现类数组
package proxy;
/** * 短信功能实现 * * @author 猪弟 * @since v1.0.0 */
public class SmsSupportImpl implements ISmsSupport {
@Override
public boolean sendMsg(String content, String phoneNumber) {
//模拟异常
int temp = 1 / 0;
System.out.println(content + "," + phoneNumber);
return true;
}
}
复制代码
在实现类中,咱们没有作任何的日志和异常处理 接下来咱们用动态代理实现上面的需求,以及为何要用动态代理实现? 首先咱们要建一个代理类SmsProxy
安全
package proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** * 短信功能代理类 * * @author 猪弟 * @since v1.0.0 */
public class SmsProxy implements InvocationHandler {
/** * 日志实例 */
private final static Logger LOGGER = LoggerFactory.getLogger(SmsProxy.class);
/** * 被代理对象 */
private Object realSubject;
/** * 构造器 * * @param realSubject 被代理对象 */
public SmsProxy(Object realSubject) {
this.realSubject = realSubject;
}
/** * 获取代理对象的方法 * * @return 代理对象 */
public Object getProxy() {
Class<?> subjectClass = realSubject.getClass();
return Proxy.newProxyInstance(subjectClass.getClassLoader(), subjectClass.getInterfaces(), this);
}
/** * @param proxy 代理对象 * @param method 执行的目标方法 * @param args 方法参数 * @return Object 目标方法执行的结果 * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标方法的方法名
String methodName = method.getName();
//打印入参(前置通知)
LOGGER.info("{} 方法的入参:{}", methodName, args);
try {
//反射Reflect执行核心方法
Object result = method.invoke(realSubject, args);
//打印执行结果(返回后通知)
LOGGER.info("{}方法的返回结果为:{}", methodName,result);
return result;
} catch (Throwable e) {
//打印异常日志(异常通知)
LOGGER.error("执行目标方法发生异常,异常:", e);
return Boolean.FALSE;
} finally {
//方法执行完打印(后置通知)
LOGGER.info("方法执行完成");
}
}
}
复制代码
简单解释一下这个代理类bash
① 首先使用jdk动态代理要实现一个java为咱们提供的InvocationHandler
接口,并实现invoke
方法,下面是接口的源码。微信
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
复制代码
② 咱们要在代理类中定义一个Object
类型的属性用于引用被代理的对象,并经过构造器初始化。app
③ 在类中定义一个函数getProxy
用于获取代理对象,这里用到了Proxy
类的静态方法newInstance
,下面是方法的源码内容:框架
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
//要求传入的handler不能为空
Objects.requireNonNull(h);
//获取被代理对象实现的全部接口
final Class<?>[] intfs = interfaces.clone();
//获取安全管理器
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//检验代理的访问安全性
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/* * 查找或者生成一个指定的代理类,加载到内存中 * Look up or generate the designated proxy class. */
Class<?> cl = getProxyClass0(loader, intfs);
/* * Invoke its constructor with the designated invocation handler. * 使用指定的invocation handler 反射调用代理类的构造器 */
try {
if (sm != null) {
//检查访问权限
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理类的构造器对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
//传入的invocation handler
final InvocationHandler ih = h;
//若是类不是public的,先赋予权限
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//调用构造器对象的方法生成代理类实例
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
复制代码
④ 最后就是实现咱们的invoke
方法了,先看一下方法的结构和咱们的实现:
/** * @param proxy 代理对象 * @param method 执行的目标方法 * @param args 方法参数 * @return Object 目标方法执行的结果 * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标方法的方法名
String methodName = method.getName();
//打印入参(前置通知)
LOGGER.info("{} 方法的入参:{}", methodName, args);
try {
//反射Reflect执行核心方法
Object result = method.invoke(realSubject, args);
//打印执行结果(返回后通知)
LOGGER.info("{}方法的返回结果为:{}", methodName,result);
return result;
} catch (Throwable e) {
//打印异常日志(异常通知)
LOGGER.error("执行目标方法发生异常,异常:", e);
return Boolean.FALSE;
} finally {
//方法执行完打印(后置通知)
LOGGER.info("方法执行完成");
}
}
复制代码
是否是很简单呢,下面是入口程序:
package proxy;
/** * 程序入口 * * @author 猪弟 * @since v1.0.0 */
public class Bootstrap {
public static void main(String[] args){
demo1();
}
public static void demo1(){
ISmsSupport smsSupport = new SmsSupportImpl();
ISmsSupport proxy = (ISmsSupport) new SmsProxy(smsSupport).getProxy();
proxy.sendMsg("hello world", "110");
}
}
复制代码
咱们先看一下运行结果 先把SmsSupportImpl
中的int i = 1/ 0;
注释掉正常运行:
17:12:26.976 [main] INFO proxy.SmsProxy - sendMsg 方法的入参:[hello world, 110]
hello world,110
17:12:26.980 [main] INFO proxy.SmsProxy - sendMsg方法的返回结果为:true
17:12:26.980 [main] INFO proxy.SmsProxy - 方法执行完成
复制代码
再取消注释,发生异常:
17:13:31.169 [main] INFO proxy.SmsProxy - sendMsg 方法的入参:[hello world, 110]
17:13:31.175 [main] ERROR proxy.SmsProxy - 执行目标方法发生异常,异常:
java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at proxy.SmsProxy.invoke(SmsProxy.java:63)
at com.sun.proxy.$Proxy0.sendMsg(Unknown Source)
at proxy.Bootstrap.demo1(Bootstrap.java:19)
at proxy.Bootstrap.main(Bootstrap.java:12)
Caused by: java.lang.ArithmeticException: / by zero
at proxy.SmsSupportImpl.sendMsg(SmsSupportImpl.java:13)
... 8 common frames omitted
17:13:31.175 [main] INFO proxy.SmsProxy - 方法执行完成
复制代码
从上面的运行结果能够看出,打印了入参和返回结果,正确拦截了异常并打印了异常信息,还有方法调用完成的日志。好啦!到这里动态代理的实现就结束了,接下来看看为何要使用动态代理。
在这个案例中其实体现不出来动态代理的优点,为何呢,由于要打印日志的类太少了,彻底能够采用硬编码的方式去实现,那么想象一下有一万个类,每一个类中有10个方法,每一个方法以前都没有打日志,如今要你去实现为每一个方法都实现入参和返回结果打印,你还会采用硬编码的方式吗?此时动态代理就发挥了做用,你只须要写一个日志代理类,专门用来完成这个打印功能,上代码:
package proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** * 日志代理打印类 * * @author 猪弟 * @since v1.0.0 */
public class LogProxy<T> implements InvocationHandler {
/** * 日志实例 */
private final static Logger LOGGER = LoggerFactory.getLogger(LogProxy.class);
/** * 被代理对象 */
private T realSubject;
/** * 构造器 * * @param realSubject */
public LogProxy(T realSubject) {
this.realSubject = realSubject;
}
/** * 获取代理对象 * * @return 代理 */
public T getProxy() {
Class<?> subjectClass = realSubject.getClass();
return (T) Proxy.newProxyInstance(subjectClass.getClassLoader(), subjectClass.getInterfaces(), this);
}
/** * @param proxy 代理对象 * @param method 执行的目标方法 * @param args 方法参数 * @return Object 目标方法执行结果 * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标方法的方法名
String methodName = method.getName();
//打印入参(前置通知)
LOGGER.info("{} 方法的入参:{}", methodName, args);
try {
//执行核心方法
Object result = method.invoke(realSubject, args);
//打印执行结果(返回后通知)
LOGGER.info("{}方法的返回结果为:{}", methodName,result);
return result;
} catch (Throwable e) {
//打印异常日志(异常通知)
LOGGER.error("执行目标方法发生异常,异常:", e);
return Boolean.FALSE;
} finally {
//方法执行完打印(后置通知)
LOGGER.info("方法执行完成");
}
}
}
复制代码
其实这个类和前面的类区别不大,只是加入了泛型和把getProxy
的返回类型改为了泛型。 调用方式和前面的相似,结果也同样,入口程序:
package proxy;
/** * 程序入口 * * @author 猪弟 * @since v1.0.0 */
public class Bootstrap {
public static void main(String[] args){
demo2();
}
public static void demo2() {
ISmsSupport smsSupport = new SmsSupportImpl();
LogProxy<ISmsSupport> proxy = new LogProxy<>(smsSupport);
ISmsSupport smsProxy = proxy.getProxy();
smsProxy.sendMsg("hello world", "110");
}
}
复制代码
其实你会发现,虽然书写方便了,可是去修改调用方法的代码也是一件工做量很大的事情,咱们能够采用AOP来处理,在AOP中采用切入点表达式能够不用修改调用方法的代码就能实现使用代理打印日志。关于AOP留到下一篇中去讲述。
好的,咱们再来看看生成的代理类长什么样子,怎么看呢?固然是把内存中的对象存到硬盘,还记得在Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
里面调用了下面这句代码:
/* * Look up or generate the designated proxy class. */
Class<?> cl = getProxyClass0(loader, intfs);
复制代码
看看这个方法的源码:
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
/** * a cache of proxy classes */
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
复制代码
在Proxy类中有个属性proxyClassCache,这是一个WeakCache类型的静态变量。它指示了类加载器和代理类之间的映射。因此proxyClassCache的get方法用于根据类加载器和接口数组来获取Proxy类,若是已经存在则直接从cache中返回,若是没有则建立一个映射并更新cache表。
咱们跟一下代理类的建立流程: 调用Factory类的get方法,而它又调用了ProxyClassFactory类的apply方法,最终找到下面一行代码:
public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
return generateProxyClass(var0, var1, 49);
}
复制代码
咱们可使用下面的代码来生成代理类的class文件到磁盘:
public static void main(String[] args) throws IOException {
/** * @param s 生成文件的名字 * @param classes 实现的接口数组 * @return byte[] 类文件的字节码数组 */
byte[] classFile = ProxyGenerator.generateProxyClass("SmsSupportProxy",
SmsSupportImpl.class.getInterfaces());
File file = new File("D:/SmsSupportProxy.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
复制代码
运行生成一个SmsSupportProxy.class文件,用IDEA打开获得反编译的内容:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.ISmsSupport;
public final class SmsSupportProxy extends Proxy implements ISmsSupport {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public SmsSupportProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean sendMsg(String var1, String var2) throws {
try {
return ((Boolean)super.h.invoke(this, m3, new Object[]{var1, var2})).booleanValue();
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("proxy.ISmsSupport").getMethod("sendMsg", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
复制代码
这样,结合上面的handler,就很好明白为何代理类在调用目标方法时会执行invoke了吧
在上一篇文章末尾咱们介绍了三种代理的使用条件和使用限制,来回顾一下
① 静态代理能够代理某一类对象,这一类对象必须实现同一接口,因此它的使用条件就是被代理类要实现接口。上面的各类场景短信都实现了ISmsService接口,因此代理类能够代理全部场景短信实现类,并调用真正的短信发送方法去发送正确的场景短信。
① 动态代理的使用条件也是被代理类要实现接口,可是动态代理可以代理全部实现了接口的类,强大必然也会有缺点:动态代理依赖Java反射机制,反射是一个比较影响性能的功能,因此动态代理性能上会比静态代理低。
③ cglib子类代理,首先须要依赖第三方库,而后它是基于字节码来生成子类代理的,没有特定的使用条件,因此也不须要实现接口,它能够代理全部的类,因此论性能是比不上静态代理的。
从上面能够看出,前两种代理都须要实现接口才能使用,而CGLIB
不用
此处留一个面试中遇到的问题:
jdk代理中为何必定要实现接口呢,用抽象类不能够吗?用心思考一下,相信和我同样聪明的你必定能获得答案
好的,咱们把问题留到下一篇中解答
OK,接下来咱们仍是一样的一个需求,使用CGLIB代理的方式实现,若是你理解了上面动态代理的内容,CGLIB
的内容就能轻松的理解了,这里咱们只学习使用方式,关于原理他是根据字节码动态生成的子类,猪弟的水平还达不到就不具体解释了,待往后我猪拱神功练成之时在来说述原理。
先看一下短信发送类,没有实现任何接口:
package xin.sun.proxy.cglib;
/** * 短信功能实现 * * @author 猪弟 * @since v1.0.0 */
public class SmsSupport {
public boolean sendMsg(String content, String phoneNumber) {
//模拟异常
//int temp = 1 / 0;
System.out.println(content + "," + phoneNumber);
return true;
}
}
复制代码
再看一下代理工厂类:
引入了 spring 的 maven 依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
复制代码
package xin.sun.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/** * @author 猪弟 */
public class ProxyFactory implements MethodInterceptor {
/** * 被代理对象 */
private Object target;
/** * 构造器 * * @param target */
public ProxyFactory(Object target) {
this.target = target;
}
/** * 获取代理对象实例 * * @return Object 代理对象实例 */
public Object getProxyInstance() {
/** * 工具类 */
Enhancer enhancer = new Enhancer();
/** * 设置父类 */
enhancer.setSuperclass(target.getClass());
/** * 设置回调对象 */
enhancer.setCallback(this);
/** * 建立子类代理对象,并返回 */
return enhancer.create();
}
/** * 重写拦截方法 * * @param o 代理对象 * @param method 委托类方法 * @param objects 方法参数 * @param methodProxy 代理方法的MethodProxy对象 * @return Object 目标方法执行结果 * @throws Throwable */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//目标方法的方法名
String methodName = method.getName();
//打印入参(前置通知)
LOGGER.info("{} 方法的入参:{}", methodName, objects);
try {
//执行核心方法
Object result = method.invoke(realSubject, objects);
//打印执行结果(返回后通知)
LOGGER.info("{}方法的返回结果为:{}", methodName, result);
return result;
} catch (Throwable e) {
//打印异常日志(异常通知)
LOGGER.error("执行目标方法发生异常,异常:", e);
return Boolean.FALSE;
} finally {
//方法执行完打印(后置通知)
LOGGER.info("方法执行完成");
}
}
}
复制代码
简单解释一下这个代理类
① 首先使用cglib代理要实现一个框架为咱们提供的MethodInterceptor
接口,并实现intercept
方法。
② 咱们要在代理类中定义一个Object
类型的属性用于引用被代理的对象,并经过构造器初始化。
③ 在类中定义一个函数getProxyInstance
用于获取代理对象,这里和jdk动态代理有点区别,这里使用的是Enhancer
这个类,看一下方法的实现:
public Object getProxyInstance() {
/** * Enhancer工具类 */
Enhancer enhancer = new Enhancer();
/** * 设置父类 */
enhancer.setSuperclass(realSubject.getClass());
/** * 设置回调对象,咱们实现的这个类自己就是实现了接口的,因此传本类的实例就行了 */
enhancer.setCallback(this);
/** * 建立子类代理对象,并返回 */
return enhancer.create();
}
复制代码
④ 最后就是实现咱们的intercept
方法了,先看一下方法的结构和咱们的实现:
/** * 重写拦截方法 * * @param o 代理对象 * @param method 委托类方法 * @param objects 方法参数 * @param methodProxy 代理方法的MethodProxy对象 * @return Object 目标方法执行结果 * @throws Throwable */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//目标方法的方法名
String methodName = method.getName();
//打印入参(前置通知)
LOGGER.info("{} 方法的入参:{}", methodName, objects);
try {
//执行核心方法
Object result = method.invoke(realSubject, objects);
//打印执行结果(返回后通知)
LOGGER.info("{}方法的返回结果为:{}", methodName, result);
return result;
} catch (Throwable e) {
//打印异常日志(异常通知)
LOGGER.error("执行目标方法发生异常,异常:", e);
return Boolean.FALSE;
} finally {
//方法执行完打印(后置通知)
LOGGER.info("方法执行完成");
}
}
复制代码
一样的,咱们写一个入口程序来检测一下:
package xin.sun.proxy.cglib;
/** * @author 猪弟 */
public class Bootstrap {
public static void main(String[] args) {
demo();
}
public static void demo(){
ProxyFactory proxyFactory = new ProxyFactory(new SmsSupport());
SmsSupport proxyInstance = (SmsSupport) proxyFactory.getProxyInstance();
proxyInstance.sendMsg("CGLIB 代理","4008008820");
}
}
复制代码
看看正常运行的结果:
22:06:26.439 [main] INFO xin.sun.proxy.cglib.ProxyFactory - sendMsg 方法的入参:[CGLIB 代理, 4008008820]
CGLIB 代理,4008008820
22:06:26.445 [main] INFO xin.sun.proxy.cglib.ProxyFactory - sendMsg方法的返回结果为:true
22:06:26.445 [main] INFO xin.sun.proxy.cglib.ProxyFactory - 方法执行完成
复制代码
一样的,咱们人为制造一个异常int temp = 1 / 0;
,看看异常可否被捕获:
22:10:36.454 [main] INFO xin.sun.proxy.cglib.ProxyFactory - sendMsg 方法的入参:[CGLIB 代理, 4008008820]
22:10:36.460 [main] ERROR xin.sun.proxy.cglib.ProxyFactory - 执行目标方法发生异常,异常:
java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at xin.sun.proxy.cglib.ProxyFactory.intercept(ProxyFactory.java:80)
at xin.sun.proxy.cglib.SmsSupport$$EnhancerByCGLIB$$8593c815.sendMsg(<generated>)
at xin.sun.proxy.cglib.Bootstrap.demo(Bootstrap.java:15)
at xin.sun.proxy.cglib.Bootstrap.main(Bootstrap.java:9)
Caused by: java.lang.ArithmeticException: / by zero
at xin.sun.proxy.cglib.SmsSupport.sendMsg(SmsSupport.java:13)
... 8 common frames omitted
22:10:36.461 [main] INFO xin.sun.proxy.cglib.ProxyFactory - 方法执行完成
复制代码
从结果能够看到,完美的捕获到了异常
能够看到,在cglib中的短信发送类没有实现接口,一样的也实现了代理的功能,因此cglib才是真的能代理全部的类,这也是对jdk动态代理的一个补充吧,可是在Spring的AOP中默认是使用jdk动态代理的,若是被代理类没有实现接口,Spring会自动为咱们切换cglib子类代理,是否是以为Spring很人性化,其实 Spring AOP 为咱们提供了更加方便的方式去作代理,下一篇文章会讲述Spring中的AOP编程。
Spring AOP 把代理作了一次封装,用起来固然更加方便了,可是要想成为java大神,仍是要追究其实质的,这也是为何我要写这两篇文章的缘由,全部的框架都是基于Java基础+编程思想来实现的,其实明白了框架的设计,咱们本身也能实现Spring IOC DI AOP,等有机会为你们手撸一波Spring框架,固然Spring是高度的抽象,因此源码阅读起来没那么容易,可是其思想仍是很容易懂的,学会思想才是真的叼,毕竟思想语言无关。
磊叔是Spring专家,感兴趣的多看看磊叔的博客哦!!!
为了提供更加方便的阅读,小伙伴们能够关注咱们的公众微信号哦...