Spring和Mybatis的面试总结

Spring和Mybatis的面试总结

1 基本概念

1.1 控制反转 IOC

1.1.1 原理

IOC是一种编程思想,由主动的编程变成被动的接收,最终实现直接修改xml文件就可以实现不同的操作

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用 Spring后,对象是由 Spring来创建的

反转:程序本身不创建对象,而变成被动的接收对象

依赖注入:就是利用set方法来进行注入的。

// 1. 以前如果我们需要在一个方法之中引用另一个类。
private UserDao userDao = new UserDaoImpl();//硬编码写死

//2.当用户的需求发生变化时,程序员就需要去修改new的对象,改变原来的代码。
// IOC控制反转的核心思想就是 通过set的方法,将需要的对象注入,实现不修改源代码的时候,让代码功能随用户需求而自动改变,实现代码之间的解耦
private UserDao userDao;
public void setUserDao(UserDao userDao) {
	this.userDao = userDao;
}
1.1.2 实现原理

工厂模式+ 反射机制

把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象

什么是工厂模式:

工厂设计模式,就是说建立一个工厂类,对实现了同一接口的子类们进行统一的实例创建方法,用来提供给用户调用.而用户无需去了解这些对象该如何创建以及如何组织.

1、抽象接口(酒)——接口的实现类(白酒、红酒、啤酒)

2、创建工厂,生成基于给定信息的实体类的对象,里面实现逻辑,但是向外界屏蔽了里面具体生产的过程

  • 如果A,创建返回白酒
  • 如果B,创建返回红酒
  • 如果C,创建返回啤酒

3、使用工厂,通过传递类型信息来获取实体类的对象。传值A,得到白酒类,这个传值可以通过反射来传

