博主现在大三在读,从三月开始找暑期实习,暑假准备去tx实习啦!总结下了很多面试真题,希望能帮助正在找工作的大家!相关参考都会标注原文链接,尊重原创!
参考:
MVC是Model-View-Controller的缩写,它将应用程序划分为三个部分:
Model: 模型,管理数据
View: 视图,展示数据
Controller: 控制器,处理数据
MVP是Model-View-Presenter的缩写,它从MVC衍生而来,从名称上看只是将C换成了P。其他都一样。而事实上呢?它们也确实这样,P承担了C的任务而已。
MVC框架中,V的数据从Model中拿
MVP框架中,V的数据从Presenter中拿。
MVVM是Model-View-ViewModel的缩写,为了解决前端的响应式编程而生,由于前端网页混合了HTML、CSS和JavaScript,而且页面众多,代码的组织和维护难度复杂,所以通过ViewModel实现View和Model的双向绑定。
Web服务器是运行及发布Web应用的容器,只有将开发的Web项目放置到该容器中,才能使网络中的所有用户通过浏览器进行访问。开发Java Web应用所采用的服务器主要是与JSP/Servlet兼容的Web服务器,比较常用的有Tomcat、Resin、JBoss、WebSphere 和 WebLogic 等,下面将分别进行介绍。
Tomcat
目前最为流行的Tomcat服务器是Apache-Jarkarta开源项目中的一个子项目,是一个小型、轻量级的支持JSP和Servlet 技术的Web服务器,也是初学者学习开发JSP应用的首选。
Resin
Resin是Caucho公司的产品,是一个非常流行的支持Servlet和JSP的服务器,速度非常快。Resin本身包含了一个支持HTML的Web服务器,这使它不仅可以显示动态内容,而且显示静态内容的能力也毫不逊色,因此许多网站都是使用Resin服务器构建。
JBoss
JBoss是一个种遵从JavaEE规范的、开放源代码的、纯Java的EJB服务器,对于J2EE有很好的支持。JBoss采用JML API实现软件模块的集成与管理,其核心服务又是提供EJB服务器,不包含Servlet和JSP的Web容器,不过它可以和Tomcat完美结合。
WebSphere
WebSphere是IBM公司的产品,可进一步细分为 WebSphere Performance Pack、Cache Manager 和WebSphere Application Server等系列,其中WebSphere Application Server 是基于Java 的应用环境,可以运行于 Sun Solaris、Windows NT 等多种操作系统平台,用于建立、部署和管理Internet和Intranet Web应用程序。
WebLogic
WebLogic 是BEA公司的产品,可进一步细分为 WebLogic Server、WebLogic Enterprise 和 WebLogic Portal 等系列,其中 WebLogic Server 的功能特别强大。WebLogic 支持企业级的、多层次的和完全分布式的Web应用,并且服务器的配置简单、界面友好。对于那些正在寻求能够提供Java平台所拥有的一切应用服务器的用户来说,WebLogic是一个十分理想的选择。
什么是跨域?
要明白什么是跨域之前,首先要明白什么是同源策略?
同源策略是一个重要的安全策略,用来限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
那怎样判断是否是同源呢?
跨域,指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略所造成的,是浏览器对于JavaScript所定义的安全限制策略。
1️⃣ 使用Filter方式进行设置
使用Filter过滤器来过滤服务请求,向请求端设置Response Header(响应头部)的
Access-Control-Allow-Origin
属性声明允许跨域访问。
@WebFilter
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
}
2️⃣ 继承 HandlerInterceptorAdapter
@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
3️⃣ 实现WebMvcConfigurer
@Configuration
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
public class AppConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 拦截所有的请求
.allowedOrigins("http://www.abc.com") // 可跨域的域名,可以为 *
.allowCredentials(true)
.allowedMethods("*") // 允许跨域的方法,可以单独配置
.allowedHeaders("*"); // 允许跨域的请求头,可以单独配置
}
}
4️⃣ 使用Nginx配置
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
}
5️⃣ 使用 @CrossOrgin
注解
如果只是想部分接口跨域,且不想使用配置来管理的话,可以使用这种方式
在Controller使用
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
}
}
在具体接口上使用
@RestController
@RequestMapping("/user")
public class UserController {
@CrossOrigin
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
}
}
6️⃣ Spring Cloud Gateway 跨域配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
// 允许跨域的源(网站域名/ip),设置*为全部
// 允许跨域请求里的head字段,设置*为全部
// 允许跨域的method,默认为GET和OPTIONS,设置*为全部
allow-credentials: true
allowed-origins:
- "http://xb.abc.com"
- "http://sf.xx.com"
allowed-headers: "*"
allowed-methods:
- OPTIONS
- GET
- POST
- DELETE
- PUT
- PATCH
max-age: 3600
注意: 通过gateway
转发的其他项目,不要进行配置跨域配置
有时即使配置了也不会起作用,这时你可以根据浏览器控制的错误输出来查看问题,如果提示是 response
中 header 出现了重复的 Access-Control-*
请求头,可以进行如下操作
import java.util.ArrayList;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component("corsResponseHeaderFilter")
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// 指定此过滤器位于NettyWriteResponseFilter之后
// 即待处理完响应体后接着处理响应头
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.defer(() -> {
exchange.getResponse().getHeaders().entrySet().stream()
.filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
.filter(kv -> (
kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_MAX_AGE)))
.forEach(kv -> {
kv.setValue(new ArrayList<String>() {
{
add(kv.getValue().get(0));
}});
});
return chain.filter(exchange);
}));
}
}
优点:
缺点:
#{}
是预编译处理,是占位符;${}
是字符串替换,是拼接符
Mybatis的插件本质上指的就是Mybatis的拦截器,它只能拦截ParameterHandler
、ResultSetHandler
、StatementHandler
、Executor
四个接口,所以Mybatis拦截器实现的功能是很有限的。
拦截原理:Mybatis使用jdk的动态代理,为需要拦截的接口生成代理对象以实现接口方法的拦截功能,每当执行这四种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler中的invoke()方法,拦截指定的方法
1️⃣ 轻量级开源的J2EE框架
2️⃣ 轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
一个系统是由不同的组件组成的,每个组件都负责一块特定的业务,实现自己的核心功能;此外,这些组件可能还会有额外的任务。比如日志、事务管理、安全保障等业务,这些业务常常需要融入到不同组件的核心功能中,会跨越系统的多个组件,这些额外的需求在AOP中被称为横切关注点;
当我们需要为这些分散的组件引入公共的需求的时候,OOP
就显得无能为力,因为面向对象编程的思想会将这额外的业务定义成一个个业务类然后插入到每个组件的核心业务中,十分麻烦,会有大量代码的重复,不利于各个模块的复用。AOP就能很好的解决这些问题。
AOP
:将程序中的交叉业务逻辑(比如安全、日志、事务等)封装成一个切面,然后注入到目标对象的具体业务逻辑中。AOP可以对某个对象或者某个对象的某些功能进行增强,比如对对象方法的增强,可以在方法的前后添加额外的功能
使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
从三个方面来回答:
容器概念
、控制反转
、依赖注入
1️⃣Ioc容器:本质上就是一个map(key-value),里面存放了各种对象:
xml中配置的bean结点
@Repository、@Service、@Controller、@Conponent标注的类
在项目启动的时候会读取配置文件中的的bean结点,扫描以上注解标时的类,根据全限定类名通过反射创建对象存放到map中;
2️⃣ 控制反转:没有引入IoC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
引入IoC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IoC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转这个名称的由来
全部对象的控制权全部上缴给"第三方"IoC容器,所以IoC容器成了整个系统的关键核心,它起到了一种类似“粘合剂的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂,对象与对象之间会彼此失去联系,这就是有人把IoC容器比喻成“粘合剂"的由来。
3️⃣ 依赖注入:DI是IoC的一种实现方式,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。
ApplicationContext
是BeanFactory
的子接口,体统了更完整的功能:
区别:
BeanFactory
采用延迟加载的形式来注入Bean,即只有在使用到某个Bean,也就是调用getBean()方法时,在对Bean进行加载实例化。这样我们在容器启动的时候就不能发现spring存在的配置问题,比如Bean的某个属性没有注入,直到BeanFactory加载后第一次调用getBean()方法才会抛出异常ApplicationContext
在容器启动时一次性创建了所有的Bean。这样就能在容器启动的时候发现Spring中存在的配置问题,有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例Bean,确保需要使用的时候无需等待,因为已经创建好了BeanFactory
,ApplicationContext
唯一的不足是占用内存空间,当应用程序配置了很多的Bean时,程序启动较慢BeanFactory
通常以编程的方式创建,ApplicationContext
可以以声明的方式创建(使用ContextLoader)1️⃣ 实例化
BeanDefinition
,它描述了bean对象的一系列信息2️⃣ 属性赋值
@Autowired
等注解的对象的进行属性的赋值3️⃣ 注入Aware接口
xxxAware
接口,并将相关的xxxAware实例注入给bean4️⃣ 前置处理
当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以继承BeanPostProcessor
接口实现其前置处理方法,这个方法会先于InitialzationBean执行,因此称为前置处理
//当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
//这个函数会先于InitialzationBean执行,因此称为前置处理。
postProcessBeforeInitialzation( Object bean, String beanName )
5️⃣ 初始化
InitializingBean
6️⃣ 后置处理
在bean初始化完成后可以进行后置处理,它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑,AOP
通常就在此实现
//当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。
//这个函数会在InitialzationBean完成后执行,因此称为后置处理。
postProcessAfterInitialzation( Object bean, String beanName )
7️⃣ 使用bean
8️⃣ 销毁bean
DisposableBean
中destory()
方法当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
singleton
:默认的单例模式,每个容器中只有一个bean实例,单例的模式由BeanFactory(第一次注入时创建)自身来维护。该对象的生命周期与Spring IoC容器一致prototype
:原型模式,为每一个bean都提供一个实例,每次注入都会创建一个对象request
:每个HTTP请求都会为bean创建一个实例,请求结束该实例回收session
:与request类似,每个session中有一个bean实例,session过期后该实例回收application
:在ServletContext的生命周期中复用一个单例对象websocket
:在websocket的声明周期中复用一个单例对象global-session
:每个全局的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作用域。
在Spring中,bean默认作用域是单例的,在spring容器中全局共享,框架并没有对bean进行多线程的封装,会存在多线程访问的问题,并不安全
首先搞清楚什么是有状态
、什么是无状态
如果Bean是有状态的,那么就需要由我们来进行线程安全的保证,最简单的方法就是改变bean的作用域,将singleton
改为protopyte
,这样每次请求bean就相当于new Bean(),这样就可以保证线程安全了
此外,还可以用ThreadLocal
来确保安全,例如Dao层与数据库进行交互,会操作Connection对象,Connection对象就是带有状态的,比如数据库事务的操作,Spring的事务管理器就是使用ThreadLocal为不同线程维护了一套独立的connection副本来保证线程之间不会相互影响
不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,那么就使用 Threadloca把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用 synchronized、Lock、CAS等这些实现线程同步的方法了。
1️⃣ 简单工厂:由一个工厂类根据传入的参数,动态决定创建哪一个产品类
spring中的BeanFactory
就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。
2️⃣ 工厂方法:
实现了FactoryBean
接口的bean是一类叫做factory的bean,其特点是:spring在使用getBean()获得bean时,会自动调用该类bean的getObject()方法,所以返回的不是这个bean,而是bean.getObject()的返回值
3️⃣ 适配器模式:
spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展 Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了
4️⃣ 装饰器模式:动态地给一个对象添加一些额外的职责。就増加功能来说,Decorator模式相比生成子类更为灵活。
Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper
,另一种是类名中含有Decorator
5️⃣ 动态代理:
织入:把切面应用到目标对象并创建新的代理对象的过程。
切面在运行时被织入,一般情况下,在织入切面时,AOP容器会未目标对象动态创建一个代理对象,SpringAOP就是以这种方式织入切面的
6️⃣ 观察者模式:
spring的事件驱动模型使用的是观察者模式,Spring中observer模式常用的地方是listener的实现
7️⃣ 策略模式:
Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring框架本身大量使用了Resource接口来访问底层资源。
1️⃣ 编程式
编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
2️⃣ 声明式
基于 Aspectj AOP 配置事务具体流程
1️⃣ 开启Spring的事务处理功能
创建一个 DataSourceTransactionManager
对象,传入上述创建好的dataSource
对象
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
2️⃣ 配置事务通知
这里使用事务tx
命名空间配置
常用的两个属性
用
name
属性规定配置事务的方法用
propagation
属性配置事务的传播特性,默认为REQUIRED
REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务 REQUIRED_NEW 新建事务,如果当前存在事务,把当前事务挂起 SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行 NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 MANDATORY 支持当前事务,如果当前没有事务,就抛出异常 NEVER 以非事务方式执行,如果当前存在事务,则抛出异常 NESTED 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
3️⃣ 结合AOP实现事务织入
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
aop:config>
事务传播机制 是指多个事务相互调用时,事务是如何在这些方法之间传播的问题
例如:方法A是一个事务的方法,执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A事务的具体执行造成影响,同时方法A的事务对方法B的事务执行也会有影响,这种影响具体是什么是由两个方法所定义的事务传播类型决定的
传播类型 | 介绍:当前指A,新建的事务都指B |
---|---|
REQUIRED | 如果当前有事务,则加入当前事务;如果当前没有事务,就新建一个事务 如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,则给B新建一个新事务 |
REQUIRED_NEW | 新建事务,如果当前存在事务,把当前事务挂起 B会创建一个新的事务,如果A有事务,则将A的事务挂起执行,也就是各自执行各自的事务 |
SUPPORTS | 如果当前有事务,则加入当前事务;如果当前没有事务,就以非事务方式执行 如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,B以非事务方式运行 |
NOT_SUPPORTED | 以非事务方式执行,如果当前存在事务,就把当前事务挂起 B以非事务方式运行,如果A有事务,则将A的事务挂起执行 |
MANDATORY | 如果当前有事务,则加入当前事务;如果当前没有事务,就抛出异常 如果A有事务,则将B事务加入A的事务中作为一整个事务一起执行;如果A没有事务,则抛出异常 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 如果A有事务,则抛出异常 |
NESTED | 如果当前事务存在,则开启一个嵌套事务,如果当前没有事务,就新建一个事务 如果A有事务(父事务),则给B开启一个嵌套事务(也称子事务),如果父事务回滚,子事务也会回滚;如果A无事务,则开启一个新事务 |
spring事务的原理是
AOP
,进行了切面增强,所以失效的根本原因就是AOP不起作用了,常见情况如下:
这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭。
如下面例子所示:
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
// update order
}
}
如果此时把 @Service
注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
@Transactional
只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ
代理模式。
来看两个示例:
@Service
public class OrderServiceImpl implements OrderService {
public void update(Order order) {
updateOrder(order);
}
@Transactional
public void updateOrder(Order order) {
// update order
}
}
update方法上面没有加 @Transactional
注解,调用有 @Transactional
注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?
再来看下面这个例子:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateOrder(Order order) {
// update order
}
}
这次在 update 方法上加了 @Transactional
,updateOrder 加了 REQUIRES_NEW
新开启一个事务,那么新开的事务管用么?
这两个例子的答案是:不管用!
因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
这个的解决方案之一就是在的类中注入自己,用注入的对象再调用另外一个方法,这个不太优雅,另外一个可行的方案可以参考《Spring 如何在一个事务中开启另一个事务?》这篇文章。
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
如上面所示,当前数据源若没有配置事务管理器,那也是白搭!
来看下面这个例子:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateOrder(Order order) {
// update order
}
}
Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起,详细的可以参考《事务隔离级别和传播机制》这篇文章。
都主动不支持以事务方式运行了,那事务生效也是白搭!
这个也是出现比较多的场景:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
}
}
}
把异常吃了,然后又不抛出来,事务就不生效了
上面的例子再抛出一个异常:
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
throw new Exception("更新错误");
}
}
}
这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:
@Transactional(rollbackFor = Exception.class)
这个配置仅限于 Throwable
异常类及其子类。
Spring
是一个IOC容器,用于管理Bean,通过DI依赖注入的方式实现控制反转,可以方便的整合各种框架,且提供了AOP机制弥补了OOP的代码重复问题,更方便的将不同类不同方法中的共同处理抽取成为切面,自动注入给方法执行,例如异常、日志
SpringMVC
是spring对web框架的一个解决方案,提供了一个总的前端控制器servlet,用于接受请求,然后定义了一套路由策略(url到heandler的映射)以及适配执行handler,将handler处理的结果使用视图解析器生成视图展现给前端
SpringBoot
是spring提供的一个快速开发工具包,让程序员能更方便、快速的开发spring+springmvc的应用,约定大于配置,通过stater机制整合了一系列的解决方案,使得redis、mongodb、es可以开箱即用
1)用户发送请求至前端控制器 DispatcherServlet
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行査找),生成处理器及处理器拦截器(如果有则生成)并以 HandlerExecutionChain 对象的形式返回给 DispatcherServlet
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。
5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)
6)Controller 执行完成返回 ModelAndview
7)HandlerAdapter 将 controller 执行结果 ModelAndview 返回给 DispatcherServlet
8)DispatcherServlet 将 ModelAndview 传给 ViewReslover 视图解析器。
9)ViewReslover 解析后返回具体view。
10)DispatcherServlet 根据 view 进行渲染视图(即将模型数据填充至视图中)
11)DispatcherServlet响应用户。
SpringFactoriesLoader.loadFactoryNames是spring的一种spi机制
结论:
package com.zsr.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@ResponseBody
public class HelloController {
@RequestMapping("/hello")
public int hello(@RequestParam int a, @RequestParam int b) {
return a + b;
}
}
访问:http://localhost:8080/hello?a=1&b=2
SpringBoot热部署实现有两种方式:
1️⃣ 使用spring loaded
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
<version>1.2.6.RELEASEversion>
dependency>
dependencies>
plugin>
plugins>
build>
添加完毕后需要使用mvn指令运行:
首先找到IDEA中的Edit configurations ,然后进行如下操作:(点击左上角的"+",然后选择maven将出现右侧面板,在红色划线部位输入如图所示指令,你可以为该指令命名(此处命名为MvnSpringBootRun))
2️⃣ spring-boot-devtools
在Spring Boot 项目中添加 spring-boot-devtools依赖即可实现页面和代码的热部署
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
1.简介
简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
2.轻量
从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
3.控制反转
Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
4.面向切面
Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
5.容器
Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
6.框架
Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping注解有六个属性,下面我们把她分成三类进行说明。
value, method:
consumes,produces
params,headers
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
一级缓存默认是开启,它是SqlSession
级别的缓存,也称为本地缓存
二级缓存需要手动开启和配置,它是基于namespace
级别的缓存,也称全局缓存,一级缓存作用域太低,所以诞生了二级缓存;只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到。大师数据查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中(注意:使用二级缓存属性类需要实现Serializable序列化接口,可用来保存对象的状态)
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通实现Cache接口来自定义二级缓存