(1)增加程序的灵活性,可扩展性,动态创建对象。
(2)框架必备,任何框架的封装都要用反射。(框架的灵魂)
(1)Class.forName();
(2)类名.class;
(3)对象.getClass();
(1)new 关键字。
Hello h = new Hello();
(2)使用类的反射机制:
- aaa
Class helloClass = Class.forName("com.shenmazong.Hello");
Hello h =(Hello) helloClass.newInstance();
构造器创建【无参,有参】
//获取类对象
Class helloClass = Class.forName("com.bw.Hello");
//获取构造器
Constructor constructor = helloClass.getConstructor();
Hello h =(Hello) constructor.newInstance();
(3)使用类的clone
Hello h = new Hello();
Hello h1 = (Hello) h.clone();
(4)采用序列化机制
将一个对象序列化到磁盘上,而采用反序列化可以将磁盘上的信息转化到内存中对象
IoC(Inversion of Control)翻译过来是控制翻转,是一种设计思想,将创建对象的权力交给spring容器去控制。
谁控制谁?
IoC/DI 容器控制应用程序。
控制什么?
IoC/DI 容器控制对象本身的创建、实例化;IoC/DI 容器控制对象之间的依赖关系。
为何叫反转(对应于正向)?
因为现在应用程序不能主动去获取外部资源了,而是被动等待 IoC/DI 容器给它注入它所需要的资源,所以称之为反转。
哪些方面反转了?
创建对象的方式;程序获取资源的方式。
为何需要反转?
引入 IoC/DI 容器过后,体系更为松散,而且管理更有序;类之间真正实现了松散耦合
依赖注入(Dependency Injection),组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。Java Dependency Injection设计模式允许我们删除硬编码的依赖关系,并使我们的应用程序松散耦合,可扩展和可维护。我们可以在Java中实现依赖注入,以将依赖解析从编译时移至运行时。总之一句话:给对象中的属性赋值。
什么是依赖(按名称理解、按动词理解)?
按名称理解:依赖关系;按动词理解:依赖的动作。
谁依赖于谁?
应用程序依赖于 IoC/DI 容器。
为什么需要依赖?
因为发生了反转,应用程序依赖的资源都是 IoC/DI 容器里面。
依赖什么东西?
应用程序依赖于 IoC/DI 容器,依赖 IoC/DI 容器为它注入所需要的资源(如:依赖关系)。
谁注入于谁?
IoC/DI 容器注入于应用程序。
注入什么东西?
注入应用程序需要的外部资源(如:依赖关系)。
为何要注入?
因为程序要正常运行需要这些外部资源。
依赖注入也叫DI,就是由容器来解决对象之间的依赖关系。
(1)不是同一概念,但实际上它们描述的是同一件事情,只不过是从不同角度来说的:控制反转是从 IoC/DI 容器的角度;依赖注入是从应用程序的角度。
(2)控制反转的描述:IoC/DI 容器反过来控制应用程序,控制应用程序锁所需要的外部资源(如:外部资源)。
(3)依赖注入的描述:应用程序依赖 IoC/DI 容器,依赖它注入所需要的外部资源。
(1)setter注入
(2)构造方法注入
(3)基于注解的方式注入
(1)相同点:
(2)不同点:
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
globalSession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
(1)BeanFactory:是一个工厂,IOC的顶级接口(其实是构建了一个spring上下文的环境,容器),用来管理和获取Bean对象。
(2)FactoryBean:是一个Bean生成工具,是用来获取一种类型对象的Bean,它是构造Bean实例的一种方式。用户可以通过实现该接口定制实例化 Bean 的逻辑。
从对象的创建到销毁的过程。而Spring中的一个Bean从开始到结束经历很多过程,但总体可以分为六个阶段:
spring中出现循环依赖主要有以下场景:
Spring是通过三级缓存机制来解决Bean创建的循环依赖问题的,具体如下:
(1)一级缓存,singletonObjects,存储所有已创建完毕的单例 Bean (完整的 Bean)。
(2)二级缓存,earlySingletonObjects,存储所有仅完成实例化,但还未进行属性注入和初始化的 Bean。
(3)三级缓存,singletonFactories,存储能建立这个 Bean 的一个工厂,通过工厂能获取这个 Bean,延迟化 Bean 的生成,工厂生成的 Bean 会塞入二级缓存。
(不需要背)具体流程如下:
(1)A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
(2)B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
(3)B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放到一级缓存中。
面向切面编程,一种编程思想,在不改变原有的逻辑的基础上,增加一些额外的功能。
(1)权限验证
(2)日志跟踪
(3)事务
(4)读写分离
(1)前置通知:在我们执行目标方法之前运行(@Before)
(2)后置通知:在我们目标方法运行结束之后,不管有没有异常(@After)
(3)返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
(4)异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
(5)环绕通知:非常灵活,目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,joinPoint.procced()就是执行目标方法的代码 。环绕通知可以控制返回对象(@Around)
AOP底层是通过动态代理实现,两种实现方式:
(1)jdk动态代理:如果目标对象实现了接口,spring默认会使用jdk动态代理实现。
(2)cglib动态代理:如果目标对象没有实现接口,spring默认使用cglib代理实现。
(不用背)可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)
(1)jdk动态代理只能对实现了接口的类生成代理,而不能针对类。cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。
(2)在低版本的jdk(jdk1.7之前),cglib代理的效率要比jdk动态代理的效率高。在jdk1.7,1.8及之后的版本对jdk动态代理做了优化,jdk动态代理效率要高于cglib代理。
(1)@Controller和@RestController,标识控制层;
(2)@RequestMapping地址映射的注解;
(3)@PathVariable请求URL中的模板变量映射到功能处理方法的参数上;
(4)@RequestParam请求的参数绑定到控制器方法的参数上;
(5)@RequestBody用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容(json数据)转换为java对象并绑定到Controller方法的参数上。
(5)@ResponseBody用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
(1)前端控制器 DispatcherServlet(不需要开发,由框架提供【核心】)
(2)处理器映射器 HandlerMapping (不需要开发,由框架提供)
(3)处理器适配器 HandlerAdapter (不需要开发,由框架提供)
(4)处理器 Handler (需要工程师开发),controller
(5)视图解析器 View Resolver (不需要开发,由框架提供)
(6)视图 View (需要工程师开发),页面(jsp、freemarker、thymeleaf等)
SpringMVC执行流程:
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用处理器映射器HandlerMapping。
(3)处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)并返回给DispatcherServlet。
(4)DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据 验证等操作
(5)执行处理器Handler(Controller,也叫页面控制器)。
(6)Handler执行完成返回ModelAndView
(7)HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
(8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器
(9)ViewReslover解析后返回具体View
(10)DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
(11)DispatcherServlet响应用户。
在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码 中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。
通常,依赖注入可以通过三种方式完成,即:
在 Spring Framework 中,仅使用构造函数和 setter 注入。
Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 globalsession 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。如果你在 web 中使用 global session 作用域来标识 bean,那么 web会自动当成 session 类型来使用。 仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用。
Spring 容器能够自动装配 bean。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。 自动装配的不同模式:
Spring框架支持以下五种bean的作用域:
注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。
不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring框架xml配置中共有5种自动装配:
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
@Autowired可用于:构造函数、成员变量、Setter方法
@Autowired和@Resource之间的区别
当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:
Spring支持两种类型的事务管理:
(1)编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
(2)声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
(1)PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
(2)PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
(3)PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
(4)PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
(5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
(6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
(7)PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
(1)ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
(2)ISOLATION_READ_UNCOMMITTED:读未提交,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
(3)ISOLATION_READ_COMMITTED:读已提交,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
(4)ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
(5)ISOLATION_SERIALIZABLE:串行化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
(1)脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
(2)不可重复读 :是指在一个事务内,多次读同一数据。
(3)幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
(1)JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
(2)如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
(1)事务方法访问修饰符非public,导致事务失效
(2)@Transactional注解的方法抛出的异常不是spring的事务支持的异常,导致事务失效
(3)数据表本身是不支持事务,导致事务失效
(4)@Transactional注解所在的类没有被spring管理,导致事务失效
(5)catch掉异常之后,没有再次抛出异常,导致事务失效
拦截器(Interceptor)和过滤器(Filter)的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
参考文档: