1. 先分析Advice

before执行Cglib2AopProxy的intercept方法:

  1. /**
  2. * General purpose AOP callback. Used when the target is dynamic or when the
  3. * proxy is not frozen.
  4. */
  5. private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
  6.  
  7. private AdvisedSupport advised;
  8.  
  9. public DynamicAdvisedInterceptor(AdvisedSupport advised) {
  10. this.advised = advised;
  11. }
  12.  
  13. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  14. MethodInvocation invocation = null;
  15. Object oldProxy = null;
  16. boolean setProxyContext = false;
  17. Class targetClass = null;
  18. Object target = null;
  19. try {
  20. Object retVal = null;
  21. if (this.advised.exposeProxy) {
  22. // Make invocation available if necessary.
  23. oldProxy = AopContext.setCurrentProxy(proxy);
  24. setProxyContext = true;
  25. }
  26. // May be <code>null</code>. Get as late as possible to minimize the time we
  27. // "own" the target, in case it comes from a pool.
  28. target = getTarget();
  29. if (target != null) {
  30. targetClass = target.getClass();
  31. }
  32. List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  33. // Check whether we only have one InvokerInterceptor: that is,
  34. // no real advice, but just reflective invocation of the target.
  35. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
  36. // We can skip creating a MethodInvocation: just invoke the target directly.
  37. // Note that the final invoker must be an InvokerInterceptor, so we know
  38. // it does nothing but a reflective operation on the target, and no hot
  39. // swapping or fancy proxying.
  40. retVal = methodProxy.invoke(target, args);
  41. }
  42. else {
  43. // We need to create a method invocation...
  44. invocation = new CglibMethodInvocation(proxy, target, method, args,
  45. targetClass, chain, methodProxy);
  46. // If we get here, we need to create a MethodInvocation.
  47. retVal = invocation.proceed();
  48. }
  49.  
  50. retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
  51. return retVal;
  52. }
  53. finally {
  54. if (target != null) {
  55. releaseTarget(target);
  56. }
  57. if (setProxyContext) {
  58. // Restore old proxy.
  59. AopContext.setCurrentProxy(oldProxy);
  60. }
  61. }
  62. }

第一步:获取target

  1. target.getClass();

第二步:获取拦截器和advice,返回定义好的org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor示例

  1. /**
  2. * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
  3. * for the given method, based on this configuration.
  4. * @param method the proxied method
  5. * @param targetClass the target class
  6. * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
  7. */
  8. public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
  9. MethodCacheKey cacheKey = new MethodCacheKey(method);
  10. List cached = (List) this.methodCache.get(cacheKey);
  11. if (cached == null) {
  12. cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
  13. this, method, targetClass);
  14. this.methodCache.put(cacheKey, cached);
  15. }
  16. return cached;
  17. }

第三步创建一个方法的invocation

  1. // We need to create a method invocation...
  2. invocation = new CglibMethodInvocation(proxy, target, method, args,
  3. targetClass, chain, methodProxy);

第四步 执行aop的before方法

  1. public void before(Method method, Object[] args, Object target)
  2. throws Throwable {
  3. System.out.println(" Before method!");
  4. }

第五步 触发MethodBeforeAdviceInterceptor的invoke方法

  1. public Object invoke(MethodInvocation mi) throws Throwable {
  2. this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
  3. return mi.proceed();
  4. }

第六步:触发ReflectiveMethodInvocation的process方法

  1. public Object proceed() throws Throwable {
  2. // We start with an index of -1 and increment early.
  3. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  4. return invokeJoinpoint();
  5. }
  6.  
  7. Object interceptorOrInterceptionAdvice =
  8. this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  9. if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  10. // Evaluate dynamic method matcher here: static part will already have
  11. // been evaluated and found to match.
  12. InterceptorAndDynamicMethodMatcher dm =
  13. (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  14. if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
  15. return dm.interceptor.invoke(this);
  16. }
  17. else {
  18. // Dynamic matching failed.
  19. // Skip this interceptor and invoke the next in the chain.
  20. return proceed();
  21. }
  22. }
  23. else {
  24. // It's an interceptor, so we just invoke it: The pointcut will have
  25. // been evaluated statically before this object was constructed.
  26. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  27. }
  28. }

第七步,包装返回值Cglib2AopProxy

  1. /**
  2. * Wrap a return of this if necessary to be the proxy
  3. */
  4. private static Object massageReturnTypeIfNecessary(Object proxy, Object target, Method method, Object retVal) {
  5. // Massage return value if necessary
  6. if (retVal != null && retVal == target &&
  7. !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  8. // Special case: it returned "this".
  9. // Note that we can't help if the target sets a reference
  10. // to itself in another returned object.
  11. retVal = proxy;
  12. }
  13. return retVal;
  14. }

