Spring框架

1、Springmvc和SpringBoot区别

一句话来概括:Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的一个 MVC 框架,而Spring Boot 是基于Spring的一套快速开发整合包
总的来说,Spring 就像一个大家族,有众多衍生产品例如 Boot,Security,JPA等等。但他们的基础都是Spring 的 IOC 和 AOP,IOC提供了依赖注入的容器,而AOP解决了面向切面的编程,然后在此两者的基础上实现了其他衍生产品的高级功能;Spring MVC是基于 Servlet 的一个 MVC 框架,主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种xml,properties处理起来比较繁琐。于是为了简化开发者的使用,Spring社区创造性地推出了Spring Boot,它遵循约定优于配置,极大降低了Spring使用门槛,但又不失Spring原本灵活强大的功能,下面用一张图来描述三者的关系:
在这里插入图片描述
spring:
我们说到Spring,一般指代的是Spring Framework,它是一个开源的应用程序框架,提供了一个简易的开发方式,通过这种开发方式,将避免那些可能致使代码变得繁杂混乱的大量的业务/工具对象,说的更通俗一点就是由框架来帮你管理这些对象,包括它的创建,销毁等,比如基于Spring的项目里经常能看到的Bean,它代表的就是由Spring管辖的对象。而在被管理对象与业务逻辑之间,Spring通过IOC(控制反转)架起使用的桥梁,IOC也可以看做Spring最核心最重要的思想,通过IOC这种方式对业务代码没有任何侵入,它有效的实现松耦合。
Spring MVC:
Spring MVC是Spring的一部分,Spring 出来以后,大家觉得很好用,于是按照这种模式设计了一个 MVC框架(一些用Spring 解耦的组件),主要用于开发WEB应用和网络接口,它是Spring的一个模块,通过Dispatcher Servlet, ModelAndView 和 View Resolver,让应用开发变得很容易
一个典型的Spring MVC应用开发分为下面几步:
首先通过配置文件声明Dispatcher Servlet:
通过配置文件声明servlet详情,如MVC resource,data source,bean等
若需添加其它功能,如security,则需添加对应配置:
增加业务代码,如controller,service,model等,最后生成war包,通过容器进行启动
在这里插入图片描述
SpringBoot:
Spring越来越庞大,其配置文件也越来越繁琐,太多复杂的xml文件也一直是Spring被人诟病的地方,特别是近些年其他简洁的WEB方案层出不穷,Spring社区推出了Spring Boot,它的目的在于实现自动配置,降低项目搭建的复杂度
Spring Boot遵循的也是约定优于配置原则
它提供了四个主要的特性,能够改变开发Spring应用程序的方式:
Spring Boot Starter: 它将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中,这里可以找到目前所有的starter项目。
自动配置: Spring Boot的自动配置特性利用了Spring 4对条件化配置的支持,合理地推测应用所需的bean并自动化配置它们,减少了你自己需要配置的数量。
命令行接口(Command-line interface,CLI):Spring Boot的CLI发挥了Groovy编程语言的优势,并结合自动配置进一步简化Spring应用的开发。
Actuator: 它为Spring Boot应用添加了一定的管理特性。
SpringBoot的优点?
Spring由于其繁琐的配置,一度被人认为“配置地狱”,各种XML、Annotation配置,让人眼花缭乱,而且如果出错了也很难找出原因。SpringBoot帮助开发者快速启动一个Web容器;SpringBoot继承了原有Spring框架的优秀基因;SpringBoot简化了使用Spring的过程。
SpringBoot的缺点?
Spring Boot作为一个微框架,离微服务的实现还是有距离的。没有提供相应的服务发现和注册的配套功能,自身的acturator所提供的监控功能,也需要与现有的监控对接。没有配套的安全管控方案,对于REST的落地,还需要自行结合实际进行URI的规范化工作。

2、Spring中有哪些模块

  1.  Spring AOP  面相切面编程
    
  2.  Spring ORM  Hibernate|mybatis|JDO
    
  3.  Spring Core  提供bean工厂 IOC
    
  4.  Spring Dao  JDBC支持
    
  5.  Spring Context  提供了关于UI支持,邮件支持等
    
  6.  Spring Web 提供了web的一些工具类的支持
    
  7.  Spring MVC  提供了web mvc , webviews , jsp ,pdf ,export
    
  1. Spring Core:
    Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
    2.Spring Context:
    构建于Core封装包基础上的 Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。
    3.Spring DAO:
    DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。
    4.Spring ORM:
    ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。
    5.Spring AOP:
    Spring的 AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。
    6.Spring Web:
    Spring中的 Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。
    7.Spring Web MVC:
    Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。

3、@Autowired的实现原理

@Autowired和@Resource的区别:
在Java中使用@Autowired和@Resource注解进行装配,这两个注解分别是:
1、@Autowired按照默认类型(类名称)装配依赖对象,默认情况下它要求依赖对象必须存在,
如果允许为null,可以设置它的required属性为false,如果我们按名称装配,可以结合@Qualifie注解一起使用。
如:
@Autowired @qualifie("personDaoBean")
private PersonDaoBean personDaoBean;

@Resource默认按照名称(name="test")进行装配,名称可以通过
@resource的name属性设定,当找不到与名称匹配的bean才会按类型装配

注意:如果没有指定name属性,并且安装默认的名称依然找不到依赖对象时,
@Resource会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