public class Test {
  public static void main(String[] args) {
	//1. 创建容器对象
	 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	//2.从容器中获取user对象
	 User user = (User) applicationContext.getBean("user");
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd ">

	<!-- 
		class属性:被管理对象的完整类名
		name属性:获得对象时根据name获得 尽量使用name属性
	 -->
	<bean name="user" class="com.java.User" ></bean>
</beans>

1.1.3 IOC容器

IOC中的Bean管理,Bean就相当于对象,IOC容器中存放着Spring通过注入创建的对象,然后我们再通过getBean把我们需要的对象取出来,实现了对象的创建。 IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。

什么时候创建了?

单例:在启动spring容器的时候创建对象,ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等。
在多例的情况下,getBean时才创建对象

BeanFactory和ApplicationContext

BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能

image-20211005194729755 image-20211005194742935
1.1.4 Spring的IOC注入方式
分为 基于xml的注入
		set
		构造器:index、bytype、name
		工厂注入:静态工厂、实例工厂
	 基于注解的注入
	 
二、依赖注入 DI
    - 依赖:bean对象的创建依赖于容器
    - 注入:bean对象中的所有属性,由容器来注入
        1.构造器注入
        2.set注入
            复杂类的属性注入,list、map、null、set。。。
        3.命名空间注入
            |-- 导入xml约束
                xmlns:p=“http://www.springframework.org/schema/p”
                xmlns:c=“http://www.springframework.org/schema/c”

            |-- p命名空间注入通过set注入,直接注入属性的值-》类似 property
            <bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
            </bean>

            |-- c命名空间,通过构造器注入,需要写入有参和无参构造方法-》类似 construct-args
            <bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>

三、自动装配
    Spring会在上下文自动寻找,并自动给bean装配属性
    |-- 隐式的自动装配bean 
            byType自动装配:自动查找和自己对象set方法参数的类型相同的bean
            <bean id="people" class="pojo.People" autowire="byType">
                <property name="name" value="cbh"></property>
            </bean>
            byName自动装配:自动查找和自己对象set对应的值对应的id

    |-- 使用注解实现自动装配
        1.导入context约束
        xmlns:context="http://www.springframework.org/schema/context"
        2. 配置注解的支持:< context:annotation-config/>,或者开启扫描
        3. 自动装配属性的注解方式
            @Autowired是先 byteType,如果唯一则注入,否则 byName查找
            @Autowired + @Qualifier = byType || byName
            @resource是先byname,不符合再继续byType

            public class People {
                @Autowired
                private Cat cat;
                @Autowired
                @Qualifier(value = "dog")
                private Dog dog;
                private String name;
            }

image-20211005195044459

1.2 AOP面向切面编程

不通过修改源代码方式,在主干功能里面添加新功能

1.2.1 什么是代理模式

代码业务已经实现,但是需要在原有的代码上增加新的功能,这个时候不能直接修改原有的代码。此时就通过代理的模式,相当于外面再包一层,附加上新的功能

1、核心功能:吃
2、为了扩展(增强)这个功能,前面增加洗手,后面增加收拾,再封装成新的类,就是代理类

再比如卖房子:
1、房主出房子,收钱
2、但是前前后后还有很多其他事,都由中介负责。这就是代理

1.2.2 实现原理

静态代理就是一开始就写好代理类,而动态代理则是在运行过程中动态的生成代理类。AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理

Spring也就是通过这两种方式来实现AOP,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib生成一个被代理对象的子类来作为代理

image-20211005195302936

  • 基于接口:JDK的动态代理
  • 基于类:cglib

jdk提供了一个API java.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事。

动态代理实现主要是实现InvocationHandler,并且将目标对象注入到代理对象proxy中,利用反射机制来执行目标对象的方法。

	public class UserDAOProxy implements InvocationHandler {
        //增强谁就要传入谁的对象,这里用有参构造函数实现传值
        private UserDAO userDAO = new UserDAOImpl();
        public UserDAOProxy(UserDAO userDAO) {
            this.userDAO = userDAO;
        }
        //创建代理类,返回代理对象,这个对象已经通过InvocationHandler实现增强
        public UserDAO creatProxy(){
            UserDAO daoProxy = (UserDAO) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(),userDAO.getClass().getInterfaces(),this);
            return daoProxy;
        }
        //InvocationHandler 重写invoke,实现功能的增强的调用
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            if("add".equals(method.getName())){
                System.out.println("动态代理实现方法的增强");
                return method.invoke(userDAO,objects);
            }
            return method.invoke(userDAO,objects);
        }
    }

    测试:
    public static void main(String[] args) {
        UserDAOImpl userDAO = new UserDAOImpl();
        UserDAO proxy = new UserDAOProxy(userDAO).creatProxy();
        proxy.add();
    }

实现

所有实现BeanPostProcessor接口的类,在初始化bean的时候都会调用这些类的方法,一般用于在bean初始化前或后对bean做一些修改。而AOP的功能实现正式基于此,在bean初始化后创建针对该bean的proxy,然后返回给用户该proxy。实际就是为bean创建一个proxy,JDKproxy或者CGLIBproxy,然后在调用bean的方法时,会通过proxy来调用bean方法

重点过程可分为:
1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中

2AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy

3)调用bean方法时通过proxy来调用,proxy依次调用增强器的相关方法,来实现方法切入
public interface Person {
	void say();
}
public class Student implements Person{
 
	public void say(){
		System.out.println("测试");
	}
}

创建切面类

@Aspect
public class AspectJTest {
 
	@Pointcut("execution(* *.say(..))")
	public void test(){}
	
	@Before("test()")
	public void before(){
		System.out.println("before test..");
	}
	
	@After("test()")
	public void after(){
		System.out.println("after test..");
	}
	
	@Around("test()")
	public Object around(ProceedingJoinPoint p){
		System.out.println("before1");
		Object o = null;
		try {
			o = p.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("after1");
		return o;
	}
}

bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
 
	<aop:aspectj-autoproxy/>
    <bean id="student" class="test.Student"/>
	<bean class="test.AspectJTest"/>
</beans>
1.2.3 相关概念

|-- 面向切面