最后执行finanly方法

  1. finally {
  2. if (target != null) {
  3. releaseTarget(target);
  4. }
  5. if (setProxyContext) {
  6. // Restore old proxy.
  7. AopContext.setCurrentProxy(oldProxy);
  8. }

before,after,around,throw基本相似,不一一赘述

2.PointCut和Advisor为例

2.1 创建代理的过程

首先是ProxyFactoryBean获取对象代理

  1. /**
  2. * Return a proxy. Invoked when clients obtain beans from this factory bean.
  3. * Create an instance of the AOP proxy to be returned by this factory.
  4. * The instance will be cached for a singleton, and create on each call to
  5. * <code>getObject()</code> for a proxy.
  6. * @return a fresh AOP proxy reflecting the current state of this factory
  7. */
  8. public Object getObject() throws BeansException {
  9. initializeAdvisorChain();
  10. if (isSingleton()) {
  11. return getSingletonInstance();
  12. }
  13. else {
  14. if (this.targetName == null) {
  15. logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
  16. "Enable prototype proxies by setting the 'targetName' property.");
  17. }
  18. return newPrototypeInstance();
  19. }
  20. }

获取过程如下:

  1. /**
  2. * Return the singleton instance of this class's proxy object,
  3. * lazily creating it if it hasn't been created already.
  4. * @return the shared singleton proxy
  5. */
  6. private synchronized Object getSingletonInstance() {
  7. if (this.singletonInstance == null) {
  8. this.targetSource = freshTargetSource();
  9. if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
  10. // Rely on AOP infrastructure to tell us what interfaces to proxy.
  11. Class targetClass = getTargetClass();
  12. if (targetClass == null) {
  13. throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
  14. }
  15. setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
  16. }
  17. // Initialize the shared singleton instance.
  18. super.setFrozen(this.freezeProxy);
  19. this.singletonInstance = getProxy(createAopProxy());
  20. }
  21. return this.singletonInstance;
  22. }

父类创建代理的过程

  1. /**
  2. * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
  3. * create an AOP proxy with <code>this</code> as an argument.
  4. */
  5. protected final synchronized AopProxy createAopProxy() {
  6. if (!this.active) {
  7. activate();
  8. }
  9. return getAopProxyFactory().createAopProxy(this);
  10. }

调用代理工厂创建代理的过程

  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  2. if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
  3. Class targetClass = config.getTargetClass();
  4. if (targetClass == null) {
  5. throw new AopConfigException("TargetSource cannot determine target class: " +
  6. "Either an interface or a target is required for proxy creation.");
  7. }
  8. if (targetClass.isInterface()) {
  9. return new JdkDynamicAopProxy(config);
  10. }
  11. if (!cglibAvailable) {
  12. throw new AopConfigException(
  13. "Cannot proxy target class because CGLIB2 is not available. " +
  14. "Add CGLIB to the class path or specify proxy interfaces.");
  15. }
  16. return CglibProxyFactory.createCglibProxy(config);
  17. }
  18. else {
  19. return new JdkDynamicAopProxy(config);
  20. }
  21. }

可以看出,代理的实现主要是jdk本身自带的动态代理和cglib提供的代理。

2.2 获取代理的过程

  1. this.singletonInstance = getProxy(createAopProxy());

