20300905记:如今这种大环境下,自己处于裸辞跳槽的情形,把自己总结的面试题记录一下,内心苦闷啊,直接上正文了。
1、单例和多例:
在spring容器中,默认情况下bean是单例的。
优点是:bean对象被创建出来以后,就缓存在内存中,使用的时候直接从内存获取,节约存储空间。而且单例的bean是由spring容器来维护的,spring容器启动的时候被创建,spring容器关闭的时候被销毁。
缺点是:在多线程模式下,会导致线程的非安全问题。
2、Spring中单例和多例的区别?
默认情况下,spring中创建的对象都是单例,并且维护其生命周期。单例对象的生命周期与spring容器共命运,同生共死。
但如果对象是多例的,那么Spring容器只负责对象的创建,不负责维护其生命周期,也就是说如果容器关闭,对象并未销毁,需要用户自行关闭。
3、懒加载和立即加载:
单例的加载:
默认情况下,bean的加载方式都是立即加载,也就是在spring容器启时就加载所有的bean,那么如果bean比较多,会影响系统的性能,我们可以修改bean加载方式为懒加载,懒加载可以在使用到bean的时候再加载bean,在一定程度上可以提高性能。 多例的加载:
多例的默认加载方式就是懒加载。只有在程序中要使用这个多例bean的时候才会进行加载。
立即加载只对单例有效,即所多例的加载方式配置为立即加载,它不会立即加载。
4、Spring的初始化方法:
Spring可以创建bean对象,bean对象创建完成会,如果在执行实例方法之前想要做一些bean的预处理工作,可以为这个bean配置初始化方法。init-method
5、SpringDI注入:
set注入又分: a) 单值注入 b) 对象的注入 c) 集合的注入 集合的间接注入: springEL表达式注入:
构造方法的注入
6、Spring如何处理线程并发问题?
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声名为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
7、Spring 框架中都用到了哪些设计模式?
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被自动更新,如Spring中listener的实现–ApplicationListener。
8、Spring框架中有哪些不同类型的事件?
Spring 提供了以下5种标准的事件:
(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。
9、@AutoWired注解进行自动配置,默认是按类型进行自动装配。
问题 ?如果和当前要装配的属性的类型相同的bean有多个,这时它的装配对象会谁?这时需要使用到@Qualifier注解来明确的指明要装配哪个bean
注意:@Qualifier注解不能单独使用,需要和@AutoWired结合使用。
@Resource 该注解是jdk提供的一个注解,也可以实现bean的自动装配,默认是按名称(byName)自动装配,当不能查找到名称相同的bean时会继续查找有没有类型相配的bean,如果有,则装配成功。如果相同类型的bean有多个,@Resource注解也支持指定装配的bean的名字。
10、@AutoWired和@Resource注解的区别?
一、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
二、@Autowired默认按类型装配(属业spring),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。
三、@Resource,默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
11、JDK动态代理:JDK动态代理的三要素:
动态代理三要素:proxy代理类,InvocationHandler接口,Invoke方法。 在静态代理的基础上创建一个动态代理类:
public class DynamicProxy {
/**
*
* @param target 被代理的目标对象
* @param tx 增强类
* @return
*/
public static Object getProxyObject(Object target,TransactionManager tx) {
//参数1 loader 是一个ClassLoader 是target的一个类加器
//参数2 interfaces 是一个接口 是target的接口
//JDK动态代理要求目标对象的类型必须是实现了接口的一个类,否则无法为其创建代理
//参数3 是一个执器处理器就是一个接口(策略模式)
Object proxy=Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
//当目标对象的方法被调用的时候会回调用该方法。
//proxy是代理对的引用
//method目标对象的方法的反射对象
//args目标对象的方法的参数的列表
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;//目标对象方法的返回值类型
//进行功能的增强
//监控方法的执行的性能
//获得一个系统的时间
long start=System.currentTimeMillis();
long end=0;
try {
tx.begin();
result=method.invoke(target, args);
tx.commit();
end=System.currentTimeMillis();
} catch (Exception e) {
end=System.currentTimeMillis();
tx.rollback();
}
System.out.println("方法耗时:"+(end-start));
return result;
}
});
return proxy;
}
}
CGLIB动态代理
public class DynamicProxy {
public static Object getProxyObject(
Object target,TransactionManager txm) {
//创建一个增强器
Enhancer enhancer=new Enhancer();
//设置代理对象明的类型
enhancer.setSuperclass(target.getClass());
enhancer.setInterfaces(target.getClass().getInterfaces());
//设置一个回调接口
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result=null;
//获得系统时间
long start=System.currentTimeMillis();
long end=0;
try {
txm.begin();
result=method.invoke(target, args);
txm.commit();
end=System.currentTimeMillis();
} catch (Exception e) {
end=System.currentTimeMillis();
txm.rollback();
}
System.out.println("消耗的时间:"+(end-start));
return result;
}
});
//返回代理对象
return enhancer.create();
}
}
12、jdk动态代理和cglib动态代理的区别?
Jdk动态代理是由java jdk提供的,目标对象必须是实现了接口的一个对象。本质上所产生的代理对象自身也会实现目标对象所实现的接口。
Cglib可以为没有实现接口的实现类创建代理对象,本质上是创建了一个目标对象的子类的对象。Cglib动态代理要求目标类不能是一个final类。
13、使用@Autowired注解自动装配的过程是怎样的?
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。
在启动spring IOC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean: 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 如果查询的结果不止一个,那么@Autowired会根据名称来查找; 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
14、@Component, @Controller, @Repository, @Service 有何区别?
@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中。
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IOC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。