大体实现步骤也就是原理:
1、定义个注解类,可以有属性。可以参考这个文章[5分钟定义个自己的注解类](https://blog.csdn.net/wolf_love666/article/details/85009082)
2、写一个带有注解的服务类
3、要注入的DAO类
4、Xml代码绑定注入id和实现类
5、注解处理器实现在加载运行的时候先读取xml绑定利用反射找到实现类
具体实现步骤可以参考这个文章https://zxf-noimp.iteye.com/blog/1071765

引申Q:@RequestBody 和 @ResponseBody 区别?
https://www.cnblogs.com/PreachChen/p/8901536.html

4、spring的实现原理(如果你来实现,来搭一个spring框架,请问你如何去实现?)

step1:创建一个WebApplicationContext
step2:配置并且刷新Bean
step3:将容器初始化到servlet上下文中

  • 如果让你自己实现,如何实现?
  • 1、ContextLoaderListener继承上下文ContextLoader类和ServletContextListener接口(继承了事件监听器EventListener)
  • 2、重写初始化上下文接口和销毁上下文接口。
  • 3、初始化上下文接口,创建createContextLoader和初始化initWebApplicationContext
  • 4、closeWebApplicationContext和清除监听器cleanupAttributes
    在这里插入图片描述

5、 Bean的默认作用范围是什么?其他的作用范围?

https://blog.csdn.net/elim168/article/details/75581612

6、Bean的初始化和销毁

(1)、java配置方式:使用@Bean的initMethod和destroyMethod(相当于XML配置的init-method和destory-method)
(2)、注解方式,利用jsr250标准的@PostConstruct和@PreDestory

7、Spring Bean 的生命周期

Spring Bean 的生命周期
实例化==>
填充属性==>
调用BeanNameAware的setBeanName方法==>
调用BeanFactoryAware的setBeanDactory方法==>
调用ApplicationContext方法==>
调用BeanPostProcess的postProcessBeforeInitialization方法==>
调用InitializingBean的afterPropertiesSet方法==>
调用定制的初始化方法==>
调用BeanPostProcess的postProcessAfterInitialization方法==>
Bean准备就绪==>
调用DispostbleBean的destory方法==>
调用定制的销毁方法
Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,
Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

8、spring的bean配置的几种方式(spring的注入bean的方式)

https://www.jianshu.com/p/34692cdf7d5f
https://blog.csdn.net/tuzongxun/article/details/53580695

9、xml 中配置的 init、destroy 方法怎么可以做到调用具体的方法?

10、项目用 Spring 比较多,有没有了解 Spring 的原理?AOP 和 IOC 的原理,自己实现IOC要怎么做,哪些步骤?ioc初始化流程 ioc,aop原理(Spring 中 IOC 和 AOP 的理解?)spring Aop的实现原理,具体说说—4次

引申Q:自己实现IOC,IOC初始化流程
https://blog.csdn.net/qq_38182963/article/details/78715109
https://blog.csdn.net/qq_38182963/article/details/78724305

SpringAOP:

AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构的另一种考虑,补充了 OOP(Object-Oriented Programming)面向对象编程。在 OOP 中模块化的关键单元是类,而在 AOP 中,模块化单元是切面。也就是说 AOP 关注的不再是类,而是一系列类里面需要共同能力的行为。

四个问题点:
讲解 OOP 与 AOP 的简单对比,以及 AOP 的基础名词,比如面试中经常会被问到的 point cut、advice、join point 等。
讲解面试经常会被问到的 JDK 动态代理和 CGLIB 代理原理以及区别。
讲解 Spring 框架中基于 Schema 的 AOP 实现原理。
讲解 Spring 框架中如何基于 AOP 实现的事务管理。

OOP 与 AOP 的简单对比:

我们知道在 OOP 中模块化的关键单元是类,类封装了一类对象的行为和状态,当多个类有共同的属性和行为时候我们把这些共同的东西封装为一个基类,然后多个类可以通过继承基类的方式来复用这些共同的东西,如果子类需要定制基类行为则可以使用多态。
OOP 中使用类来提供封装,继承,多态三个特性。
当我们需要在多个不相关的类的某些已有的行为里面添加一个共同的非业务逻辑时候,比如我们需要统计一些业务方法的执行耗时时候,以往做法需要在统计耗时的行为里面写入计算耗时的代码,在 OOP 里面这种不涉及业务的散落在多个类的行为里面的代码叫做横切(Cross-cutting)代码,OOP 中这种方式缺点一是业务逻辑行为受到了计算耗时代码干扰(业务逻辑行为应该只专注业务),缺点二是计算耗时的代码不能被复用。
而在 AOP 中模块化单元是切面(Aspect),它将那些影响多个类的共同行为封装到可重用的模块中,然后你就可以决定在什么时候对哪些类的哪些行为执行进行拦截(切点),并使用封装好的可重用模块里面的行为(通知)对其拦截的业务行为进行功能增强,而不需要修改业务模块的代码,切面就是对此的一个抽象描述。
AOP 中有以下基础概念:
Join point(连接点):程序执行期间的某一个点,例如执行方法或处理异常时候的点。在 Spring AOP 中,连接点总是表示方法的执行。
Advice(通知):通知是指一个切面在特定的连接点要做的事情。通知分为方法执行前通知,方法执行后通知,环绕通知等。许多 AOP 框架(包括 Spring)都将通知建模为拦截器,在连接点周围维护一系列拦截器(形成拦截器链),对连接点的方法进行增强。
Pointcut(切点):一个匹配连接点(Join point)的谓词表达式。通知(Advice)与切点表达式关联,并在切点匹配的任何连接点(Join point)(例如,执行具有特定名称的方法)上运行。切点是匹配连接点(Join point)的表达式的概念,是AOP的核心,并且 Spring 默认使用 AspectJ 作为切入点表达式语言。
Aspect(切面):它是一个跨越多个类的模块化的关注点,它是通知(Advice)和切点(Pointcut)合起来的抽象,它定义了一个切点(Pointcut)用来匹配连接点(Join point),也就是需要对需要拦截的那些方法进行定义;它定义了一系列的通知(Advice)用来对拦截到的方法进行增强;
Target object(目标对象):被一个或者多个切面(Aspect)通知的对象,也就是需要被 AOP 进行拦截对方法进行增强(使用通知)的对象,也称为被通知的对象。由于在 AOP 里面使用运行时代理,所以目标对象一直是被代理的对象。
AOP proxy(AOP 代理):为了实现切面(Aspect)功能使用 AOP 框架创建一个对象,在 Spring 框架里面一个 AOP 代理要么指 JDK 动态代理,要么指 CgLIB 代理。
Weaving(织入):是将切面应用到目标对象的过程,这个过程可以是在编译时(例如使用 AspectJ 编译器),类加载时,运行时完成。Spring AOP 和其它纯 Java AOP 框架一样,是在运行时执行植入。
Advisor:这个概念是从 Spring 1.2的 AOP 支持中提出的,一个 Advisor 相当于一个小型的切面,不同的是它只有一个通知(Advice),Advisor 在事务管理里面会经常遇到,这个后面会讲到。
相比 OOP,AOP 有以下优点:
业务代码更加简洁,例如当需要在业务行为前后做一些事情时候,只需要在该行为前后配置切面进行处理,无须修改业务行为代码。
切面逻辑封装性好,并且可以被复用,例如我们可以把打日志的逻辑封装为一个切面,那么我们就可以在多个相关或者不相关的类的多个方法上配置该切面。

JDK 动态代理和 CGLIB 代理原理以及区别:

在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类,本节就简单来介绍这两种代理的原理和区别。

JDK 动态代理
由于 JDK 代理是对接口进行代理,所以首先写一个接口类:
public interface UserServiceBo {    
public  int add();
}

然后实现该接口如下:
public class UserServiceImpl implements UserServiceBo {    
@Override
    public int add() {
        System.out.println("--------------------add----------------------");        
        return 0;
    }
}
JDK 动态代理是需要实现 InvocationHandler 接口,这里创建一个 InvocationHandler 的实现类:
public class MyInvocationHandler implements InvocationHandler {    
	private Object target;   
	public MyInvocationHandler(Object target) {        
		super();        
		this.target = target;
	    }   
     @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {       
     //(1)
        System.out.println("-----------------begin "+method.getName()+"-----------------");        
        //(2)
        Object result = method.invoke(target, args);        
        //(3)
        System.out.println("-----------------end "+method.getName()+"-----------------");        
        return result;
    }   
     public Object getProxy(){        
     //(4)
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
         target.getClass().getInterfaces(), this);
    }

}
public static void main(String[] args) {  
//(5)打开这个开关,可以把生成的代理类保存到磁盘
 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");  
  //(6)创建目标对象(被代理对象)
  UserServiceBo service = new UserServiceImpl(); 
   //(7)创建一个InvocationHandler实例,并传递被代理对象
  MyInvocationHandler handler = new MyInvocationHandler(service);  //(8)生成代理类
  UserServiceBo proxy = (UserServiceBo) handler.getProxy();
  proxy.add();
}

其中代码(6)创建了一个 UserServiceImpl 的实例对象,这个对象就是要被代理的目标对象。
代码(7)创建了一个 InvocationHandler 实例,并传递被代理目标对象 service 给内部变量 target。
代码(8)调用 MyInvocationHandler 的 getProxy 方法使用 JDK 生成一个代理对象。
代码(5)设置系统属性变量 sun.misc.ProxyGenerator.saveGeneratedFiles 为 true,这是为了让代码(8)生成的代理对象的字节码文件保存到磁盘。
运行上面代码会在项目的 com.sun.proxy 下面会生成 $Proxy0.class 类,经反编译后核心 Java 代码如下:

package com.sun.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import proxy.JDK.UserServiceBo;public final class $Proxy0
  extends Proxy
  implements UserServiceBo{  private static Method m1;  private static Method m3;  private static Method m0;  private static Method m2;  public $Proxy0(InvocationHandler paramInvocationHandler)
  {    super(paramInvocationHandler);
  }  public final int add()
  {    try
    {    //(9)第一个参数是代理类本身,第二个是实现类的方法,第三个是参数
      return h.invoke(this, m3, null);
    }    catch (Error|RuntimeException localError)
    {      throw localError;
    }    catch (Throwable localThrowable)
    {      throw new UndeclaredThrowableException(localThrowable);
    }
  }

 ...  static
  {    try
    {

      m3 = Class.forName("proxy.JDK.UserServiceBo").getMethod("add", new Class[0]);
    ...      return;
    }    catch (NoSuchMethodException localNoSuchMethodException)
    ...
  }
}

