Spring AOP知识点
AOP面向切面编程,处理的对象是切面,在处理日志,安全管理,事务管理等方面有非常重要的作用。用来在程序运行期间对IOC做补充
AOP(spring aop)底层用的是代理(动态代理)
Spring AOP and AspectJ AOP 有什么区别?AOP 有 哪些实现 方式?
AOP 实现的关键在于代理模式 ,AOP 代理主要分为静态代理和动态代理。静 态 代理的代表为 AspectJ ;动态代理则以 Spring AOP 为代表。
( 1) AspectJ 是静态代理的增强 ,所谓静态代理 ,就是 AOP 框架会在编 译阶段 生成 AOP 代理类 ,因此也称为编译时增强 ,他会在编译阶段将 AspectJ(切面)织 入到 Java 字节码中 ,运行的时候就是增强之后的 AOP 对 象。
( 2) Spring AOP 使用的动态代理 ,每次运行时在内存中临时为方法生成一个 AOP 对象 , 这个AOP 对象包含了目标对象的全部方法 ,并且在特定的切点做了增强处 理 ,并回调原对象的方法。
代理分为:1.静态代理,需要自己创建代理类,不好的地方是需要为每一个被代理的类创建代理类,这种实现方式成本高 2.动态代理,其中又分为jdk动态代理(必须保证被代理的类实现了接口)和cglib动态代理(不需要接口)
jdk动态代理是基于反射机制,生成一个实现代理接口的匿名类。cglib是基于继承机制(字节码增强),继承被代理类,生成一个子类,然后覆盖其中的方法。所以性能上会有差异。cglib底层是基于asm第三方框架,对代理对象类的class文件加载进来,通过修改字节码生成子类来处理(字节码增强)
jdk动态代理只能针对实现了接口的类,不能对没有实现接口的类进行代理。cglib动态代理因为是基于继承机制,所以该类或方法最好不用声明为final.
在老版本中,cglib的速度是jdk速度的10倍左右,但是在新版本中,1w次执行下,jdk7及8的动态代理性能比cglib要好20%左右
JDK动态代理和CGLIB动态代理都是在运行时动态地创建代理对象
aop中,被注入到ioc容器的是动态代理对象,而不是被代理的对象
cglib会使用到Enhancer相关字节码增强类
Spring AOP底层原理
spring aop的代理通过ProxyFactory进行封装。封装了代理和@Before @After等相关逻辑。ProxyFactory会判断类是否实现了接口,如果实现了接口则使用jdk动态代理,如果没有则使用cglib动态代理。具体逻辑是在DefaultAopProxyFactory实现,不同版本spring,其实现逻辑不一样。整个spring aop的底层就是ProxyFactory。
spring aop设置的切面规则和切面的逻辑是通过beanPostProcessor来作用到相关的bean的,其具体beanPostProcessor是AnnotationAwareAspectJAutoProxyCreator,@Import(AnnotationAwareAspectJAutoProxyCreator.class)和@EnableAspectJAutoProxy效果一样,AnnotationAwareAspectJAutoProxyCreator是个BeanPostProcessor。每个bean在初始化后会判断是否需要AOP。
执行被代理的对象的方法是通过反射来实现的
@Lazy也用到了spring动态代理 ProxyFactory
Advisor=Advise(代理逻辑)+PointCut(切面规则)
Advice + Target Object = Proxy
将 Advice 应用于目标对象后创建的对象称为代理。
TargetSouce表示被代理对象,这个对象可以灵活改变
连接点和切点的区别:
Spring Aop中四个重要概念,切点,切面,连接点,通知
通知: 就是我们编写的希望Aop时执行的那个方法。我们通过Aop希望我们编写的方法在目标方法执行前执行,或者执行后执行。
切点:切点就是我们配置的满足我们条件的目标方法。比如我们规定:名字前面是select开头的才执行我们自定义的通知方法。那么这些select开头的方法就是切点。
连接点:连接点可以说是切点的全集。切点是连接点的子集。也可以理解为,连接点是我们没有定义那个select开头规则时,满足条件的全部的方法。
切面:切面是切点和通知的组合称谓,就是变相给组合起了个名
Spring 事务知识点
什么是事务?事务是一种机制。把一组业务当成是一个业务来做,要么成功要么都失败,保证业务操作完整性。
声明式事务4大特性 ACID 原子性(一组业务要么都成功,要么都失败)、一致性(事务前后要保证数据的一致性)、隔离性(在并发情况事务之前要相互隔离)、持久性(数据一旦保存就是持久的)
事务分为编程式事务:在代码中直接加入处理事务的逻辑,显示调用beginTransaction、commit、rollback , 声明式事务:在方法外部通过注解或配置文件定义事务。声明式事务可以通过AOP来实现。
不管什么事务底层都是控制connection
事务配置的属性包含:isolation设置事务的隔离级别、propagation事务传播行为,哪些事务可回滚、是否只读、超时时长等
事务的使用需要用到事务管理器TransactionManager,无论是编程式还是声明式,我们可以直接使用事务管理器来管理事务,而无需了解其底层是如何实现的。
Spring 事务原理
切面编程和事务都需要用到AOP,它们的原理都是通过beanPostProcessor实现的。只不过切面编程用到的是AspectJAwareAdvisorAutoProxyCreator这个beanPostProcessor,而事务用的是InfrastructureAdvisorAutoProxyCreator,它们父类都是AbstractAdvisorAutoProxyCreator。AspectJAwareAdvisorAutoProxyCreator会去解析aspectj定义的切面及其规则,而因为事务不需要解析切面,所以用的不是AspectJAwareAdvisorAutoProxyCreator,只需去解析添加advisor即可。
Advisor=Advise(代理逻辑)+PointCut(切面规则)
事务AOP时添加的advisor的advice就是事务拦截器
事务流程原理,会用到threadLocal,事务会将创建的connection存放到threadLocal中。这样被代理对象就可以拿到当前线程的connection。不同的事务用的是不同的connection,新开启事务会创建新的connection。但是在同一个事务中嵌套另外一个事务时,是否用同一个connection,要看事务传播配置。
为了处理在嵌套事务的时候出现threadLocal connection被覆盖,会有挂起操作。挂起操作逻辑在TransactionSynchronizationManager中进行存储实现。
TransactionAttributeSource用于缓存@Transactional注解的配置
TransactionAttribute用于存放每个属性值
开启事务及相关处理逻辑在DataSourceTransactionManager这个类里。会依次进行新建conn,设置隔离级别,设置只读,设置autoCommit,定义超时时间等操作。这一系列操作形成了一个事务。所以说事务不是一个新的操作术语,事务其实就是上面的几个步骤的集合一起实现了事务这个概念。
事务传播行为总共有7种,具体看Propagation这个枚举。不开启新的事务是指不会开启新的数据库连接conn。其中NOT_SUPPORTED表示不支持事务,不会用spring的事务,即不会使用spring创建的连接。而是用jdbcTemplate自己的事务,自己创建的连接conn。注意事务上面的概念。
Spring MVC知识点
springmvc和spring都会扫描bean,springmvc会扫描controller,而spring会扫描除了controller外的bean
Spring mvc基于Servlet API上构建。是Spring框架的一部分,基于java实现的一个轻量级web框架。spring mvc围绕着前端控制器模式设计,核心是DispatchServlet。
springmvc需要用到spring-web、spring-webmvc。导入spring-webmvc后会自动导入spring-web ioc等。
DispatcherServlet又叫前端控制器,核心调度器,分发器
web.xml初始化参数的contextConfigLocation也可以不配置:springmvc会自动去WEB-INF去找一个名字叫做{servlet-name}-servlet.xml
servlet-mapping的映射规则url pattern表示的含义
/ 除了.jsp的请求都会被匹配
/*所有的请求都会被匹配
.do、.action url结尾以.do或者.action的请求会匹配
DispatchServlet拦截处理请求流程:
1.客户端发送请求,由tomcat接收到对应请求。
2.springmvc的前端控制器DispatchServlet接收到对应的请求并进行拦截。
3.DispatchServlet根据请求url调用HandlerMapping,匹配返回给DispatchServlet对应的Handler(具体的处理请求方法)
4.DispatchServlet调用HandlerAdapter(负责调用Handler,并返回ModelAndView或json什么的),由HandlerAdapter执行Handler,Handler会封装视图和模型为ModelAndView返回给HandlerAdapter。
5.HandlerAdapter将ModelAndView(此时View视图对象中还没有包含jsp)返回给DispatchServlet。
6.DispatchServlet调用视图解析器ViewResolver(根据ModelAndView的视图名找具体的jsp封装在View视图对象中),解析ModelAndView(此时View中已经包含了jsp),并返回View视图对象给DispatchServlet。
7.DispatchServlet调用View视图对象的渲染方法进行渲染(将jsp转换成html),并将渲染结果返回给DispatchServlet。
8.DispatchServlet将渲染结果返回给客户端。
400是springmvc自定义的错误码 不是http错误码
分布式一般做前后端分离 ssm一般不会做前后端分离
rest是客户端映射到服务器资源的一种架构设计,是一种优雅的url风格,restful主要是规范化url设计
request.method只支持GET/POST/HEAD,要支持PUT和DELETE,需要做些调整
DispatcherServlet url-pattern设置为/后,.css .html等都会被DispatcherServlet拦截,所以为了能够访问.css .html,需要进行访问静态资源相关配置。<mvc:resources mapping=“” location=“”>,也可以配置mvc:default-servlet-handler。
配置了mvc:default-servlet-handler后,当请求没有映射到一个servelt时,会调用tomcat默认servlet,这个servlet会处理静态资源访问。
这个tomcat默认servlet不是DispatcherServlet,DispatcherServlet是springmvc的默认处理servlet,而不是tomcat默认servlet。
modelAndview 即包含model和view ,然后交给viewReslover解析器解析。默认解析器只支持解析jsp
ModelAndView传输数据和视图,底层都是将数据设置到request
读写session方式有
1.通过servlet api 其中又包含参数绑定的方式和自动注入的方式(推荐用第二个-自动注入)


2.通过Session相关注解

3.通过@ModelAttribute的方式(不推荐,存在线程安全问题)

以上3种方式的获取servletapi的线程安全问题:
- 通过参数绑定的方式
是线程安全的,因为参数绑定变量时方法级别,所以每次请求方法都会创建一个新的私
有变量。
2.通过自动注入的方式@AutoWired
是线程安全的,spring底层通过自动注入 将 servlet api相关bean对象 封装到ThreadLocal(是绑定
在线程上面的)里面.
3.通过@ModelAttribute的方式
不是线程安全的,控制器是单例的,并且变量声明在类的级别中(共享变量)。
所以一般使用局部变量,或使用ThreadLocal
关于@Autowired注入获取servlet api线程安全问题,讲到会用到ThreadLocal。这里只是在spring-web这个spring子项目中会将servlet api相关bean对象如HttpServletRequest,HttpServletResponse,Session存放到ThreadLocal中。spring其它bean是不会放到ThreadLocal中的。
拦截器
采用aop思想,类似servlet过滤器。用来拦截方法,在方法执行前后处理一些跟主业务逻辑无关的逻辑。比如实现权限控制、日志、异常记录、记录方法执行事件等。
拦截器是springmvc的,过滤器是servlet的。过滤器先执行。
拦截器跟过滤器区别:
1.执行顺序有区别
请求->tomcat->过滤器->Servlet(DispatchServlet)->拦截器->Controller
2. 过滤器基于函数回调,拦截器基于java反射(因为aop)
3.过滤器依赖于servlet容器(tomcat),而拦截器依赖于springmvc
4.过滤器几乎对所有请求都可以起作用,而拦截器只能对springmvc请求起作用。
5.拦截器可以访问方法的上下文,而过滤器不行。
前后端分离项目中,字符串国际化可以交由前端做,但是接口返回数据国际化还是得交给后端来做
比如数据验证错误信息和代码字符串国际化
Spring MVC原理
springmvc原理其实就是:springmvc父子容器启动流程和springmvc请求流程两个部分。
springmvc请求流程
springmvc只是spring的一个模块,它不是一个新的框架
servlet是一个单例,不会每次请求都创建
HandlerMapping可以配置,不配置的话用默认的,而且可以配置多个,可以配置优先级
HandlerAdapter种类主要有4种,每种handlerMapping都有一种apdater和它对应。
1.HttpRequestHandlerAdapter:适用于实现HttpRequestHandler接口的处理器
2.SimpleServletHandlerAdapter:用于继承HttpServlet的处理器
3.SimpleControllerHandlerAdapter:适用于实现Controller接口的处理器
4.RequestMappingHandlerAdapter:适用于开启注解的处理器(一般使用这种)
springmvc请求流程图

RequestMappingInfo封装了RequestMapping注解信息,封装成一个个Condition,这些condition用来作匹配用的
解析地址的时候,会以key=path/name,value=HandlerMapping放到pathLookup或nameLookup等Map中。

解析json的Converter

springmvc父子容器启动流程
(spring为父容器、springmvc为子容器)
DispatcherServlet也需要加载一个spring容器
为什么要弄两个ioc容器?一个不可以吗
因为spring mvc 是个前端框架,不一定只用于和spring 整合(即 Spring MVC 可以单独用作一个 Web 应用的前端控制器,而不需要使用 Spring 的其他部分(例如 Spring Core 或 Spring Data)),因此是很有必要设计一个自己管辖的ioc容器,有点单一职责的味道。
启动流程图
图。
总结
- spring 启动会立即初始化ioc容器 为父容器。
- spring mvc 在发起第一次请求时会初始化 spring mvc容器 为子容器。
- spring 容器初始化的范围是除了controller层的,比如dao层 service层。
- spring mvc 初始化的范围是controller层。
- 子容器能访问父容器中的对象,父容器访问不到子容器中的对象。
- 在controller层查找容器中的对象会先查找子容器再查找父容器,其他层都是直接从父容器中查找
- 父容器和子容器中不能存在相同的bean。
Root WebApplicationContext为父容器(spring ioc) createRootApplicationContext(),Servlet WebApplicationContext(spring mvc ioc) createServletApplicationContext()
SPI(Service Provider Interface,服务提供者接口,为tomcat提供的机制):
其实就是根据Servlet厂商(服务提供商)提供要求的一个接口, 在固定的目录(META-INF/services)放上以接口全类名 为命名的文件, 文件中放入接口的实现的全类名,该类由我们自己实现,按照这种约定的方式(即SPI规范),服务提供商会
调用文件中实现类的方法, 从而完成扩展。
Tomcat为服务提供者。
实现类为SpringServletContainerInitializer.class
SpringServletContainerInitializer实现逻辑如下:即Tomcat启动的时候会把遍历实现了WebApplicationInitializer接口的类,并执行onStartup方法。
Servlet ioc容器就是根据spi规范(机制)来进行启动的。在启动的时候,xml和注解方式(springboot)启动的实现会有不同。
springmvc父子容器启动原理是根据SPI规范来实现的。

1.先通过spi机制,找到spi实现类SpringServletContainerInitializer.class。
2.SpringServletContainerInitializer.class会找到所有实现WebApplicationInitializer接口的类,包含了父子容器的WebApplicationInitializer接口实现类,并执行其onStartup方法。
3.onStartup中会创建父子容器
4.初始化父子容器(初始化ContextLoaderListener、初始化DispatcherServlet)及其他操作,如加载、初始化bean之类。
spring子容器在加载bean前会注册一个ContextRefreshListener监听器,当该子容器在加载完所有bean后会发送ContextRefreshListener的事件,调用onApplicationEvent方法,然后通知FrameworkServlet进行onRefresh方法,在这个方法中会进行子容器初始化操作。比如初始化handlerMapping viewResolvers等,为后续SpringMVC处理请求做准备。如果解析到没有配置handlerMapping viewResolvers等,则会根据情况使用默认的HandlerMapping,ViewResolvers等。
是否可以把所有Bean都通过Spring容器来管理?(Spring的applicationContext.xml中配置全局扫描)
不可以,这样会导致我们请求接口的时候产生404。 如果所有的Bean都交给父容器,SpringMVC在初始化HandlerMethods的时候(initHandlerMethods)无法根据Controller的handler方法注册HandlerMethod,并没有去查找父容器的bean;
也就无法根据请求URI 获取到 HandlerMethod来进行匹配。
因为springmvc在代码中是这么写的:不会去父容器找controller,只会在子容器中找。
SpringData&JPA
为什么要用SpringData 统一了持久层 提升了开发效率 针对不同的存储方式提供了不同的模板对象来进行数据存储
springdata包含多个子项目,比如springdata jdbc、springdata mongodb、springdata redis、springdata jpa…针对不同数据库存储的实现。
JPA(这是一种规范,而非上面讲的springdata jpa)是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系ORM(对象关系映射),并将运行期的实体对象持久化到数据库中。
spring data jpa针对关系型数据库实现
springdata jdbc支持mybatis查询,springdata jpa默认使用hibernate实现
jpa和jdbc的相同点:1.都跟数据库操作有关,jpa基于jdbc,是jdbc的升级版 2.jdbc和jpa都是一组规范接口 3.都是由sun官方推出。不同点:1.jdbc是由各个关系型数据库实现,jpa是由全自动ORM框架实现 2.jpa需要依赖jdbc
Java Database Connectivity,简称JDBC
hibernate无需自己创建表
jpa支持对象和原生sql操作
JPA对象的分4种状态:临时状态、持久状态、删除状态、游离状态。JPA在对持久状态进行更新,就算不调用更新方法,直接提交事务也会进行更新数据库。如果对一个游离态的对象进行操作(比如删除)会报错,需要将游离态变为持久态后才能操作。
springdata jpa是spring提供的一套简化JPA开发的框架。可以在只写dao层接口而无需写实现的情况下对数据库进行访问和操作,同时提供了crud之外的比如分页、排序、复杂查询等操作。springdata jpa基于jdk动态代理封装了jpa。
spring jpa的定位是为简单数据库操作,简单业务逻辑而产生的。复杂用mybatis
springdata jpa没有多表关联操作的文档,它只是整合hibernate,实现用的是hibernate,要看相关文档就去hibernate官方文档查看
spring jpa Repository原理:动态代理+jpa实现。jdkDynamicAopProxy AOP统一处理类,调用jpa的Repository统一实现,调用jpa api
java web三大组件
Servlet
tomcat是一个servlet容器,里面运行了很多个servlet实例
servlet是一个接口,只要实现了servlet,就可以处理请求。一个项目可以包含多个servlet。
/index.jsp加了/之后表示根目录下的index.jsp。
重定向是在客户端发生的,客户端会认为根目录=8080/
转发是在服务端,服务端会认为根目录=8080/springmvc-servlet
所以重定向的话 /index.jsp实际访问的地址是8080/index.jsp,这是会报错的,需要将/去掉
请求和转发是一次请求,重定向是 两次请求。重定向也是一种资源跳转方式。重定向加/的时候一定要写资源访问路径。
重定向地址栏会发生变化,可以访问其它站点服务器的资源,由于重定向是两次请求,不能使用request对象来共享数据。所以当两个Servlet需要传递数据的时候,选择forward转发。不建议使用sendRedirect进行传递。
cookie有效期 默认关闭浏览器就失效 ,但是可以设置有效期。session也是一样,浏览器关闭或session超时或手动销毁就失效。
Cookie 存储在客户端,即用户的浏览器中。
Session 数据存储在服务器端。通常,服务器为每个会话生成一个唯一的 Session ID,并将该 ID 通过 Cookie 或 URL 参数传给客户端。
servlet执行原理
1.当服务器接收到客户端浏览器的请求后,会解析请求的URL路径,获取访问的Servlet的资源路径。
2.查找web.xml文件,查看是否有对应的资源(即接收请求的Serlvet)。
3.如果有,则找到对应的全类名。
4.tomcat会将对应Servlet字节码加载进内存,并且创建其对象。(如果配置了在Server启动时就加载Servlet,则该步骤会提前,详细看servlet生命周期)
5.调用其方法进行处理。
servlet生命周期
Servlet的生命周期可以被归纳为以下几步:
1.装载Servlet,这一项操作一般是动态执行的。但是,Servlet通常会提供一个可配置的选项,用于在Server启动时强制装载和初始化特定的Servlet(就是load-on-startup这个配置,可以把Servlet的创建提前到服务器启动时)。
2.Server创建一个Servlet实例。
3.Server调用Servlet的init方法。
4.一个客户端请求到达到达Server。(如果没有设置在Server启动时就加载创建Servlet实例,则这一部应该先执行,然后才装载Servlet操作。即先等客户端请求到了之后才开始装载Servlet)
5.Server创建一个请求对象。
6.Server创建一个响应对象。
7.Server调用Servlet的service方法,并传递请求和响应对象作为参数。
8.service方法获得请求对象的信息,处理请求,访问其它资源,获得需要的信息。
9.service方法使用响应对象的方法,将响应传回server。最终到达客户端。
10.对于一个servlet有多次客户端请求,则init方法只执行一次,下次直接调用service方法。
11.当server不在需要servlet,比如server关闭的时候。server调用Servlet的destroy方法。
总的来说,Servlet生命周期分为:实例化,初始化,提供服务,销毁。
servlet是一个单例,不会每次请求都创建
不是每次相同请求/url/方法进来,都会创建一个Servlet
servlet配置方式
servlet2.5之前在web.xml配置,3.0开始用注解方式@WebServlet(“/hello”)
servlet线程安全
如何保证线程安全?尽可能使用局部变量。
ServletContext上下文:作用范围是tomcat服务器启动和关闭。
作用范围包括:
一次请求范围
一次会话范围:浏览器打开和关闭,比如session,cookie
一次应用范围:tomcat服务器启动和关闭 ,比如ServletContext上下文
ServletContext是一个全局对象,对应一个Tomcat中的web应用
Filter过滤器
过滤器是出于客户端和服务器目标资源之间的一道过滤技术。可以将请求拦截下来,完成一些特殊的功能。
过滤器生命周期方法
1.init在服务器启动后,会创建Filter对象,然后调用init方法。方法只执行一次,用于加载资源
2.doFilter每一次请求被拦截时会执行,可以执行多次
3.destroy在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭的,则会执行destroy方法,该方法只执行一次,用于释放资源。
可以使用@WebFilter注解方式或xml方式配置过滤器
过滤器链:多个过滤器的组合
过滤器优先级
优先级:
如果为注解的话,是按照类全名称的字符串顺序决定作用顺序
如果web.xml,按照filter-mapping注册顺序,从上往下
web.xml配置高于注解方式
如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次
拦截器跟过滤器区别:
1.执行顺序有区别
请求->tomcat->过滤器->Servlet(DispatchServlet)->拦截器->Controller
2.过滤器基于函数回调,拦截器基于java反射(因为aop)
3.过滤器依赖于servlet容器(tomcat),而拦截器依赖于springmvc
4.过滤器几乎对所有请求都可以起作用,而拦截器只能对springmvc(controller)请求起作用。
5.拦截器可以访问方法的上下文,而过滤器不行。
Listener
主要用来监听Application、Session、Request三个对象的创建、销毁并对对象的属性进行增、删、改。
Listener的分类
Listener主要分为三类八种
比如servletContextListener,监听ServletContext对象的创建和销毁
二&spm=1001.2101.3001.5002&articleId=137832558&d=1&t=3&u=acefe92ae1c14b42b49c373c890d5ce9)
4690

被折叠的 条评论
为什么被折叠?