Cglib2AopProxy类的Object getProxy(ClassLoader classLoader)

  1. public Object getProxy(ClassLoader classLoader) {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());
  4. }
  5.  
  6. try {
  7. Class rootClass = this.advised.getTargetClass();
  8. Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
  9.  
  10. Class proxySuperClass = rootClass;
  11. if (AopUtils.isCglibProxyClass(rootClass)) {
  12. proxySuperClass = rootClass.getSuperclass();
  13. Class[] additionalInterfaces = rootClass.getInterfaces();
  14. for (int i = 0; i < additionalInterfaces.length; i++) {
  15. Class additionalInterface = additionalInterfaces[i];
  16. this.advised.addInterface(additionalInterface);
  17. }
  18. }
  19.  
  20. // Validate the class, writing log messages as necessary.
  21. validateClassIfNecessary(proxySuperClass);
  22.  
  23. // Configure CGLIB Enhancer...
  24. Enhancer enhancer = createEnhancer();
  25. if (classLoader != null) {
  26. enhancer.setClassLoader(classLoader);
  27. if (classLoader instanceof SmartClassLoader &&
  28. ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
  29. enhancer.setUseCache(false);
  30. }
  31. }
  32. enhancer.setSuperclass(proxySuperClass);
  33. enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
  34. enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
  35. enhancer.setInterceptDuringConstruction(false);
  36.  
  37. Callback[] callbacks = getCallbacks(rootClass);
  38. enhancer.setCallbacks(callbacks);
  39. enhancer.setCallbackFilter(new ProxyCallbackFilter(
  40. this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
  41.  
  42. Class[] types = new Class[callbacks.length];
  43. for (int x = 0; x < types.length; x++) {
  44. types[x] = callbacks[x].getClass();
  45. }
  46. enhancer.setCallbackTypes(types);
  47.  
  48. // Generate the proxy class and create a proxy instance.
  49. Object proxy;
  50. if (this.constructorArgs != null) {
  51. proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
  52. }
  53. else {
  54. proxy = enhancer.create();
  55. }
  56.  
  57. return proxy;
  58. }
  59. catch (CodeGenerationException ex) {
  60. throw new AopConfigException("Could not generate CGLIB subclass of class [" +
  61. this.advised.getTargetClass() + "]: " +
  62. "Common causes of this problem include using a final class or a non-visible class",
  63. ex);
  64. }
  65. catch (IllegalArgumentException ex) {
  66. throw new AopConfigException("Could not generate CGLIB subclass of class [" +
  67. this.advised.getTargetClass() + "]: " +
  68. "Common causes of this problem include using a final class or a non-visible class",
  69. ex);
  70. }
  71. catch (Exception ex) {
  72. // TargetSource.getTarget() failed
  73. throw new AopConfigException("Unexpected AOP exception", ex);
  74. }
  75. }

获取回调方法

  1. private Callback[] getCallbacks(Class rootClass) throws Exception {
  2. // Parameters used for optimisation choices...
  3. boolean exposeProxy = this.advised.isExposeProxy();
  4. boolean isFrozen = this.advised.isFrozen();
  5. boolean isStatic = this.advised.getTargetSource().isStatic();
  6.  
  7. // Choose an "aop" interceptor (used for AOP calls).
  8. Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
  9.  
  10. // Choose a "straight to target" interceptor. (used for calls that are
  11. // unadvised but can return this). May be required to expose the proxy.
  12. Callback targetInterceptor = null;
  13.  
  14. if (exposeProxy) {
  15. targetInterceptor = isStatic ?
  16. (Callback) new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
  17. (Callback) new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
  18. }
  19. else {
  20. targetInterceptor = isStatic ?
  21. (Callback) new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
  22. (Callback) new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
  23. }
  24.  
  25. // Choose a "direct to target" dispatcher (used for
  26. // unadvised calls to static targets that cannot return this).
  27. Callback targetDispatcher = isStatic ?
  28. (Callback) new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
  29.  
  30. Callback[] mainCallbacks = new Callback[]{
  31. aopInterceptor, // for normal advice
  32. targetInterceptor, // invoke target without considering advice, if optimized
  33. new SerializableNoOp(), // no override for methods mapped to this
  34. targetDispatcher, this.advisedDispatcher,
  35. new EqualsInterceptor(this.advised),
  36. new HashCodeInterceptor(this.advised)
  37. };
  38.  
  39. Callback[] callbacks;
  40.  
  41. // If the target is a static one and the advice chain is frozen,
  42. // then we can make some optimisations by sending the AOP calls
  43. // direct to the target using the fixed chain for that method.
  44. if (isStatic && isFrozen) {
  45. Method[] methods = rootClass.getMethods();
  46. Callback[] fixedCallbacks = new Callback[methods.length];
  47. this.fixedInterceptorMap = new HashMap(methods.length);
  48.  
  49. // TODO: small memory optimisation here (can skip creation for
  50. // methods with no advice)
  51. for (int x = 0; x < methods.length; x++) {
  52. List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
  53. fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
  54. chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
  55. this.fixedInterceptorMap.put(methods[x].toString(), new Integer(x));
  56. }
  57.  
  58. // Now copy both the callbacks from mainCallbacks
  59. // and fixedCallbacks into the callbacks array.
  60. callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
  61.  
  62. for (int x = 0; x < mainCallbacks.length; x++) {
  63. callbacks[x] = mainCallbacks[x];
  64. }
  65.  
  66. for (int x = 0; x < fixedCallbacks.length; x++) {
  67. callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
  68. }
  69.  
  70. this.fixedInterceptorOffset = mainCallbacks.length;
  71. }
  72. else {
  73. callbacks = mainCallbacks;
  74. }
  75. return callbacks;
  76. }

获取拦截器和动态拦截器Advice

  1. /**
  2. * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
  3. * for the given method, based on this configuration.
  4. * @param method the proxied method
  5. * @param targetClass the target class
  6. * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
  7. */
  8. public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
  9. MethodCacheKey cacheKey = new MethodCacheKey(method);
  10. List cached = (List) this.methodCache.get(cacheKey);
  11. if (cached == null) {
  12. cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
  13. this, method, targetClass);
  14. this.methodCache.put(cacheKey, cached);
  15. }
  16. return cached;
  17. }