代理类 $Proxy0 中代码(9)中的 h 就是 main 函数里面创建的 MyInvocationHandler 的实例,h.invoke(this, m3, null) 实际就是调用的 MyInvocationHandler 的 invoke 方法,而后者则是委托给被代理对象进行执行,这里可以对目标对象方法进行拦截,然后对其功能进行增强。
另外代码(8)生成的 proxy 对象实际就是 $Proxy0 的一个实例,当调用 proxy.add() 时候,实际是调用的代理类 P r o x y 0 的 a d d 方 法 , 后 者 内 部 则 委 托 给 M y I n v o c a t i o n H a n d l e r 的 i n v o k e 方 法 , i n v o k e 方 法 内 部 有 调 用 了 目 标 对 象 s e r v i c e 的 a d d 方 法 。 那 么 接 口 ( U s e r S e r v i c e B o ) 、 目 标 对 象 ( 被 代 理 对 象 U s e r S e r v i c e I m p l ) 、 代 理 对 象 ( Proxy0 的 add 方法,后者内部则委托给 MyInvocationHandler 的 invoke 方法,invoke 方法内部有调用了目标对象 service 的 add 方法。 那么接口(UserServiceBo)、目标对象(被代理对象 UserServiceImpl)、代理对象( Proxy0addMyInvocationHandlerinvokeinvokeserviceaddUserServiceBoUserServiceImplProxy0)三者具体关系可以使用下图表示:
在这里插入图片描述
可知 JDK 动态代理是对接口进行的代理;代理类实现了接口,并继承了 Proxy 类;目标对象与代理对象没有什么直接关系,只是它们都实现了接口,并且代理对象执行方法时候内部最终是委托目标对象执行具体的方法。

CGLIB 动态代理:

相比 JDK 动态代理对接口进行代理,CGLIB 则是对实现类进行代理,这意味着无论目标对象是否有接口,都可以使用 CGLIB 进行代理。
下面结合一个使用 CGLIB 对实现类进行动态代理的简单代码来讲解。
使用 CGLIB 进行代理需要实现 MethodInterceptor,创建一个方法拦截器 CglibProxy 类:

public class CglibProxy implements MethodInterceptor {    //(10)
    private Enhancer enhancer = new Enhancer();    //(11)
    public Object getProxy(Class clazz) {        //(12)设置被代理类的Class对象
        enhancer.setSuperclass(clazz);        //(13)设置拦截器回调
        enhancer.setCallback( this);        return enhancer.create();
    }    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(obj.getClass().getName()+"."+method.getName());
       Object result = proxy.invokeSuper(obj, args);        return result;
    }
}
public void testCglibProxy() {//(14)生成代理类到本地
 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/zhuizhumengxiang/Downloads");    //(15)生成目标对象
    UserServiceImpl service = new UserServiceImpl();    //(16)创建CglibProxy对象
    CglibProxy cp = new CglibProxy();    //(17)生成代理类
    UserServiceBo proxy = (UserServiceBo) cp.getProxy(service.getClass());
    proxy.add();
}
执行上面代码会在 /Users/zhuizhumengxiang/Downloads 目录生成代理类UserServiceImpl$$EnhancerByCGLIB$$d0bce05a.class 文件,反编译后部分代码如下:
public class UserServiceImpl$$EnhancerByCGLIB$$d0bce05a extends UserServiceImpl
  implements Factory{static void CGLIB$STATICHOOK1()
  {    //(18)空参数
    CGLIB$emptyArgs = new Object[0];    //(19)获取UserServiceImpl的add方法列表
    Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "add", "()I" }, (localClass2 = Class.forName("zlx.cglib.zlx.UserServiceImpl")).getDeclaredMethods());
    CGLIB$add$0$Method = tmp191_188[0];    //(20)创建CGLIB$add$0,根据创建一个MethodProxy
    CGLIB$add$0$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "add", "CGLIB$add$0");

  }  static
  {
    CGLIB$STATICHOOK1();
  }  //(21)
  final int CGLIB$add$0()
  {    return super.add();
  }  //(22)
  public final int add()
  {
    ...    //(23)获取拦截器,这里为CglibProxy的实例
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;    if (tmp17_14 != null)
    { //(24)调用拦截器的intercept方法
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$add$0$Method, CGLIB$emptyArgs, CGLIB$add$0$Proxy);
      tmp36_31;      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }    return super.add();
  }
 }
代码(18)创建了一个 CGLIB$emptyArgs,因为 add 方法是无入参的,所以这里创建的 Object 对象个数为0,这对象是 CglibProxy 拦截器的 intercept 的第三个参数。
代码(19)获取 UserServiceImpl 的 add 方法列表,并把唯一方法赋值给 CGLIB$add$0$Method,这个对象是 CglibProxy 拦截器的 intercept 第二个参数。
代码(20)创建了一个 MethodProxy 对象,当调用 MethodProxy 对象的 invokeSuper 方法时候会直接调用代理对象的 CGLIB$add$0 方法,也就是直接调用父类 UserServiceImpl 的 add 方法,这避免了一次反射调用,创建的 MethodProxy 对象是 CglibProxy 拦截器的 intercept 的第四个参数。
代码(22)是代理类的 add 方法,代码(17)生成的代理对象 proxy 就是UserServiceImpl$$EnhancerByCGLIB$$d0bce05a 的一个实例,当调用 proxy.add() 方法时候就是调用的代码(22),从代码(22)可知内部调用了拦截器 CglibProxy 的 intercept 方法,可知 intercept 的第一个参数就是代理对象本身。
那么接口(UserServiceBo)、目标对象(被代理对象 UserServiceImpl),代理对象(UserServiceImpl$$EnhancerByCGLIB$$d0bce05a)三者具体关系可以使用下图表示:

在这里插入图片描述
知接口和代理对象没有啥关系,代理对象是继承了目标对象和实现了 Factory 接口。

总结:
JDK 动态代理机制只能对接口进行代理,其原理是动态生成一个代理类,这个代理类实现了目标对象的接口,目标对象和代理类都实现了接口,但是目标对象和代理类的 Class 对象是不一样的,所以两者是没法相互赋值的。
CGLIB 是对目标对象本身进行代理,所以无论目标对象是否有接口,都可以对目标对象进行代理,其原理是使用字节码生成工具在内存生成一个继承目标对象的代理类,然后创建代理对象实例。由于代理类的父类是目标对象,所以代理类是可以赋值给目标对象的,自然如果目标对象有接口,代理对象也是可以赋值给接口的。
CGLIB 动态代理中生成的代理类的字节码相比 JDK 来说更加复杂。
JDK 使用反射机制调用目标类的方法,CGLIB 则使用类似索引的方式直接调用目标类方法,所以 JDK 动态代理生成代理类的速度相比 CGLIB 要快一些,但是运行速度比 CGLIB 低一些,并且 JDK 代理只能对有接口的目标对象进行代理。

Spring 框架中基于 Schema 的 AOP 实现原理:

Spring 提供了两种方式对 AOP 进行支持:基于 Schema 的 AOP,基于注解的 AOP。
基于 Schema 的 AOP 允许您基于 XML 的格式配置切面功能,Spring 2.0 提供了新的“aop”命名空间标记来定义切面的支持,基于注解的 AOP 则允许您使用 @Aspect 风格来配置切面。
本文就来先讲讲基于 Schema 的 AOP 的实现原理。
AOP 简单使用
一个切面实际上是一个被定义在 Spring application context 里面的一个正常的 Java 对象,配置切面对目标对象进行增强时候,一般使用下面配置格式:

<!--(1) -->
    <bean id="helloService" class="zlx.test.aop.HelloServiceBoImpl" />

    <!--(2) -->
    <bean id="myAspect" class="zlx.test.aop.MyAspect" />

    <aop:config>
        <!--(3) -->
        <aop:pointcut id="pointcut"
            expression="execution(* *..*BoImpl.sayHello(..))"/>
        <!--(4) -->
        <aop:aspect ref="myAspect">
            <!--(4.1) -->
            <aop:before pointcut-ref="pointcut" method="beforeAdvice" />
            <!--(4.2) -->
            <aop:after pointcut="execution(* *..*BoImpl.sayHello(..))"
                method="afterAdvice" />
            <!--(4.3) -->               
             <aop:after-returning
                pointcut="execution(* *..*BoImpl.sayHelloAfterReturn(..))" method="afterReturningAdvice"
                arg-names="content" returning="content" />
            <!--(4.4) -->               
            <aop:after-throwing pointcut="execution(* *..*BoImpl.sayHelloThrowExecption(..))"
                method="afterThrowingAdvice" arg-names="e" throwing="e" />
            <!--(4.5) -->               
            <aop:around pointcut="execution(* *..*BoImpl.sayHelloAround(..))"
                method="aroundAdvice" /> 
        </aop:aspect>
    </aop:config>
    <!--(5) -->
    <bean id = "tracingInterceptor" class="zlx.test.aop.TracingInterceptor"/>

    <!--(6) -->
    <aop:config>
        <aop:pointcut id="pointcutForadVisor" expression="execution(* *..*BoImpl.sayHelloAdvisor(..))" />
        <aop:advisor pointcut-ref="pointcutForadVisor" advice-ref="tracingInterceptor" />
    </aop:config>

代码(1)创建一个要被 AOP 进行功能增强的目标对象(Target object),HelloServiceBoImpl 的代码如下:

public class HelloServiceBoImpl implements HelloServiceBo{ @Override
public void sayHello(String content) {
System.out.println(“sayHello:” + content);
} @Override
public String sayHelloAround(String content) {
System.out.println(" sayHelloAround:" + content); return content;
} @Override
public String sayHelloAfterReturn(String content) {
System.out.println(“sayHelloAround:” + content); return content;
} @Override
public void sayHelloThrowExecption() {
System.out.println(“sayHelloThrowExecption”); throw new RuntimeException(“hello Im an exception”);
}
}
public interface HelloServiceBo { public void sayHello(String content); public String sayHelloAround(String content); public String sayHelloAfterReturn(String content); public void sayHelloThrowExecption();
}
代码(2)实质是定义了切面要使用的一系列的通知方法(Advice),用来对目标对象(Target object)的方法进行增强,MyAspect 的代码如下:

public class MyAspect {    public void beforeAdvice(String content) {
        System.out.println("---before advice "+ "---");
    }    public void afterAdvice(JoinPoint jp) {
        System.out.println("---after advice " + jp.getArgs()[0].toString()+"---");
    }    public Object afterReturningAdvice(Object value) {
        System.out.println("---afterReturning advice " + value+"---");        return value + " ha";
    }    public void afterThrowingAdvice(Exception e) {
        System.out.println("---after throwing advice exception:" + e+"---");
    }    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        Object[] obj = pjp.getArgs();
        String content = (String) obj[0];
        System.out.println("---before sayHelloAround execute---");
        String  retVal = (String) pjp.proceed();
        System.out.println("---after sayHelloAround execute---");        return retVal+ " suffix";
    }
}
代码(3)定义了一个切点(pointcut),这里是对满足* *..*BoImpl表达式的类里面的方法名称为 sayHello 的方法进行拦截。

代码(4)定义一个切面,一个切面中可以定义多个拦击器,其中(4.1)定义了一个前置拦截器,这个拦截器对满足代码(3)中定义的切点的连接点(方法)进行拦截,并使用 MyAspect 中定义的通知方法 beforeAdvice 进行功能增强。其中(4.2)定义了一个后置拦截器(finally),对满足 execution(* *..*BoImpl.sayHello(..)) 条件的连接点方法使用 MyAspect 中的通知方法 afterAdvice 进行功能增强。

其中(4.3)定义了一个后置拦截器,对满足 execution(* *..*BoImpl.sayHelloAfterReturn(..)) 条件的连接点方法使用 MyAspect 中的通知方法 afterReturningAdvice 进行功能增强,这个后置连接器与(4.2)不同在于如果被拦截方法抛出了异常,则这个拦截器不会被执行,而(4.2)的拦截器一直会被执行。

其中(4.4)定义了一个当被拦截方法抛出异常后对异常进行拦截的拦截器,具体拦截哪些方法由 execution(* *..*BoImpl.sayHelloThrowExecption(..)) 来决定,具体的通知方法是 MyAspect 中的 afterThrowingAdvice 方法。其中(4.5)对满足 execution(* *..*BoImpl.sayHelloAround(..)) 条件的连接点方法使用 MyAspect 中的通知方法 aroundAdvice 进行增强。

代码(5)创建一个方法拦截器,它是一个通知,代码如下:
class TracingInterceptor implements MethodInterceptor {    public Object invoke(MethodInvocation i) throws Throwable {
        System.out
                .println("---method " + i.getMethod() + " is called on " + i.getThis() + " with args " + i.getArguments()+"---");
        Object ret = i.proceed();
        System.out.println("---method " + i.getMethod() + " returns " + ret+"---");        return ret;
    }
}
代码(6)创建了一个新的 aop:config 标签,内部首先创建了一个切点,然后创建了一个 advisor(一个小型切面),它对应的通知方法是 tracingInterceptor,对应的切点是 pointcutForadVisor。

需要注意的是为了能够使用 AOP 命名空间下的 aop 标签,您需要在 XML 引入下面的 spring-aop schema:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><!-- <bean/> definitions here --></beans>
把上面配置收集起来,放入到 beanaop.xml 配置文件后,写下下面代码,就可以进行测试:
public class TestAOP {    public static final String xmlpath = "beanaop.xml";    public static void main(String[] args) {

        ClassPathXmlApplicationContext cpxa = new ClassPathXmlApplicationContext(xmlpath);
        HelloServiceBo serviceBo = cpxa.getBean("helloService",HelloServiceBo.class);
        serviceBo.sayHello(" I love you");

        String result = serviceBo.sayHelloAround("I love you");
        System.out.println(result);

        result = serviceBo.sayHelloAfterReturn("I love you");
        System.out.println(result);

        serviceBo.sayHelloAdvisor("I love you");

        serviceBo.sayHelloThrowExecption();

    }
}

aop:config 标签的解析:
既然本文讲解基于 XML 配置的 AOP 的原理,那么我们就先从解析 XML 里面配置的 aop:config 讲起。首先看看 Spring 框架的 ConfigBeanDefinitionParser 类是如何解析aop:config 标签,主要时序图如下:
在这里插入图片描述
代码(3)注册了一个 AspectJAwareAdvisorAutoProxyCreator 类到 Spring IOC 容器,该类的作用是自动创建代理类,这个后面会讲。这里需要注意的是在 registerAspectJAutoProxyCreatorIfNecessary的useClassProxyingIfNecessary 方法里面解析了 aop:config 标签里面的 proxy-target-class 和 expose-proxy 属性值,代码如下:

 private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {        
 //解析proxy-target-class属性
        if (sourceElement != null) {            
        boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));  
        //设置proxy-target-class属性值到AspectJAwareAdvisorAutoProxyCreator里面
            if (proxyTargetClass) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }            //解析expose-proxy属性值
            boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));        
            //设置expose-proxy属性属性值到AspectJAwareAdvisorAutoProxyCreator
            if (exposeProxy) {
    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
   

针对 proxy-target-class 属性(默认 false),如果设置为 true,则会强制使用 CGLIB 生成代理类;对于 expose-proxy 属性(默认 false)如果设置为 true,则对一个类里面的嵌套方法调用的方法也进行代理,具体说是如果一个 ServiceImpl 类里面有 a 和 b 方法,如果 a 里面调用了 b:
public void a(){ this.b();
}
那么默认情况下在对方法 a 进行功能增强时候,里面的 b 方法是不会被增强的,如果也需要对 b 方法进行增强则可以设置 expose-proxy 为 true,并且在调用 b 方法时候使用下面方式:
public void a(){
((ServiceBo)AopContext.currentProxy()).b();
}
需要注意的是 AopContext.currentProxy() 返回的是代理后的类,如果使用 JDK 代理则这里类型转换时候必须要用接口类,如果是 CGLIB 代理则这里类型转换为实现类 ServiceImpl。
代码(4)是对 aop:config 标签里面的 aop:pointcut 元素进行解析,每个 pointcut 元素会创建一个 AspectJExpressionPointcut 的 bean 定义,并注册到Spring IOC,parsePointcut 的主干代码如下:

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {    //获取aop:pointcut标签的id属性值
    String id = pointcutElement.getAttribute(ID);    //获取aop:pointcut标签的expression属性值
    String expression = pointcutElement.getAttribute(EXPRESSION);

    AbstractBeanDefinition pointcutDefinition = null;    try {        
    //创建AspectJExpressionPointcut的bean定义,并设置属性
        pointcutDefinition = createPointcutDefinition(expression);
        pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));        
        //如果aop:pointcut标签的id属性值不为空,则id作为该bean在Spring容器里面的bean名字,并注册该bean到Spring容器。
        String pointcutBeanName = id;       
         if (StringUtils.hasText(pointcutBeanName)) {
            parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
        }        else {
        //否者系统自动生成一个名字,并注入该bean到Spring容器
            pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
        }
        ...
    }
    ...    return pointcutDefinition;
}
注: AspectJExpressionPointcut 的成员变量 expression 保存了 aop:pointcut 标签的 expression 属性值,这个在自动代理时候会用到。

