Spring是一个轻量级框架,核心内容是IOC和AOP。它目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。主要包括以下七个模块(简单了解一下就行):●SpringContext:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);●SpringCore:核心类库,所有功能都依赖于该类库,提供IOC和DI服务;●SpringAOP:AOP服务;●SpringWeb:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器;●SpringMVC:提供面向Web应用的Model-View-Controller,即MVC实现。●SpringDAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务;●SpringORM:对现有的ORM框架的支持;
(1)Spring属于低侵入式设计,代码的污染极低;(2)Spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。(4)Spring对于主流的应用框架提供了集成支持。
IOC,英文InversionofControl的缩写,即“控制反转”,是一种设计思想。所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到Sqring容器中去了(ApplicationContext.xml)而程序本身不在维护了。什么意思呢?在javase中一个对象中使用另外的对象,就必须主动通过new指令去创建依赖对象,使用完后还需要销毁(比如Connection等),对象始终会和其他接口或类耦合起来。也就是说创建对象的时机和主动权都是由自己把控的,而IOC则是由专门的容器来帮忙创建对象,将所有的类都通过Spring容器中注册,当需要某个对象时,不再需要自己主动去new了,只需告诉Spring容器,然后让Spring容器主动给你。也就是说,对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被Spring容器控制,由Spring容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。
IOC的一个重点就是在程序运行时,动态的向某个对象提供它所需要的其他对象,这一点是通过DI(DependencyInjection,依赖注入)来实现的,即应用程序在运行时依赖IoC容器来动态注入对象所需要的外部依赖。而Spring的DI具体就是通过反射实现注入的,反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性。
IOC是解决耦合问题的一种设计思想,DI是IOC的一种实现。IOC主要的实现方式有两种:依赖查找(DL),依赖注入(DI)。
AOP,称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。SpringAOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:JDK动态代理只提供接口的代理,不支持类的代理,因此要求被代理类实现接口。CGLIB是通过继承的方式做的动态代理。
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以SpringAOP为代表。静态代理与动态代理区别在于生成AOP代理对象的时机不同(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。(2)SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
①JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时,InvocationHandler通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;②如果被代理类没有实现接口,那么SpringAOP会选择使用CGLIB来动态代理目标类。CGLIB(CodeGenerationLibrary),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
1.前置通知(BeforeAdvice):在连接点(Joinpoint)之前执行的通知。2.后置通知(AfterAdvice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。3.环绕通知(AroundAdvice):包围一个连接点的通知,这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。4.返回后通知(AfterReturningAdvice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)5.抛出异常后通知(AfterThrowingadvice):在方法抛出异常退出时执行的通知
●set()方法注入;●构造器注入:①通过index设置参数的位置;②通过type设置参数类型;●接口注入;(用的比较少)注意:通过Spring创建的对象默认是单例的,如果需要创建多实例对象可以在标签后面添加一个属性scope="prototype":
SpringBean的生命周期只有四个阶段:实例化Instantiation-->属性赋值Populate-->初始化Initialization-->销毁Destruction
当bean在Spring容器中组合在一起时,它被称为装配或bean装配。Spring容器需要知道需要什么bean以及容器应该如何使用依赖注入来将bean绑定在一起,同时装配bean。
Spring容器能够自动装配bean。也就是说,可以通过检查BeanFactory的内容让Spring自动解析bean的协作者。自动装配的不同模式:no-这是默认设置,表示没有自动装配。应使用显式bean引用进行装配。byName-它根据bean的名称注入对象依赖项。它匹配并装配其属性与XML文件中由相同名称定义的bean。byType-它根据类型注入对象依赖项。如果属性的类型与XML文件中的一个bean名称匹配,则匹配并装配属性。构造函数-它通过调用类的构造函数来注入依赖项。它有大量的参数。autodetect-首先容器尝试通过构造函数使用autowire装配,如果不能,则尝试通过byType自动装配。
(1)@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。(2)@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
1.BeanFactory是Spring里面最底层的接口,是最简单的容器,只提供了实例化对象和拿对象的功能;ApplicationContext是应用上下文,继承BeanFactory接口,它是Spring的一个更高级的容器,提供了更多的有用的功能。2.两者在装载Bean的时候,BeanFactory在启动的时候不会去实例化Bean,只有从容器中拿Bean的时候才会去实例化,因此应用启动的时候占用资源很少;而ApplicationContext在启动的时候就把所有的Bean全部实例化了。这样系统在启动的时候,可以尽早的发现系统中的配置问题,建议web应用在启动的时候就把所有的Bean都加载了,以便让费时的操作放到系统启动中完成。
类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。比如下图中A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题。
1.通过构造方法进行依赖注入时产生的循环依赖问题。该情况会报异常,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。2.通过setter方法进行依赖注入且是在多例模式下产生的循环依赖问题。该情况会报异常,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。3.通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。因此在Spring中,前两种都会报异常,只有第(3)种方式的循环依赖问题能被解决。
Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。读取顺序依次是一级缓存==>二级缓存==>三级缓存。
Spring启动过程是IOC容器的启动过程,本质是创建和初始化bean工厂(BeanFactory).BeanFactory是SpringIOC的核心,Spring使用beanFactory来实例化,配置和管理bean。对于web程序,IOC容器启动过程即是建立上下文的过程,web容器会提供一个全局的servletContext上下文环境。其启动过程主要包含三个类,ContextLoaderListener,ContextLoader和XmlWebApplicationContext。在web.xml中提供ContextLoaderListener上下文监听器,在web容器启动时,会触发容器初始化事件,ContextLoaderListener会监听到这个事件,从而触发ContextInitialized方法完成上下文初始化,这个方法中调用父类ContextLoader的方法完成上下文初始化。ContextLoader类中主要完成三件事:1)创建WebApplicationContext;2)加载对应的Spring配置文件中的bean;(refresh方法,完成bean的加载)3)将WebApplicationContext放入servletContext中。ContextLoaderListener监听器初始化完之后,开始初始化web.xml中配置的servlet,如DispatcherSevletContextLoaderListener监听器监听的是servletContext,当web容器初始化后,servletContext发生变化时,会触发相应事件。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。Spring只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过redolog和undolog实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。
Spring事务的传播机制说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。①PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。②PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。③PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘④PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。⑤PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按REQUIRED属性执行。⑥PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。⑦PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
①ISOLATION_DEFAULT:这是个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。②ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。③ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。④ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。⑤ISOLATION_SERIALIZABLE:所有事务逐个依次执行。
1.@Transactional配置的方法非public权限修饰(例如private的就别加了);2.@Transactional所在类非Spring容器管理的bean(例如一个Util就别加了,都没注入容器,你叫Spring怎么用他的AOP帮你管理事务?);3.@Transactional所在类中,注解修饰的方法被类内部方法调用(例如同一个class类中,方法A调用方法B,只在方法B加了@Transactional注解,那就失效了);4.业务代码抛出异常类型非RuntimeException,事务失效;5.业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有thrownewRuntimeExecption异常(只有该异常或者他的父异常例如Exception可以回滚);6.注解@Transactional中Propagation属性值设置错误(例如Propagation.NOT_SUPPORTED,一般不会这么设置)7.mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用。
在java中异常的基类为Throwable,Error和Exception继承Throwable。Exception中RuntimeException及其子类成为未检查异常(unchecked),其它Exception成为已检查异常(checked)。Spring的事务管理默认是针对uncheckedexception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常,若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);checked异常,checked异常必须由try-catch语句包含或者由方法throws抛出,且事务默认对checked异常不进行回滚。
单例模式应该是大家印象最深的一种设计模式了。在Spring中最明显的使用场景是在配置文件中配置注册bean对象的时候设置scope的值为singleton。
观察者模式定义的是对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。使用比较场景是在监听器中而spring中Observer模式常用的地方也是listener的实现。如ApplicationListener.
简单工厂模式:就是通过工厂根据传递进来的参数决定产生哪个对象。Spring中我们通过getBean方法获取对象的时候根据id或者name获取就是简单工厂模式了。
/***User工厂类**/publicclassUserFactory{/***必须是static方法*@return*/publicstaticUserBeangetInstance(){returnnewUserBean();}}036Spring中适配器模式体现?将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。这就是适配器模式。在Spring中在AOP实现中的Advice和interceptor之间的转换就是通过适配器模式实现的。
classMethodBeforeAdviceAdapterimplementsAdvisorAdapter,Serializable{@OverridepublicbooleansupportsAdvice(Adviceadvice){return(adviceinstanceofMethodBeforeAdvice);}@OverridepublicMethodInterceptorgetInterceptor(Advisoradvisor){MethodBeforeAdviceadvice=(MethodBeforeAdvice)advisor.getAdvice();//通知类型匹配对应的拦截器returnnewMethodBeforeAdviceInterceptor(advice);}}037Spring中装饰者模式体现?装饰者模式:装饰者模式又称为包装模式(Wrapper),作用是用来动态的为一个对象增加新的功能。装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。具体的使用在Springsession框架中的SessionRepositoryRequestWrapper使用包装模式对原生的request的功能进行增强,可以将session中的数据和分布式数据库进行同步,这样即使当前tomcat崩溃,session中的数据也不会丢失。
SpringMVC是一个基于MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
1.与Spring框架集成,因此拥有Spring的优点。2.支持RestFul风格以及支持各种请求资源的映射策略。3.SpringMVC提供强大的约定大于配置的契约式编程支持,例如前端控制器(dispatcherServlet),请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。开发人员仅需规定进行配置即可。4.可以十分灵活地实现数据验证、格式化和数据绑定机制,可以使用任意对象进行数据绑定操作。
(1)前端控制器DispatcherServlet(不需要程序员开发)作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet就减少了其它组件之间的耦合度。(2)处理器映射器HandlerMapping(不需要程序员开发)作用:根据请求的URL来查找Handler(3)处理器适配器HandlerAdapter注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。(4)处理器Handler(需要程序员开发)(5)视图解析器ViewResolver(不需要程序员开发)作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)(6)视图View(需要程序员开发jsp)View是一个接口,它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
mvc是一种思想,可以理解成一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端页面的展现与后端业务数据处理的分离。mvc设计模式的好处1.分层设计,实现了业务系统各个组件之间的解耦,有利于业务系统的可扩展性,可维护性。2.有利于系统的并行开发,提升开发效率。
请求路径上有个id的变量值,可以通过@PathVariable来获取@RequestMapping(value=“/page/{id}”,method=RequestMethod.GET)@RequestParam用来获得静态的URL请求入参spring注解时action里用到。
是单例模式,在多线程访问的时候有线程安全问题,解决方案是在控制器里面不能写可变状态量,如果需要使用这些可变状态,可以使用ThreadLocal机制解决,为每个线程单独生成一份变量副本,独立操作,互不影响。
可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。
相同点:都是基于mvc的表现层框架,都用于web项目的开发。不同点1、前端控制器不一样。SpringMVC的前端控制器是servlet:DispatcherServlet。struts2的前端控制器是filter:StrutsPreparedAndExcutorFilter。2、请求参数的接收方式不一样。SpringMVC是使用方法的形参接收请求的参数,基于方法的开发,线程安全,可以设计为单例或者多例的开发,推荐使用单例模式的开发(执行效率更高),默认就是单例开发模式。struts2是通过类的成员变量接收请求的参数,是基于类的开发,线程不安全,只能设计为多例的开发。3、Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,SpringMVC通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。4、与spring整合不一样。SpringMVC是spring框架的一部分,不需要整合。在企业项目中,SpringMVC使用更多一些。
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。过滤器:依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。拦截器:依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表达式拿到。
返回值可以有很多类型,有String,ModelAndView,但一般用String比较好。
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下:(1)加入Jackson.jar(2)在配置文件中配置json的映射(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。