  1. 连接点:类里面哪些方法可以被增强,这些方法称为连接点
  2. 切入点:实际被真正增强的方法,称为切入点
  3. 通知(增强)
    (1)实际增强的逻辑部分称为通知(增强)
    (2)通知有多种类型,既根据增强的逻辑位于方法的那个位置来区分
    前置通知:先于被增强方法执行的方法
    后置通知
    环绕通知
    异常通知
    最终通知:在连接点正常完成后执行的通知
  4. 切面是动作,表示整个过程
    (1)把通知应用到切入点过程

image-20211005194535644

1.3 spring中用到了哪些设计模式

  • 工厂模式:Spring使用工厂模式可以通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 单例模式: bean 的默认作用域是 singleto。Spring 通过 ConcurrentHashMap实现单例注册表的特殊方式实现单例模式。
  • 代理模式:AOP功能的实现
  • 模板方法模式:Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类。
  • 适配器模式:AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapterBeforeAdviceAfterAdvice,AfterReturningAdvice
  • 观察者模式ApplicationListener 充当了事件监听者角色

2 Bean的生命周期

​ (1)通过构造器创建 bean 实例(无参数构造)

​ (2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

​ (3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

​ (4)调用 bean 的初始化的方法(需要进行配置初始化的方法)

​ (5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

​ (6)bean 可以使用了(对象获取到了)

​ (7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。

bean的使用:

img

Spring Bean的生命周期只有这四个阶段。逻辑在doCreate()方法中,顺序调用以下三个方法

  1. 实例化 Instantiation——createBeanInstance()
  2. 属性赋值 Populate——populateBean()
  3. 初始化 Initialization——initializeBean()
  4. 销毁 Destruction——容器关闭时调用的
	// 忽略了无关代码
	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
       // Instantiate the bean.
       BeanWrapper instanceWrapper = null;
       if (instanceWrapper == null) {
           // 实例化阶段!
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }

       // Initialize the bean instance.
       Object exposedObject = bean;
       try {
           // 属性赋值阶段!
          populateBean(beanName, mbd, instanceWrapper);
           // 初始化阶段!
          exposedObject = initializeBean(beanName, exposedObject, mbd);
       }
   }

2.1 常用扩展点

2.1.1 第一大类:影响多个Bean的接口

实现了这些接口的Bean会切入到多个Bean的生命周期中。Spring内部扩展经常使用这些接口,例如自动注入以及AOP的实现

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。

InstantiationAwareBeanPostProcessor extends BeanPostProcessor
img
	@Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        try {
            // 该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点
            // postProcessBeforeInstantiation方法调用点,for循环调用所有的InstantiationAwareBeanPostProcessor
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        
        try {   
            // postProcessBeforeInstantiation方法在doCreateBean方法创建Bean之前调用
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        
    }

2.1.2 第二大类:只调用一次的接口
  1. Aware类型的接口作用就是让我们能够拿到Spring容器中的一些资源。所有的Aware方法都是在初始化阶段之前调用的
  2. 生命周期接口
  • InitializingBean 对应生命周期的初始化阶段,在invokeInitMethods(beanName, wrappedBean, mbd);方法中调用
  • DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。
无所不知的Aware
  1. BeanNameAware
  2. BeanClassLoaderAware
  3. BeanFactoryAware

image-20211005201903090

这里写图片描述

img

img

Mybatis

1、 #{}和${}的区别是什么?

  • ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver
  • #{}是 sql 的参数占位符,Mybatis 会将 sql 中的#{}替换为?号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),#{item.name} 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 param.getItem().getName()

2、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?

<resultMap><parameterMap><sql><include><selectKey>,加上动态 sql 的 9 个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中为 sql 片段标签,通过<include>标签引入 sql 片段,<selectKey>为不支持自增的主键生成策略标签。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值