代码(5)是对 aop:config 标签里面的 aop:advisor 元素进行解析,每个 advisor 元素会创建一个 DefaultBeanFactoryPointcutAdvisor 的 bean 定义,并注册到 Spring IOC,parseAdvisor代码如下:
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {   
 //创建DefaultBeanFactoryPointcutAdvisor的定义,并设置对advice的引用
    AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);    String id = advisorElement.getAttribute(ID);    try {
        ...    
        //注册DefaultBeanFactoryPointcutAdvisor到Spring容器
        String advisorBeanName = id;        
        if (StringUtils.hasText(advisorBeanName)) {
            parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
        }        else {
            advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
        }        //解析aop:advisor标签中引用的切点,并设置到DefaultBeanFactoryPointcutAdvisor的定义
        Object pointcut = parsePointcutProperty(advisorElement, parserContext);       
         if (pointcut instanceof String) {
                advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
                ...
        }
        ...
    }
    ...
}

注:每个 DefaultBeanFactoryPointcutAdvisor 对象里面通过成员变量 adviceBeanName 保存引用通知在 BeanFactory 里面的 Bean 名称,通过成员变量 pointcut 保存切点。
DefaultBeanFactoryPointcutAdvisor 继承自 Advisor 接口,这里需要注意的是 adviceBeanName 保存的只是通知的字符串名称,那么如何获取真正的通知对象呢,其实 DefaultBeanFactoryPointcutAdvisor 实现了 BeanFactoryAware,其内部保证着 BeanFactory 的引用,既然有了 BeanFactory,那么就可以根据 Bean 名称拿到想要的 Bean,这个可以参考 Chat:Spring 框架常用扩展接口揭秘。
代码(6)是对 aop:config 标签里面的 aop:aspect 元素进行解析,会解析 aop:aspect 元素内的子元素,每个子元素会对应创建一个 AspectJPointcutAdvisor 的 bean 定义,并注册到 Spring IOC,parseAspect的代码如下:

private void parseAspect(Element aspectElement, ParserContext parserContext) {        
//获取 aop:aspect 元素的id属性
        String aspectId = aspectElement.getAttribute(ID);        
        //获取 aop:aspect 元素的ref属性
        String aspectName = aspectElement.getAttribute(REF);        
        try {            
        this.parseState.push(new AspectEntry(aspectId, aspectName));
            List<BeanDefinition> beanDefinitions = new ArrayList<>();
            List<BeanReference> beanReferences = new ArrayList<>();

            ...            //循环解析```aop:aspect```元素内所有通知
            NodeList nodeList = aspectElement.getChildNodes();
            boolean adviceFoundAlready = false;            
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);                
                //判断是否为通知节点,比如前置,后者通知等
                if (isAdviceNode(node, parserContext)) {                    
                if (!adviceFoundAlready) {
                        adviceFoundAlready = true;
                        ...
                        beanReferences.add(new RuntimeBeanReference(aspectName));
                    }                    
                    //根据通知类型的不同,创建不同的通知对象,最后封装为AspectJPointcutAdvisor的bean定义,并注册到Spring容器
                    AbstractBeanDefinition advisorDefinition = parseAdvice(
                            aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                    beanDefinitions.add(advisorDefinition);
                }
            }            //循环解析```aop:aspect```元素内所有切点,并注册到Spring容器
            List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);            
            for (Element pointcutElement : pointcuts) {
                parsePointcut(pointcutElement, parserContext);
            }
            ...
        }        finally {
            ...
        }
}

需要注意的是 parseAdvice 函数内部会根据通知类型的不同创建不同的 advice 对象,对应 before 通知会创建通知对象为 AspectJMethodBeforeAdvice,对应 after 通知创建的通知对象为 AspectJAfterAdvice,对应 after-returning 通知创建的通知对象为 AspectJAfterReturningAdvice,对应 after-throwing 通知创建的通知对象为 AspectJAfterThrowingAdvice,对应 around 通知创建的通知对象为 AspectJAroundAdvice,一个共同点是这些通知对象都实现了 MethodInterceptor 接口。
注:每个 AspectJPointcutAdvisor 对象里面通过 advice 维护着一个通知,通过 pointcut 维护这么一个切点,AspectJPointcutAdvisor 继承自 Advisor 接口。
至此 Spring 框架把 aop:config 标签里面的配置全部转换为了 Bean 定义的形式并注入到 Spring 容器了,需要注意的是对应一个 Spring 应用程序上下文的 XML 配置里面可以配置多个 aop:config 标签,每次解析一个 aop:config 标签的时候都会重新走这个流程。

代理类的生成

(1)代理类生成的主干流程
上节在解析 aop:config 标签时候注入了一个 AspectJAwareAdvisorAutoProxyCreator 类到 Spring 容器,其实这个类就是实现动态生成代理类的,AspectJAwareAdvisorAutoProxyCreator 实现了 BeanPostProcessor 接口(这个接口是 Spring 框架在 bean 初始化前后做事情的扩展接口,具体可以参考:
http://gitbook.cn/gitchat/activity/5a84589a1f42d45a333f2a8e),所以会有 Object postProcessAfterInitialization(@Nullable Object bean, String beanName) 方法,下面看下这个方法代码执行时序图:
在这里插入图片描述

代码(3)在 Spring 容器中查找可以对当前 bean 进行增强的通知 bean,内部首先执行代码(4)在 Spring 容器查找所有实现了 Advisor 接口的 Bean,也就是上节讲解的从 aop:config 标签里面解析的所有 DefaultBeanFactoryPointcutAdvisor 和 AspectJPointcutAdvisor 的实例都会被找到。

代码(5)则看找到的 Advisor 里面哪些可以应用到当前 bean 上,这个是通过切点表达式来匹配的。代码(6)则对匹配的 Advisor 进行排序,根据每个 Advisor 对象的 getOrder 方法的值进行排序。
代码(13)这里根据一些条件决定是使用 JDK 动态代理,还是使用 CGLIB 进行代理,属于设计模式里面的策略模式。
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {    
//如果optimize =true或者proxyTargetClass=ture 或者没有指定代理接口,则使用CGLIB进行代理
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();        
        //如果目标类为接口或者目标类是使用JDK动态代理生成的类,则是要使用JDK对其进行代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {            
        return new JdkDynamicAopProxy(config);
        }        //否者使用CGLIB进行代理
        return new ObjenesisCglibAopProxy(config);
    }    //使用JDK进行代理
    else {        return new JdkDynamicAopProxy(config);
    }
    对于 proxyTargetClass 前面已经讲过了可以通过在 aop:config 这个标签里面配置,那么 optimize 是在哪里配置的呢?其实除了本文讲的基于标签的 AOP 模式,Spring 还提供了比如下面的方式进行动态代理:
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*impl"></property> 
         <!-- 只为后缀为"impl"的bean生产代理 -->
        <property name="interceptorNames" value="myAdvisor"></property>   
        <!--自定义方法拦击器-->
        <property name="optimize" value="true"></property>  
  </bean>
  BeanNameAutoProxyCreator 可以针对指定规则的 beanName 的 bean 使用 interceptorNames 进行增强,由于这时候不能确定匹配的 bean 是否有接口,所以这里 optimize 设置为 true,然后在创建代理工厂时候具体看被代理的类是否是接口决定是使用 JDK 代理还是 CGLIB 代理。
