spring

什么是IOC容器?

Spring容器使用以来管理组成应用程序的组件。容器通过读取提供的配置元数据来接口对象进行实例化,配置和组装的指令,该元素可以通过xml,java注解或这java代码提供.
控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

有多少方式实现依赖注入?

1、构造函数注入
2、Setter注入
3、接口注入
好处:
将最小化应用程序中的代码量
使得应用程序易于测试,
以最小的影响和最少的侵入机制促进松耦合
支持即时的实例化和延迟加载服务

Bean的作用域scope

1、singleton:在容器中仅存在bean实例,bean以单例的方式存在,默认值
2、Prototype:每次从容器中调用bean,都返回一个新的实例,也就是每次调用getBean()时,相当于执行new xxxBean()
3、Request:每次http请求都会创建一个新的bean,
4、Session:同一个http session共享一个bean,不同session使用不同的bean
5、GlobalSession:一般用于portlet
五个作用域中,request、session、gloableSession三种作用域仅在基于web应用中使用。
Singleton是单例类型,就是在创建容器时就自动创建了一个对象,不管是否使用,他都存在了,每次获取的对象都是同一个对象。注意:sigleton的作用域是spring的缺省作用域

IOC的好处

1、它将最小化应用程序中的代码量
2、它使得应用程序易于测试,因为它不需要单元测试用例中的任何单例或者JNDI查找机制
3、以最小的影响和最少的侵入机制促进松耦合
4、支持即时的实例化和延迟加载服务

bean的生命周期

1、根据配置debean定义中实例化bean
2、使用依赖注入填充所有属性
3、如果bean使用beanNameAware接口,则工程通过船体bean的ID来调用setBeanName()
4、如果bean实现beanFactoryAware接口,工程通过传递自身的实例来调用setBeanFactory()
5、如果存在与bean关联的任何beanPostProcessors,则调用preProcessBeforeInitialization
6、如果bean指定了init方法(init-method属性),那么将调用它
7、最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。
8、如果 bean 实现DisposableBean 接口,当 spring 容器关闭时会调用destory()
9、如果bean指定了destroy方法(destroy-methon属性),那么将调用他

@RestController vs @Controller

Controller 返回一个页面
单独使用 @Controller 不加 @ResponseBody的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况
@RestController 返回JSON 或 XML 形式数据
但@RestController只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。

谈谈自己对于 Spring IoC 和 AOP 的理解

IOC
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

AOP
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理.
当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。
使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

Spring AOP 和 AspectJ AOP 有什么区别?

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。

Spring 中的单例 bean 的线程安全问题了解吗?

是有存在安全问题的,因为当多个线程进行操作同一个对象的时候,对这个对象的成员变量的写操作会存在线程安全问题。但是在一般情况下,我们使用的controller、service、Dao这些bean是无状态的,无状态的bean不能保存数据,因此是线程安全的。解决方案:

  1. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
  2. 改变 Bean 的作用域为 “prototype”:每次请求都会创建一个新的 bean 实例,自然不会存在线程安全问题。

@Component 和 @Bean 的区别是什么?

1、作用对象不同:@component注解用于子类,而@Bean注解作用于方法
2、@Component通常是通过类路径扫描来自动侦测以及自动装配到spring容器中;@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
3、@Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
@Bean注解使用示例:

@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}

}
上面的代码相当于下面的 xml 配置

下面这个例子是通过 @Component 无法实现的。

@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}

将一个类声明为Spring的 bean 的注解有哪些?

我们一般使用 @Autowired 注解自动装配 bean,要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类,采用以下注解可实现:

1、@Component :通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用@Component 注解标注。
2、@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
3、@Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
4、@Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

spring 事务

 需要格外注意的是:事务能否生效数据库引擎是否支持事务是关键。比如常用的 MySQL 数据库默认使用支持事务的innodb引擎。但是,如果把数据库引擎变为 myisam,那么程序也就不再支持事务了!
 我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。

事务的特性(ACDI)

1)原子性(atomicity):事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节,事务在执行过程中发生错误,会被回滚到事务开始前的状态
2)一致性(Consistency):在事务开始之前和事务借宿之后,数据库的完整性没有被皮怀。
3)隔离性(Isolation):数据允许多个并发事务同事对其数据进行读写和修改能力,隔离性可以防止多个事务并发执行
4)持久性:事务处理结果后,对数据的修改就是永久的,即使系统故障也不会丢失

管理事务的方式有几种