继续调用DefaultAdvisorChainFactory:

  1. public List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass) {
  2. // This is somewhat tricky... we have to process introductions first,
  3. // but we need to preserve order in the ultimate list.
  4. List interceptorList = new ArrayList(config.getAdvisors().length);
  5. boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
  6. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
  7. Advisor[] advisors = config.getAdvisors();
  8. for (int i = 0; i < advisors.length; i++) {
  9. Advisor advisor = advisors[i];
  10. if (advisor instanceof PointcutAdvisor) {
  11. // Add it conditionally.
  12. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
  13. if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
  14. MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
  15. MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
  16. if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
  17. if (mm.isRuntime()) {
  18. // Creating a new object instance in the getInterceptors() method
  19. // isn't a problem as we normally cache created chains.
  20. for (int j = 0; j < interceptors.length; j++) {
  21. interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j], mm));
  22. }
  23. }
  24. else {
  25. interceptorList.addAll(Arrays.asList(interceptors));
  26. }
  27. }
  28. }
  29. }
  30. else if (advisor instanceof IntroductionAdvisor) {
  31. IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
  32. if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
  33. Interceptor[] interceptors = registry.getInterceptors(advisor);
  34. interceptorList.addAll(Arrays.asList(interceptors));
  35. }
  36. }
  37. else {
  38. Interceptor[] interceptors = registry.getInterceptors(advisor);
  39. interceptorList.addAll(Arrays.asList(interceptors));
  40. }
  41. }
  42. return interceptorList;
  43. }

NameMatchMethodPointcut

  1. public boolean matches(Method method, Class targetClass) {
  2. for (int i = 0; i < this.mappedNames.size(); i++) {
  3. String mappedName = (String) this.mappedNames.get(i);
  4. if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
  5. return true;
  6. }
  7. }
  8. return false;
  9. }

spring aop源码实现分析的更多相关文章

  1. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  4. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  5. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  6. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  7. 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

  8. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  9. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

随机推荐

  1. MSSQL数据库中Text类型字段在PHP中被截断之解 (转)

    在PHP中使用了MSSQL数据库,恰巧数据库中又使用了Text类型字段,于是问题产生了.每次从数据库中查询得到的数据总是被莫名的截断,一开始是以为我使用的PHP框架中对字符串的长度有所限制,后来发现这 ...

  2. iPhone的Push(推送通知)功能原理浅析

    第一部分:Push原理(以下绝大多数内容参考自.图片来自iPhone OS Reference Library)机制简介Push 的工作机制可以简单的概括为下图图中,Provider是指某个iPhon ...

  3. wordpress搬家换域名

    很多朋友开始接触wordpress都是在本地安装调试好了,再上传到服务器正常运营,我也是一样当我在本地测试好了准备将网站上线,在搬家到服务器的时候遇到过的一些问题,记录分享一下我是如何为wordpre ...

  4. 分享:写了一个 java 调用 C语言 开发的动态库的范例

    分享:写了一个 java 调用 C语言 开发的动态库的范例 cfunction.h   代码#pragma once#ifdef __cplusplusextern "C" {#e ...

  5. 嗅探、中间人sql注入、反编译--例说桌面软件安全性问题

    嗅探.中间人sql注入.反编译--例说桌面软件安全性问题 今天这篇文章不准备讲太多理论,讲我最近遇到的一个案例.从技术上讲,这个例子没什么高深的,还有一点狗屎运的成分,但是它又足够典型,典型到我可以讲 ...

  6. 冲刺阶段 day12

    项目进展 周二我们将专业管理部分又继续做了完善,之前漏掉的几项功能也都在熟能生巧中编写的越来越顺畅,但还差最后一点数据库部分没能实现,我们会尽快完成. 存在问题 还是与数据库的连接上出现问题,部分不能 ...

  7. json-smart 使用示例(推荐fastjson)

    关于json库,请使用fastjson,这是我用过的最好用的json库! 地址:https://github.com/alibaba/fastjson ======================== ...

  8. Ping!

    我知道我很久没有更新这个博客了,所以特意来更新一下,骚扰一下各位订户.我有几年没有写过很具体跟技术相关的文章了,而跟职业发展相关的文章也半年没更新了,所以最近准备开始写写技术文章.在此之前,我要先完结 ...

  9. Unity3D核心类型一览

    Unity3D核心类型一览 本文记录了Unity3D的最基本的核心类型.包括Object.GameObject.Component.Transform.Behaviour.Renderer.Colli ...

  10. Spring MVC 1

    ==============================  摘抄至<跟我学SpringMVC.pdf>  =========================== 1.首先用户发送请求— ...