代码(14)则是具体调用 JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 的 getProxy 方法获取代理类,下面两节具体介绍如何生成。

(2)JDK 动态代理生成代理对象

首先看下 JdkDynamicAopProxy 的 getProxy 方法:
public Object getProxy(@Nullable ClassLoader classLoader) {        
		if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);       
         return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    由于 JdkDynamicAopProxy 类实现了 InvocationHandler 接口,所以这里使用 Proxy.newProxyInstance 创建代理对象时候第三个参数传递的为 this。下面看下 JdkDynamicAopProxy 的 invoke 方法:
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ...        try {
            ...            //获取可以运用到该方法上的拦截器列表
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);            //创建一个invocation方法,这个里面是一个interceptor链,是一个责任链模式
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);            //使用拦截器链处理这个连接点方法
            retVal = invocation.proceed();
            ...            return retVal;
        }        finally {
            ...
        }
}

下面看下上面小节“AOP 简单使用”例子中调用 serviceBo.sayHello 时候的时序图从而加深理解:
“AOP 简单使用”这一小节例子中我们在 sayHello 方法执行前加了一个前置拦截器,在 sayHello 方法执行后加了个后置拦截器;
当执行 serviceBo.sayHello 时候实际上执行的代理类的 sayHello 方法,所以会被 JdkDynamicAopProxy 的 invoke 方法拦截,invoke 方法内首先调用 getInterceptorsAndDynamicInterceptionAdvice 方法获取配置到 sayHello 方法上的拦截器列表,然后创建一个 ReflectiveMethodInvocation 对象(内部是一个基于数数组的责任链),然后调用该对象的 procced 方法激活拦截器链对 sayHello 方法进行增强,这里是首先调用了前置连接器对 sayHello 进行增强,然后调用实际业务方法 sayHello,最后调用了后置拦截器对 sayHello 进行增强。

(3)CGLIB 动态代理生成代理对象:

首先看下 ObjenesisCglibAopProxy 的 getProxy 方法:

public Object getProxy(@Nullable ClassLoader classLoader) {    try {
        Class<?> rootClass = this.advised.getTargetClass();        //创建CGLIB Enhancer
        Enhancer enhancer = createEnhancer();
        ...
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        ...        //获取callback,主要是DynamicAdvisedInterceptor
        Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
        }        //设置callback
        enhancer.setCallbackTypes(types);        //产生代理类并创建一个代理实例
        return createProxyClassAndInstance(enhancer, callbacks);
    }    catch (CodeGenerationException | IllegalArgumentException ex) {
        ...
    }
    ...
}

下面看下拦截器 DynamicAdvisedInterceptor 的 intercept 方法,代码如下

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    ...
    TargetSource targetSource = this.advised.getTargetSource();    try {
        ...        //获取可以运用到该方法上的拦截器列表
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);        Object retVal;        
        //如果拦截器列表为空,则直接反射调用业务方法
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }        else {            
        //创建一个方法invocation,其实这个是个拦截器链,这里调用proceed激活拦截器链对当前方法进行功能增强
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }        //处理返回值
        retVal = processReturnType(proxy, target, method, retVal);        return retVal;
    }    finally {
        ...
    }
}

下面看“AOP 简单使用”这一节例子中调用 serviceBo.sayHello 时候的调用链路时序图从而加深理解:在这里插入图片描述
由于默认使用的 JDK 动态代理,要使用 CGLIB 进行代理,需要在 aop:config 标签添加属性如下:
<aop:config proxy-target-class=“true”>
执行流程类似于 JDK 动态代理,这里不再累述。
Spring 框架中如何基于 AOP 实现的事务管理:

事务的简单配置
XML 使用标签配置事务,一般是按照下面方式进行配置:

<aop:config>
  <!--(1) -->
  <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>
  <!--(2) -->
  <aop:advisor 
      pointcut-ref="businessService"
      advice-ref="tx-advice"/></aop:config>
  <!--(3) --><tx:advice id="tx-advice">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes></tx:advice>

如上配置(1),配置了一个 id 为 businessService 的切点用来匹配要对哪些方法进行事务增强;
配置(2)配置了一个 advisor ,使用 businessService 作为切点,tx-advice 作为通知方法;
配置(3)则使用 tx:advice 标签配置了一个通知,这个是重点,下节专门讲解。

注:这个配置作用是对满足 id 为 businessService 的切点条件的方法进行事务增强,并且设置事务传播性级别为 REQUIRED。
原理剖析
tx:advice 标签的解析
tx:advice 标签是使用 TxAdviceBeanDefinitionParser 进行解析的,下面看看解析时序图:
在这里插入图片描述

如上时序图 BeanDefinitionBuilder 是一个建造者模式,用来构造一个 bean 定义,这个 bean 定义最终会生成一个 TransactionInterceptor 的实例;
步骤(5)通过循环解析 tx:attributes 标签里面的所有 tx:method 标签,每个 tx:method 对应一个 RuleBasedTransactionAttribute 对象,其中 tx:method 标签中除了可以配置事务传播性,还可以配置事务隔离级别,超时时间,是否只读,和回滚策略。
步骤(12)把所有的 method 标签的 RuleBasedTransactionAttribute 对象存在到了一个 map 数据结构,步骤(13)设置解析的所有属性到建造者模式的对象里面,步骤(14)使用建造者对象创建一个 bean 定义,步骤(15)则注册该 bean 到 Spring 容器。
注: tx:advice 作用是创建一个 TransactionInterceptor 拦截器,内部维护事务配置信息。

事务拦截器原理简单剖析:

public Object invoke(final MethodInvocation invocation) throws Throwable {
    ...    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,            final InvocationCallback invocation) throws Throwable {        
// If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();        
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);        final PlatformTransactionManager tm = determineTransactionManager(txAttr);       
         final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);        
         if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {           
          // 标准事务,内部有getTransaction(开启事务) 和commit(提交)
          /rollback(回滚)事务被调用.
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;           
             try {                
            //这是一个环绕通知,调用proceedWithInvocation激活拦截器链里面的下一个拦击器
                retVal = invocation.proceedWithInvocation();
            }            catch (Throwable ex) {                
            // target invocation exception
                completeTransactionAfterThrowing(txInfo, ex);               
                 throw ex;
            }            finally {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);          
              return retVal;
        }

        ...
}

其中 createTransactionIfNecessary 是重要方法,其内部逻辑处理流程如下图:
在这里插入图片描述

注: Spring 事务管理通过配置一个 AOP 切面来实现,其中定义了一个切点用来决定对哪些方法进行方法拦截,定义了一个 TransactionInterceptor 通知,来对拦截到的方法进行事务增强,具体事务拦击器里面是怎么做的,读者可以结合上面的 TransactionInterceptor 方法的流程图结合源码来研究下

Spring事务切面生成原理:
Advisor这个概念是从 Spring 1.2的 AOP 支持中提出的,一个 Advisor 相当于一个小型的切面,不同的是它只有一个通知(Advice),Advisor中还包含一个pointcut(切点),切点定义了对那些方法进行拦截,而通知是具体对拦截到的方法进行增强的逻辑。
具体对 tx:advice/ 标签进行解析的是TxAdviceBeanDefinitionParser,其时序图如下:
在这里插入图片描述
首先TxAdviceBeanDefinitionParser有getBeanClass方法代码代码:
在这里插入图片描述
这说明该标签解析后生成的是TransactionInterceptor对象的bean定义。
其中时序图中步骤(2)是设置配置demo的XML配置文件里面创建的事务管理器到TransactionInterceptor对象
在这里插入图片描述