1、编程式事务,在代码中硬编码。(不推荐使用)
2、声明式事务,在配置文件中配置(推荐使用)基于@Transactional 的全注解方式使用最多
声明式事务又分为两种:
1、基于XML的声明式事务
2、基于注解的声明式事务

Spring 事务管理接口介绍

Spring 框架中,事务管理相关最重要的 3 个接口如下:
PlatformTransactionManager: (平台)事务管理器,Spring 事务策略的核心。
TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
TransactionStatus: 事务运行状态
我们可以把 PlatformTransactionManager 接口可以被看作是事务上层的管理者,而 TransactionDefinition 和 TransactionStatus 这两个接口可以看作是事务的描述。
PlatformTransactionManager 会根据 TransactionDefinition 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 ,而 TransactionStatus 接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。

PlatformTransactionManager:事务管理接口

Spring 并不直接管理事务,而是提供了多种事务管理器 。Spring 事务管理器的接口是: PlatformTransactionManager 。
通过这个接口,Spring 为各个平台如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
PlatformTransactionManager接口中定义了三个方法:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionDefinition:事务属性

主要包含:隔离级别、传播行为、回滚规则、是否只读、事务超时。

TransactionStatus:事务状态

public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事务
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}

Spring 事务中的隔离级别有哪几种?

TransactionDefinition 接口中定义了五个表示隔离级别的常量:
1、TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
2、TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
3、TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
4、TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
5、TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

题外:
MySQL InnoDB的REPEATABLE-READ(可重读)并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是 Next-Key Locks。因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) ,但是你要知道的是InnoDB 存储引擎默认使用 REPEAaTABLE-READ(可重读) 并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别

事务的超时属性

所谓的事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但是事务还没有完成则会自动回滚事务,默认值为-1,单位是秒。

事务的回滚规则

这些规则定义了哪些异常会导致事务回滚而哪些不会。默认请款下,事务只有遇到运行时异常才会回滚,ERROR也会导致事务回滚,但是遇到检查型异常时不会回滚。
如果你想要回滚你定义的特定的异常类型的话,可以这样:
@Transactional(rollbackFor= MyException.class)

事务中哪几种事务传播行为?

为了解决业务层方法之间互相调用的事务问题。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
例如:我们在 A 类的aMethod()方法中调用了 B 类的 bMethod() 方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 bMethod()如果发生异常需要回滚,如何配置事务传播行为才能让 aMethod()也跟着回滚呢?这个时候就需要事务传播行为的知识了
支持当前事务的情况:
1、TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
2、TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
3、TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
1、TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
2、TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
3、TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

@Transactional(rollbackFor = Exception.class)注解了解吗?

1) @Transactional 的作用范围
1、方法:推荐奖注解使用在方法上,不过需要注意的是,该注解智能用在public方法上,否则不生效
2、类:如果注解使用在类上,表明针对该类中所有的public方法都生效。
3、接口:不推荐在接口上使用
2) @Transactional 的常用配置参数
在这里插入图片描述

我们知道:Exception分为运行时异常RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

当@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。

3)@Transactional 事务注解原理 (面试会问到)
@Transactional的工作机制是AOP实现的,AOP又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用JDK的动态代理,如果目标对象没有实现接口,会使用CGLIB动态代理。
其中createAopProxy() 方法 决定了是使用 JDK 还是 Cglib 来做动态代理。
如果一个类或者方法上被标注@Transactional注解的话,spring容器就会在启动的时候为其创建一个代理类,在调用被注解的方法的时候,实际上调用的是TransactionInterceptor类中的invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的就回滚时候,方法调用完成之后提交事务
4)spring AOP自调用问题
同一个类中没有被@Transactional注解的方法内部调用有@Transactional调用没有注解的,注解方法的事务会失效,这是由于spring AOP代理的原因造成的,因为只有当注解的方法在类以外被调用的时候,spring事务管理才生效。
解决这种方法就是避免同一个类中自调用或者使用AspectJ取代spring AOP代理。

如何使用JPA在数据库中非持久化一个字段?

假如我们有有下面一个类:

Entity(name=“USER”)
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Long id;

@Column(name="USER_NAME")
private String userName;

@Column(name="PASSWORD")
private String password;

private String secrect;

}
如果我们想让secrect 这个字段不被持久化,也就是不被数据库存储怎么办?我们可以采用下面几种方法:

1)static String transient1; // not persistent because of static
2)final String transient2 = “Satish”; // not persistent because of final
3)transient String transient3; // not persistent because of transient
4)@Transient
String transient4; // not persistent because of @Transient
一般使用后面两种方式比较多,我个人使用注解的方式比较多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值