赞
踩
本文章主要用于记录博主准备面试的题目记录,有哪个题目有问题或者解释不清的,还请及时下方留言,感谢支持!!!
①、首先 ORM 为:
- 对象关系映射模式,为了解决面向对象和关系数据库存在互不匹配的现象的技术,它是通过使用描述对象和数据库之间的关系的元数据,将程序中的对象自动持久化存储到数据库中。至于如何实现持久化,一种简单的方式就是通过硬编码的方式,为每一种数据库访问操作提供单独的方法
此种方法有以下不足:
Ⅰ、持久层缺乏弹性,不能响应需求的变化,修改变化随之就需要修改持久层的接口
Ⅱ、持久层同时和域模型与关系数据库模型绑定,一旦域模型或者是关系数据库模型改变,毒药修改持久层的代码,增加软件的维护成本
- ORM 提供了另外一种实现持久化的模式,它采用映射元数据的方式来描述对象关系的映射。使得任意 ORM 中间件能在任何一个业务逻辑层和数据持久层起到桥梁的作用
- ORM 基于三个核心原则:简单、传达性、精确性,其优点是:提高开发效率,由于 ORM 可以自动对对象和数据库中的 table 表中的字段进行映射,所以不需要一个庞大的数据访问层,同时可以像操作对象一样从数据库中获取数据。同时其也存在缺点,ORM 会牺牲程序效率和固有思维模式,采用 ORM 的系统一般都是多层系统,系统的层数多了,效率就会降低。ORM 是一种完全面向对象的做法,而面向对象也会对性能产生一定的影响
②、Java 中常见的 ORM 框架:
Ⅰ、MyBatis 半自动的 ORM 框架,需要手动去实现 sql,再由框架根据 sql 及 sql 传入的数据来组装为要执行的 sql。其优点为:
- ①、程序员手动写 sql 学习成本低
- ②、更方便做 sql 性能的优化和维护
- ③、对关系型数据库模型要求不高,这样在做数据库调整时,影响不会太大,适合比较频繁变动需求的系统,因此国内大部分系统都是使用的半自动的 ORM 框架
- Mybatis 的缺点:不能跨数据库,因为写的 sql 存在某种数据库的特有的语法或关键词
Ⅱ、Hibernate 全自动的 ORM 框架,不需要手动实现 sql ,调用框架提供的 API,可以自动将对象组装为要执行的 sql,其优点为:
- ①、可以跨数据库
- ②、全自动 ORM 框架,自动组装为 sql 语句
- 其缺点为:学习门槛高,要学习框架 API 与 sql 的转换关系
- 对数据库的模型依赖非常大,在软件需求的频发变更中,会导致维护的难度高,很难定位问题,也很难进行性能优化,需要精通框架,对数据库的框架需要十分熟悉
Spring 容器是对 Ioc 设计模式的实现,使用统一的容器去存储 Bean 对象,及管理对象的依赖关系,创建容器的 API 主要是 BeanFactory 和 ApplicationContext 两种:
①、整合了 bean 的生命周期管理
②、国际化功能
③、载入多个上下文关系(存在继承关系),使得上下文都专注于一个特定的层次,比如应用的 web 层
④、事件发布响应机制
⑤、AOP
两种 API 创建容器的方式:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
即是在依赖对象的时候被反转了,控制反转之后,获取依赖对象的过程由自身管理转换为由 Ioc 容器主动注入,所以在 Ioc 容器在运行时,动态的将依赖关系,注入到对象之中被称为:“依赖注入”(DI)
1、属性注入:(此示例是在 userController 类中注入一个 userService 类)
@Autowired
private UserService userService;
2、setter 注入:
@Autowired // 必须要添加
public void setUserService(UserService userService) {
this.userService = userService;
}
3、构造方法注入:
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
4、三种注入方式的优缺点:
- ①、属性注入:简洁方便使用,只能用于 Ioc 容器,并且在使用时才会报出空指针异常
- ②、构造方法注入:spring 官方的推荐注入方式,在使用时一定保证注入的类不能为空,多个注入会显得程序比较臃肿
- ③、setter 注入通用性不如构造方法注入
5、两种注入方式: @Resource / @Autowired 二者区别:
- Autowired 来自 spring、Resource 来自 JDK
- 作用范围不同:Resource 不能进行构造方法注入,Autowired 三种注入方法都可以
- Resource 支持更多参数的设置
单例 bean 存在线程安全问题,当多线程同时操作一个同一对象时,对这个对象的非静态成员变量的写操作会存在线程安全问题。常见的有两种解决方法:
关于 ThreadLocal:
①、ThreadLocal 是什么
ThreadLocal 为本地线程变量,其中填充的是当前线程的变量,该变量对于其他线程来说是封闭且隔离的。ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问到自己的副本变量
1、在进行对象跨层传递时,使用 ThreadLocal 可以避免多次传递,打破层次间的约束
2、线程间数据隔离
3、进行事物操作,用于存储线程事物信息
4、数据库连接,session 会话管理
②、ThreadLocal怎么用
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<>();
for (int i = 0; i < 10; i++) {
int finalI = i;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
local.set(Thread.currentThread().getName() + ":" + finalI);
System.out.println(Thread.currentThread().getName() + ":" + finalI);
}
});
thread.start();
}
}
线程:Thread-0,local:Thread-0:0
线程:Thread-1,local:Thread-1:1
线程:Thread-2,local:Thread-2:2
线程:Thread-3,local:Thread-3:3
线程:Thread-4,local:Thread-4:4
线程:Thread-5,local:Thread-5:5
线程:Thread-6,local:Thread-6:6
线程:Thread-7,local:Thread-7:7
线程:Thread-8,local:Thread-8:8
线程:Thread-9,local:Thread-9:9
③、ThreadLocal源码分析
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { //首先获取当前线程对象 Thread t = Thread.currentThread(); //获取线程中变量 ThreadLocal.ThreadLocalMap ThreadLocalMap map = getMap(t); //如果不为空, if (map != null) map.set(this, value); else //如果为空,初始化该线程对象的map变量,其中key 为当前的threadlocal 变量 createMap(t, value); } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ //初始化线程内部变量 threadLocals ,key 为当前 threadlocal void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
2、get 方法:同过 ThreadLocal (当前的线程)作为 key 去查询 value 值
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
④、ThreadLocal 内存泄漏问题:先看下面的类
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
⑤、为什么 key 为弱引用
附:强弱引用
- BeanFactory 是 spring 中的顶级接口,所有的 bean 对象都是通过 BeanFactory 进行管理的。
- FactoryBean 是实例化一个 bean 对象的工厂类实现了 FactoryBean 接⼝的 Bean 根据该 bean 的 id 从BeanFactory中获取的实际上是FactoryBean中 getObject() ⽅法返回的对象,⽽不是 FactoryBean本身,如果要获取FactoryBean对象,请在id前⾯加⼀个 & 符号来获取
@Component
public class A{
@Autowired
private B b;
}
@Component
public class B{
@Autowired
private A a;
}
- 实例化 Bean
- 依赖注入:装配 Bean 的属性
- 初始化:执行各种 aware 方法,执行初始化前置方法,执行初始化方法,执行初始化后置方法
- 使用 bean
- 销毁 bean
首先实例化 A 对象,将其放入到三级缓存中,依赖注入 B,在缓存中寻找 B 发现是找到不的,此时的 A 是一个半成品,将 A 放入到 二级缓存中,去实例化 B ,将 B 放入到三级缓存,后依赖注入 A 可以在二级缓存中找到 A,那么将此时的 A 对象指向二级缓存中的 A 的地址,虽然 A 是一个半成品,但是我们仅仅只是把引用指向了缓存中的 A ,后 B 再进行初始化,将 B 放入到一级缓存中,最后进行 A 对象可以继续向下执行,在一级缓存中找到了 B ,后再执行 A 的初始化方法,将 A 放入到一级缓存中,此时循环依赖的问题就被解决了
答案是不行的,这三个缓存中存储的内容是不一样的,当一个 Bean 开始实例化时,是存放在三级缓存中的,当加载到一半时,就会放到二级缓存,将三级缓存的数据清除,而二级缓存又被分为两部分,二级缓存这里多了 AOP 的问题,导致二级缓存变复杂了,如果没有 AOP 这一环节,二三级是可以合并在一起的。最后一个完整的 bean 对象肯定是需要一个单独的缓存来进行存储,所以三级缓存是必要的,下面的是更细一点的循环依赖的解决:
AOP :面向切面编程,对于多个业务逻辑横切来实现统一的代码管理,而不用切入代码本身,这样面向切面编程的思想就被称为 AOP(一种思想),AOP 是对某一类事件进行处理,让后面程序没有后顾之忧去写程序,比如博客查询,你就单纯去写查询博客的代码,不用考虑登录情况检查,直接 AOP 解决
Ⅰ、切面:定义 AOP 针对哪一个的功能的,这个功能就叫做一个切面,比如用户登录,方法日志统计,切面是由切点和通知组成
Ⅱ、连接点:可以触发 AOP 的点,就是连接点,需要被增强的某个 AOP 功能的所有方法
Ⅲ、切点:定义 AOP 拦截规则
Ⅳ、通知:规定 AOP 执行的时期和执行的方法
实现步骤:
Ⅰ、引入 Spring-AOP 依赖
Ⅱ、定义切面
使用场景:日志记录,异常控制,事务管理,安全控制,性能统计
优点:代码解耦合,统一功能业务对代码没有入侵,可扩展能力强,灵活性高
springAOP 是采取动态代理的方式具体是基于 jdk 和 CGLIB 两种:
SpringAOP 如何使用,@Aspect 定义切面,point 定义切点方法后,可以对目标方法进行拦截
代码实例:定义切面、定义切点、定义通知
@Aspect @Component public class UserAspect { @Pointcut("execution(* com.example.demo.controller.UserController.*(..))") public void pointcut(){} @Before("pointcut()") public void doBefore(){ System.out.println("执行前置方法"); } @After("pointcut()") public void doAfter(){ System.out.println("执行后置方法"); } @AfterReturning("pointcut()") public void doAfterReturning(){ System.out.println("执行 AfterReturning"); } @AfterThrowing("pointcut()") public void doAfterThrowing(){ System.out.println("执行 AfterThrowing"); } @Around("pointcut()") public Object doAround(ProceedingJoinPoint joinPoint){ Object o = null; System.out.println("执行 Around"); StopWatch stopWatch = new StopWatch(); stopWatch.start(); try{ o = joinPoint.proceed(); }catch (Throwable throwable) { throwable.printStackTrace(); } stopWatch.stop(); System.out.println("Around 执行结束"); System.out.println("执行时间:" + ); return o; } }
@RestController
public class UserController {
@RequestMapping("/sayhi")
public String sayHi(){
System.out.println("你好世界");
return "你好世界";
}
}
Spring 事物一共五种隔离级别:
在 TransactionDefinition 中定义了七种表示事物传播行为的常量
支持当前事物的情况:
不支持当前事务:
其他情况:
- 上述流程返回的是一个静态页面,还有一种执行模式,缺少 8 ~ 10 的步骤,返回的就是一个 普通的数据。简单来说就是一个没有使用 @ResponseBody 或 @RestController 注解,项目默认返回的是一个静态页面,反之,使用了这两个注解之一,返回的就是一个普通的数据。
①、一对一的情况:例如 CSDN 的博客一篇博客对应的就是一个作者,这就是一对一的情况
使用 association 标签,用户表和文章表分别在 xml 文件中映射自己的字段,对于此处 一对一的情况,是在文章实体类中存在 user 属性,对于 user 属性的映射采用 association 标签,其中有三个属性:property 指定文章实体类中的属性名、resultMap :指定关联的结果集,基于该结果集组织用户数据,columnPrefix 为了防止两张表同名字段的覆盖,绑定一对一对象
<resultMap id="BaseMap" type="com.example.demo.model.Ariticle"> <id column="id" property="id"></id> <result column="title" property="title"></result> <result column="content" property="content"></result> <result column="createtime" property="createtime"></result> <result column="updatetime" property="updatetime"></result> <result column="uid" property="uid"></result> <result column="rcount" property="rcount"></result> <result column="state" property="state"></result> <association property="user" resultMap="com.example.demo.mapper.UserMapper.BaseMap" columnPrefix="u_"></association> </resultMap> <select id="getArticleById" resultMap="BaseMap"> select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on a.uid=u.id where a.id=#{id} </select>
②、一对多的情况:反过来看,当一篇文章对应一个作者,那么一个作者就可以对应多篇文章,这就是一对多的关系
对于 一对多 的情况使用的是 collection 标签,用户实体类的属性一一进行映射,ariticles 属性需要使用到 collection 对应的关联的结果集是:com.example.demo.mapper.AriticleMapper.BaseMap,使用方式大体和一对一的使用方式一致
<resultMap id="BaseMap" type="com.example.demo.model.User"> <!-- 主键映射 --> <id column="id" property="id"></id> <!-- 普通属性映射 --> <result column="username" property="username"></result> <result column="password" property="password"></result> <result column="createtime" property="createtime"></result> <result column="updatetime" property="updatetime"></result> <result column="state" property="state"></result> <collection property="ariticles" resultMap="com.example.demo.mapper.AriticleMapper.BaseMap" columnPrefix="a_"> </collection> </resultMap> <select id="getUserAndArticleByUid" resultMap="BaseMap"> select u.*,a.id a_id,a.title a_title,a.content a_content, a.createtime a_createtime, a.updatetime a_updatetime from userinfo u left join articleinfo a on u.id=a.uid where u.id=#{uid} </select>
SpringBoot 能够自动配置,只需要了解下面几个点:
- SpringBoot 能够自动配置,主要是因为 @SpringBootApplication 这个注解
- @SpringBootApplication 的分支 @EnableAutoConfiguration(开启自动导入配置),其里面有一个 @Import(AutoConfigurationPackages.Registrar,class)【自动配置包下所有的Bean定义和注册】注解,它会将需要自动装配的所有的Bean全部装配起来。
问题来了:哪些类是需要自动装配呢?
如上图,spring factories 里面就配置了(标识了)哪一些需要配置的类
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。