时序图(4)~(10)则解析 tx:advice/标签中事务属性值设置到TransactionInterceptor对象里面属性里面。
注:也就是 tx:advice/标签的作用是生成一个TransactionInterceptor拦击器对象,并设置该对象的一些事务属性,然后该对象将作为事务切面的通知方法。
然后 aop:config标签作用是创建一个DefaultBeanFactoryPointcutAdvisor(其实现了Advisor接口)对象作为作一个Advisor,前面说了一个Advisor就是一个小型的切面,所以其中定义了切点和通知。该标签是ConfigBeanDefinitionParser类进行解析的,其时序图如
在这里插入图片描述
时序图中步骤(2)创建了一个DefaultBeanFactoryPointcutAdvisor对象的bean定义,步骤(3)(4)则是设置上面创建的通知对象(TransactionInterceptor)到该Advisor
时序图中步骤(8)则是解析标签中的切点表达式,然后设置到DefaultBeanFactoryPointcutAdvisor对象的bean定义。
时序图步骤(4)注册了一个AspectJAwareAdvisorAutoProxyCreator到Spring容器,作用就是对满足pointcut表达式的类的方法进行代理,并且使用advice进行拦截处理,而advice就是事务拦截器。
由于AspectJAwareAdvisorAutoProxyCreator类实现了BeanPostProcessor接口,所以具有postProcessAfterInitialization方法,而对符合切点的方法进行代理就是在该方法内的wrapIfNecessary方法:

在这里插入图片描述
其中8.1查找所有可以对当前bean进行增强的切面,其中有一个条件就是看那些bean实现了Advisor接口,而 aop:config标签作用是创建一个DefaultBeanFactoryPointcutAdvisor,并且其实现了Advisor接口,所以这里会使用DefaultBeanFactoryPointcutAdvisor切面,然后会看当前bean的方法是否满足切面的切点表达式,具体是AopUtils的canApply方法进行判断
在这里插入图片描述
如果满足则执行8.2对方法进行代理,这里会对TestTransactionProgagationUserImpl、TestTransactionProgagationCourseImpl、UserManagerBoImpl类的所有方法进行事务代理。
注:Spring框架中一个 Advisor 相当于一个小型的切面, tx:advice/定义了这个切面的通知方法,而 aop:config具体定义了一个Advisor切面,并且内部定义了一个切点,并且引入了 tx:advice/定义的通知方法。

引申Q:aop的底层实现,动态代理是如何动态,假如有100个对象,如何动态的为这100个对象代理,动态代理的几种实现方式及优缺点
https://www.jianshu.com/p/3137b33efc3f
引申Q:动态代理和普通代理的区别

代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
而AOP,是通过动态代理实现的。
一、简单来说:
  JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
二、Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
  (3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)
三、CGlib比JDK快?
  (1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
  (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。

11、说说springboot启动机制

https://blog.csdn.net/hengyunabc/article/details/50120001

引申Q:spring cloud相关组件

https://www.cnblogs.com/xiaojunbo/p/7090742.html

引申Q:Spring Cloud 有了解多少?

springCloud是基于SpringBoot的一整套实现微服务的框架。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,
跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便。

12、介绍spring任意一个模块的实现(如:ioc如何实现)OC实现原理,发射?(说说Spring的IOC容器初始化流程?)

答:http://blog.csdn.net/william_hangshao/article/details/70113349

13、SpringMVC实现原理(springmvc的流程,spring mvc实现原理(一共几步,步骤分别是什么)):

在这里插入图片描述

1、客户端发请求,被spring的前段控制器DispatcherServlet捕获
2、DispatcherServlet请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
4、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5、执行用户自行编写的业务逻辑
6、Handler执行完成后,向HandlerAdapter 返回一个ModelAndView对象;
7、HandlerAdapter把ModelAdnView返回给DispatcherServlet
8、DispatcherServlet把ModelAndView传给ViewResolver,ViewResolver结合model和view来渲染视图
9、ViewResolver返回给DispatcherServlet view对象
10、DispatcherServlet,通过view对view对象渲染成视图,
11、view返回给DispatcherServlet
12、DispatcherServlet相应给用户

源码分析:

首先源码入口,因为springMVC是基于servlet的,所以我们此处可以通过web.xml查看入口代码
在这里插入图片描述
找到DispatcherServlet之后,查看一下DispatcherServlet的结构
在这里插入图片描述
我们可以看到其实就是一个servlet,但是封装了进了spring的容器中,因为本身是servlet,那么我们可以找一个doGet方法和doPost方法,结果在DispatcherServlet中没找到,在父类FrameworkServlet中找到了在这里插入图片描述
在这里插入图片描述
从doService中再次跳转到doDispatch(request, response);
在这里插入图片描述
在这里插入图片描述

14、SpringIOC

IOC的组成体系结构
1、资源定位(配置文件定位)
2、载入(读取配置文件)
3、注册(把加载以后的配置文件解释成BeanDefinition)
依赖注入
1、读取BeanDefinition中的信息,获取其依赖关系,解释成BeanWapper
2、实例化(代理对象)createBeanInstance()方法,根据情况可能用JDK的代理,也可能用Cglib的代理,依赖关系,List,Array,Map,
3、注入设值,populateBean(),注入方法,做类型转换factoryBeanObjectCache,这个ConcurrentHashMap,就是真正的spring的IOC容器lazy-init ,如果使用了延时,,依赖注入这个动作发生在调用getBean方法的时候,spring-bean 定义的是接口规范,里面大部分都是接口spring-context工厂的实现、DI的实现 spring-core是最顶层的,所有的项目都要依赖

15、SpringAOP

Aspects,切面spring-aop 是spring-aspects的上层建筑 targetClass MethodInvokerspring-aspects从IOC中取得代理以后的对象,对每个方法进行重写,
加入一些切面调用所需要的东西do开头的方法都是具体干活的方法:doCreateBean AOP通知触发器,Trggier 专门监听的定时器
AdvisorAdapterRegistrationManager,在bean对象初始化后注册通知适配器,AdvisorAdapterRegistrationManager是BeanPostProcessor的一个实现类,其主要作用为容器中管理的Bean注册一个面向切面编程的通知适配器,以便在Spring容器为所管理的Bean进行面向切面编程时提供方便
1、Autowiring,实现原理,:SpringIOC容器的依赖自动装配功能,不需要对Bean属性的依赖关系做显式声明,只需要在配置好autowiring属性,IOC容器会自动使用反射查到属性的类型和名称,然后给予属性的类型或者名称来自懂匹配容器中管理的Bean,从而自动的完成依赖注入通过对autowiring自动装配特性的理解,我们知道容器对Bean的自动装配发生在容器对Bean依赖注入的过程中,在前面对Spring IOC容器的依赖注入过程源码分析中,我们已经知道了容器对Bean实例对象的属性注入的处理发生在AbstractAutoWireCapableBeanFactory类中的populateBean方法中,
我们通过程序流程分析autowiring的实现原理:
(1)、AbstractAutoWireCapableBeanFactory对Bean实例进行属性依赖注入 应用第一次通过getBean方法(配置了lazy-init预实例化属性的除外),向IOC容器索取Bean时,容器创建Bean实例对象,并且对Bean实例对象进行属性依赖注入,AbstractAutoWireCapableBeanFactory的poopulateBean方法就是实现Bean属性依赖注入功能。
(2)、springIOC容器根据Bean名称活着类型进行autowiring自动依赖注入,通过属性名进行自动依赖注入的相对比通过属性类型进行自动依赖注入要稍微简单一些,但是,真正实现属性注入的是DefaultSingletonBeanRegistry类的registerDependentBean方法(3)、DefaultSingletonBeanRegistry类的registerDependentBean方法对属性注入springAOP,如果执行规则呢一个切面就代表着N个Bean的一个集合,这N个Bean,它们都拥有共同点,所以它们组成了一个切面,
举例:事物管理的时候,就用到了切面的定义,提问:
1、spring的AOP默认使用的是jdk的动态代理还是CGlib?
IOC判断,如果被代理的类实现了一个接口,那么默认用jdk动态代理,如果被代理的对象没有实现任何接口,那么就默认用cglib
1、加载配置信息,解析成AopConfig
2、交给AopProxyFactory,调用一个CreateAopProxy方法;
3、JdkDynamicAopProxy调用,advisedSupport的getInterceptorsAndDynamicInterceptionAdvice方法,得到方法拦截器链,并保存到一个List容器中,MethodInterceptor容器是List
4、递归执行拦截器方法proceed()方法。最终就是有一个ACiesor来调用切面中的方法

Spring-JDBC:

封装了JDBC操作的一个框架,必须依赖Spring才可以使用,SpringJDBC本身就是给予末班模式来开发的,JDBCTemplateJDBC二次开发,来封装一个NOSQL框架
1、加载驱动类(给予Mysql )
2、获取链接(被封装到dataSource里面去了)
3、创建语句集(预处理语句集合标准语句集)
4、执行语句集(执行事物操作)
5、获取结果集(如果是增删改,拿到一个int值,影响行数,如果查询,就会拿到一个ResultSet)
MyBatis是一个半自动的ORM框架,hibernate是一个全自动的ORM框架,springJDBC是一个手动的ORM框架springJDBC采用的是Template设计模式,只定义了一个RowMapper的接口,mapping方法,这个方法是没有实现的

Mybatis专题

mybatis如何映射表结构

ibatis跟hibernate的区别

答:http://blog.csdn.net/abbydream/article/details/62236305

ibatis是怎么实现映射的,它的映射原理是什么

答:通过 SQL Map 将 Java 对象映射成 SQL 语句和将结果集再转化成 Java 对象,与其他 ORM 框架相比,既解决了 Java 对象与输入参数和结果集的映射,又能够让用户方便的手写使用 SQL 语句

微服务springcloud

8、微服务

soa和微服务的区别?

首先SOA和微服务架构一个层面的东西,而对于ESB和微服务网关是一个层面的东西,一个谈到是架构风格和方法,一个谈的是实现工具或组件。
1.SOA(Service Oriented Architecture)“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
2.微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想 + 80%的领域建模思想
1.ESB(企业服务总线),简单 来说 ESB 就是一根管道,用来连接各个服务节点。为了集 成不同系统,不同协议的服务,ESB 做了消息的转化解释和路由工作,让不同的服务互联互通;
在这里插入图片描述
2.API网关:API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
在这里插入图片描述在这里插入图片描述
SOA架构特点:
系统集成:站在系统的角度,解决企业系统间的通信问 题,把原先散乱、无规划的系统间的网状结构,梳理成 规整、可治理的系统间星形结构,这一步往往需要引入 一些产品,比如 ESB、以及技术规范、服务管理规范; 这一步解决的核心问题是【有序】
系统的服务化:站在功能的角度,把业务逻辑抽象成 可复用、可组装的服务,通过服务的编排实现业务的 快速再生,目的:把原先固有的业务功能转变为通用 的业务服务,实现业务逻辑的快速复用;这一步解决 的核心问题是【复用】
业务的服务化:站在企业的角度,把企业职能抽象成 可复用、可组装的服务;把原先职能化的企业架构转变为服务化的企业架构,进一步提升企业的对外服务能力;“前面两步都是从技术层面来解决系统调用、系统功能复用的问题”。第三步,则是以业务驱动把一个业务单元封装成一项服务。这一步解决的核心问题是【高效】
微服务架构特点:
1.通过服务实现组件化
开发者不再需要协调其它服务部署对本服务的影响。
2.按业务能力来划分服务和开发团队
开发者可以自由选择开发技术,提供 API 服务
3.去中心化
每个微服务有自己私有的数据库持久化业务数据
每个微服务只能访问自己的数据库,而不能访问其它服务的数据库
某些业务场景下,需要在一个事务中更新多个数据库。这种情况也不能直接访问其它微服务的数据库,而是通过对于微服务进行操作。
数据的去中心化,进一步降低了微服务之间的耦合度,不同服务可以采用不同的数据库技术(SQL、NoSQL等)。在复杂的业务场景下,如果包含多个微服务,通常在客户端或者中间层(网关)处理。
4.基础设施自动化(devops、自动化部署)
的Java EE部署架构,通过展现层打包WARs,业务层划分到JARs最后部署为EAR一个大包,而微服务则打开了这个黑盒子,把应用拆分成为一个一个的单个服务,应用Docker技术,不依赖任何服务器和数据模型,是一个全栈应用,可以通过自动化方式独立部署,每个服务运行在自己的进程中,通过轻量的通讯机制联系,经常是基于HTTP资源API,这些服务基于业务能力构建,能实现集中化管理(因为服务太多啦,不集中管理就无法DevOps啦)。在这里插入图片描述
分包
服务接口、请求服务模型、异常信息都放在api里面,符合重用发布等价原则,共同重用原则
api里面放入spring 的引用配置。 也可以放在模块的包目录下。
粒度
尽可能把接口设置成粗粒度,每个服务方法代表一个独立的功能,而不是某个功能的步骤。否则就会涉及到分布式事务
服务接口建议以业务场景为单位划分。并对相近业务做抽象,防止接口暴增
不建议使用过于抽象的通用接口 T T<泛型>,接口没有明确的语义,带来后期的维护
版本
每个接口都应该定义版本,为后续的兼容性提供前瞻性的考虑 version (maven -snapshot)
建议使用两位版本号,因为第三位版本号表示的兼容性升级,只有不兼容时才需要变更服务版本
当接口做到不兼容升级的时候,先升级一半或者一台提供者为新版本,再将消费全部升级新版本,然后再将剩下的一半提供者升级新版本
预发布环境
推荐用法
在provider端尽可能配置consumer端的属性
比如timeout、retires、线程池大小、LoadBalance
配置管理员信息
application上面配置的owner 、 owner建议配置2个人以上。因为owner都能够在监控中心看到
配置dubbo缓存文件
注册中心的列表
服务提供者列表

spring cloud的服务注册与发现是怎么设计的?
https://www.jianshu.com/p/2fa691d4a00a
微服务你的理解?以及常用的微服务方案dubbo、spring cloud的比较?
https://www.cnblogs.com/aspirant/p/9089146.html
微服务架构是什么,它的优缺点?
https://www.xianjichina.com/news/details_67980.html
划分的粒度,微服务的高可用怎么保证的?

优雅的服务降级
通过微服务架构可以实现失效隔离,也就是说,在组件发生故障时可以实现优雅的服务降级。例如,在图片共享应用发生故障时,用户可能无法上传新的图片,但他们仍然可以浏览、编辑和分享已有的图片。在这里插入图片描述
在大多数情况下,实现这种优雅的服务降级是很困难的,因为在分布式系统里,应用之间相互依赖,为了应对临时的故障,需要应用到一些失效备援方案(稍后会提到)。在这里插入图片描述
变更管理Google 的网站可靠性团队发现,70% 的故障都是由系统变更引起的。更改服务、部署新代码、变更配置,这些都有可能引入新的缺陷或造成服务失效。在微服务架构里,服务之间是相互依赖的。所以我们要最小化出现故障的几率,限制故障所造成的负面影响。我们需要良好的变更管理策略和自动回滚机制。例如,在部署新代码时,或者在对配置做出变更时,要先在一小部分服务实例上进行,然后监控它们,一旦发现关键性度量指标出现异常,马上自动回滚。
在这里插入图片描述
另一个解决方案就是运行两套生产环境。在部署的时候只部署到其中一个生产环境,只有在确认这个环境没问题了之后才能将负载均衡器指向这个环境。这种部署方式被称为蓝绿部署或者红黑部署。回退代码并不是件坏事。你总不可能一边把有问题的代码留在生产环境里,一边想着到底发生了什么问题。所以,在必要的时候回退代码,越快越好。

微服务涉及到的技术以及需要注意的问题有哪些?

微服务涉及到的技术以及需要注意的问题有哪些?
1.每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求。

2.微服务能够被小团队单独开发,这个小团队是2到5人的开发人员组成。

3.微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。

4.微服务能使用不同的语言开发。

5.微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
3.1微服务架构的缺点
1.微服务架构可能带来过多的操作。

2.需要DevOps技巧 (http://en.wikipedia.org/wiki/DevOps)。

3.可能双倍的努力。

4.分布式系统可能复杂难以管理。

5.因为分布部署跟踪问题难。

6.当服务数量增加,管理复杂性增加。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小诚信驿站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值