四、JVM
1. Java的内存模型以及GC算法
答:
1)内存空间中可以根据是否线程共享分为两块,线程共享的是方法区和堆,线程独享的是JAVA栈,本地方法栈和PC寄存器。
其中方法区指的Perm永久代,永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。堆分为New年轻代和Tenured年老代,年轻代主要存放刚JVM分配出来的类实例对象。年老代主要是年轻代经过垃圾回收没有收掉的对象被Copy到年老代。年轻代可以细分为Eden(jvm刚分配出来的对象),survivor1,survivor2(两个survivor一样大,当eden中的对象经过垃圾回收没有被回收掉时,会在两个survivor之间来回copy,当满足一定条件,比如copy次数,就会被Copy到Tenured)
2)分代收集算法、复制算法、标记-清理或标记-整理算法。
2. jvm性能调优都做了什么
答:
从以下几个方面进行:
1)线程池:解决用户响应时间长的问题
大多数JVM6上的应用采用的线程池都是JDK自带的线程池.
Java线程池有几个重要的配置参数:
corePoolSize:核心线程数(最新线程数)
maximumPoolSize:最大线程数,超过这个数量的任务会被拒绝,用户可以通过RejectedExecutionHandler接口自定义处理方式
keepAliveTime:线程保持活动的时间
workQueue:工作队列,存放执行的任务
Java线程池需要传入一个Queue参数(workQueue)用来存放执行的任务,而对Queue的不同选择,线程池有完全不同的行为:
SynchronousQueue: 一个无容量的等待队列,一个线程的insert操作必须等待另一线程的remove操作,采用这个Queue线程池将会为每个任务分配一个新线程
LinkedBlockingQueue : 无界队列,采用该Queue,线程池将忽略 maximumPoolSize参数,仅用corePoolSize的线程处理所有的任务,未处理的任务便在LinkedBlockingQueue中排队
ArrayBlockingQueue: 有界队列,在有界队列和 maximumPoolSize的作用下,程序将很难被调优:更大的Queue和小的maximumPoolSize将导致CPU的低负载;小的Queue和大的池,Queue就没起动应有的作用。
其实我们的要求很简单,希望线程池能跟连接池一样,能设置最小线程数、最大线程数,当最小数<任务<最大数时,应该分配新的线程处理;当任务>最大数时,应该等待有空闲线程再处理该任务。
但线程池的设计思路是,任务应该放到Queue中,当Queue放不下时再考虑用新线程处理,如果Queue满且无法派生新线程,就拒绝该任务。设计导致“先放等执行”、“放不下再执行”、“拒绝不等待”。所以,根据不同的Queue参数,要提高吞吐量不能一味地增大maximumPoolSize。
当然,要达到我们的目标,必须对线程池进行一定的封装,幸运的是ThreadPoolExecutor中留了足够的自定义接口以帮助我们达到目标。我们封装的方式是:
以SynchronousQueue作为参数,使maximumPoolSize发挥作用,以防止线程被无限制的分配,同时可以通过提高maximumPoolSize来提高系统吞吐量
自定义一个RejectedExecutionHandler,当线程数超过maximumPoolSize时进行处理,处理方式为隔一段时间检查线程池是否可以执行新Task,如果可以把拒绝的Task重新放入到线程池,检查的时间依赖keepAliveTime的大小。
JVM启动参数:调整各代的内存比例和垃圾回收算法,提高吞吐量
在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标:
GC的时间足够的小
GC的次数足够的少
发生Full GC的周期足够的长
前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。
(1)针对JVM堆的设置一般,可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值
(2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
(3)年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,否则也就不会有调优。我们观察一下二者大小变化有哪些影响
更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率
如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间
(4)在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC ,默认为Serial收集
(5)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
(6)可以通过下面的参数打Heap Dump信息
-XX:HeapDumpPath
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/usr/aaa/dump/heap_trace.txt
通过下面参数可以控制OutOfMemoryError时打印堆的信息
-XX:+HeapDumpOnOutOfMemoryError
请看一下一个时间的Java参数配置:(服务器:Linux 64Bit,8Core×16G)
JAVA_OPTS=”$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/aaa/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize=1G -XX:MaxNewSize=1G”
3. 介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明
答:
方法区、堆、方法栈、本地方法栈、pc寄存器、直接内存
ps:在Java7之前,HotSpot虚拟机中将GC分代收集扩展到了方法区,使用永久代来实现了方法区。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。但是在之后的HotSpot虚拟机实现中,逐渐开始将方法区从永久代移除。Java7中已经将运行时常量池从永久代移除,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。而在Java8中,已经彻底没有了永久代,将方法区直接放在一个与堆不相连的本地内存区域,这个区域被叫做元空间。
1)Java 堆溢出
下面的程中我们限制Java 堆的大小为20MB,不可扩展(将堆的最小值-Xms 参数与最大值-Xmx 参数设置为一样即可避免堆自动扩展),通过参数
-XX:+HeapDump
可以让虚拟机在出现内存溢出异常OnOutOfMemoryError 时Dump 出当前的内存堆转储快照以便事后进行分析。
要解决这个区域的异常,一般的手段是首先通过内存映像分析工具(如EclipseMemory Analyzer)对dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots 相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及GC Roots 引用链的信息,就可以比较准确地定位出泄漏代码的位置。
如果不存在泄漏,换句话说就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx 与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗
List cases = new ArrayList();
while(true){
cases.add(new TestCase());
}
..
class TestCase{
}
2)java栈溢出
private int i ;
public void plus() {
i++;
plus();
}
3)常量池溢出
List<String> strings = new ArrayList<String>();
int i = 0;
while(true){
strings.add(String.valueOf(i++).intern());
}
4)方法区溢出
@VM args : -XX:PermSize=10M -XX:MaxPermSize=10M
while(true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestCase.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
return arg3.invokeSuper(arg0, arg2);
}
});
enhancer.create();
}
5)直接内存溢出
args: -Xmx20M -XX:MaxDirectMemorySize=10M
private static final int ONE_MB = 1024*1024;
private static int count = 1;
..
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
while (true) {
unsafe.allocateMemory(ONE_MB);
count++;
}
jvm 如何分配直接内存, new 对象如何不分配在堆而是栈上,常量池解析
答:
参考上面例子,Unsafe的 allocateMemory(ONE_MB)可以指定大小的直接内存。
数组多大放在 JVM 老年代(不只是设置 PretenureSizeThreshold ,问通常多大,没做过一问便知)
答:
当创建的对象超过指定大小时,直接把对象分配在老年代中。
-XX:PretenureSizeThreshold=3145728 参数设定超过对象超过多少时,分配到老年代中,此例为3M(3*1024*1024)。
要给一个类增加收尾(finalizer ),你只要定义finalize ( ) 方法即可。Java 回收该类的一个对象时,就会调用这个方法。在finalize ( )方法中,你要指定在一个对象被撤消前必须执行的操作。垃圾回收周期性地运行,检查对象不再被运行状态引用或间接地通过其他对象引用。就在对象被释放之前,Java 运行系统调用该对象的finalize( ) 方法。
finalize()方法的通用格式如下:
protected void finalize( )
{
// finalization code here
}
public class FinalizationDemo {
public static void main(String[] args) {
Cake c1 = new Cake(1);
Cake c2 = new Cake(2);
Cake c3 = new Cake(3);
c2 = c3 = null;
System.gc(); //Invoke the Java garbage collector
}
}
class Cake extends Object {
private int id;
public Cake(int id) {
this.id = id;
System.out.println("Cake Object " + id + "is created");
}
protected void finalize() throws java.lang.Throwable {
super.finalize();
System.out.println("Cake Object " + id + "is disposed");
}
}
C:\1>java FinalizationDemo
Cake Object 1is created
Cake Object 2is created
Cake Object 3is created
Cake Object 3is disposed
Cake Object 2is disposed
五、开源框架
1. hibernate和ibatis的区别
答:
Mybatis优势
MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
MyBatis容易掌握,而Hibernate门槛较高。
Hibernate优势
Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
hibernate:是一个标准的ORM框架(对象关系映射)。入门门槛较高,不需要写sql,sql语句自动生成,对sql语句进行优化、修改比较困难。
应用场景:适用需求变化不多的中小型项目,比如:后台管理系统,erp,orm,oa等
mybatis:专注sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输入映射,输出映射)
应用场景:适用需求变化较多的项目,比如:互联网项目。
2. 讲讲mybatis的连接池。
答:
MyBatis把数据源DataSource分为三种:
ž UNPOOLED 不使用连接池的数据源
ž POOLED 使用连接池的数据源
ž JNDI 使用JNDI实现的数据源
即:
相应地,MyBatis内部分别定义了实现了java.sql.DataSource接口的UnpooledDataSource,PooledDataSource类来表示UNPOOLED、POOLED类型的数据源。 如下图所示:
对于JNDI类型的数据源DataSource,则是通过JNDI上下文中取值。
3. spring框架中需要引用哪些jar包,以及这些jar包的用途
答:
除了spring.jar文件,Spring还包括有其它13个独立的jar包,各自包含着对应的Spring组件,用户可以根据自己的需要来选择组合自己的jar包,而不必引入整个spring.jar的所有类文件。
(1) spring-core.jar
这个jar文件包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。
(2) spring-beans.jar
这个jar文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean以及进行Inversion of Control / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI支持,引入spring-core.jar及spring-beans.jar文件就可以了。
(3) spring-aop.jar
这个jar文件包含在应用中使用Spring的AOP特性时所需的类。使用基于AOP的Spring特性,如声明型事务管理(Declarative Transaction Management),也要在应用里包含这个jar包。
(4) spring-context.jar
这个jar文件为Spring核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI所需的全部类,UI方面的用来与模板(Templating)引擎如Velocity、FreeMarker、JasperReports集成的类,以及校验Validation方面的相关类。
(5) spring-dao.jar
这个jar文件包含Spring DAO、Spring Transaction进行数据访问的所有类。为了使用声明型事务支持,还需在自己的应用里包含spring-aop.jar。
(6) spring-hibernate.jar
这个jar文件包含Spring对Hibernate 2及Hibernate 3进行封装的所有类。
(7) spring-jdbc.jar
这个jar文件包含对Spring对JDBC数据访问进行封装的所有类。
(8) spring-orm.jar
这个jar文件包含Spring对DAO特性集进行了扩展,使其支持 iBATIS、JDO、OJB、TopLink,因为Hibernate已经独立成包了,现在不包含在这个包里了。这个jar文件里大部分的类都要依赖spring-dao.jar里的类,用这个包时你需要同时包含spring-dao.jar包。
(9) spring-remoting.jar
这个jar文件包含支持EJB、JMS、远程调用Remoting(RMI、Hessian、Burlap、Http Invoker、JAX-RPC)方面的类。
(10) spring-support.jar
这个jar文件包含支持缓存Cache(ehcache)、JCA、JMX、邮件服务(Java Mail、COS Mail)、任务计划Scheduling(Timer、Quartz)方面的类。
(11) spring-web.jar
这个jar文件包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。
(12) spring-webmvc.jar
这个jar文件包含Spring MVC框架相关的所有类。包含国际化、标签、Theme、视图展现的FreeMarker、JasperReports、Tiles、Velocity、XSLT相关类。当然,如果你的应用使用了独立的MVC框架,则无需这个JAR文件里的任何类。
(13) spring-mock.jar
这个jar文件包含Spring一整套mock类来辅助应用的测试。Spring测试套件使用了其中大量mock类,这样测试就更加简单。模拟HttpServletRequest和HttpServletResponse类在Web应用单元测试是很方便的。
4. springMVC的原理
答:
5. springMVC注解的意思
答:
1)@Controller
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式:
(1)在SpringMVC 的配置文件中定义MyController 的bean 对象。
(2)在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
class="com.host.app.web.controller.MyController"/>
< context:component-scan base-package = "com.host.app.web" />//路径写到controller的上一层
2)@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
RequestMapping注解有六个属性,下面我们把她分成三类进行说明(下面有相应示例)。
1、 value, method;
value: 指定请求的实际地址,指定的地址可以是URI Template 模式;
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
2、consumes,produces
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3、params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
3)@Resource和@Autowired
@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入
4)@ModelAttribute和 @SessionAttributes
代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
@SessionAttributes即将值放到session作用域中
5)@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数
6)@requestParam
@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter(“name”),它有三个常用参数:defaultValue = “0”, required = false, value = “isApp”;defaultValue 表示设置默认值,required 铜过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型
7)@ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
8)@Component
相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议。
9)@Repository
用于注解dao层,在daoImpl类上面注解
6. spring中beanFactory和ApplicationContext的联系和区别
答:
在Srping Ioc容器中,有BeanFactory和ApplicationContext两个系列,分别是:
实现BeanFactory接口的简单容器,具备最基本功能。
实现ApplicationContext接口的复杂容器,具备高级功能。
7. spring注入的几种方式(循环注入)
1)Set注入
package com.bless.springdemo.action;
public class SpringAction {
//注入对象springDao
private SpringDao springDao;
//一定要写被注入对象的set方法
public void setSpringDao(SpringDao springDao) {
this.springDao = springDao;
}
public void ok(){
springDao.ok();
}
}
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<property name="springDao" ref="springDao">property>
bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl">bean>
2)构造器注入
这种方式的注入是指带有参数的构造函数注入
public class SpringAction {
//注入对象springDao
private SpringDao springDao;
private User user;
public SpringAction(SpringDao springDao,User user){
this.springDao = springDao;
this.user = user;
System.out.println("构造方法调用springDao和user");
}
public void save(){
user.setName("卡卡");
springDao.save(user);
}
}
在XML文件中同样不用的形式,而是使用标签,ref属性同样指向其它标签的name属性:
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<constructor-arg ref="springDao">constructor-arg>
<constructor-arg ref="user">constructor-arg>
bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl">bean>
<bean name="user" class="com.bless.springdemo.vo.User">bean>
3)静态工厂的方法注入
静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过”工程类.静态方法()”来获取对象,而是依然通过spring注入的形式获取
package com.bless.springdemo.factory;
import com.bless.springdemo.dao.FactoryDao;
import com.bless.springdemo.dao.impl.FactoryDaoImpl;
import com.bless.springdemo.dao.impl.StaticFacotryDaoImpl;
public class DaoFactory {
//静态工厂
public static final FactoryDao getStaticFactoryDaoImpl(){
return new StaticFacotryDaoImpl();
}
}
public class SpringAction {
//注入对象
private FactoryDao staticFactoryDao;
public void staticFactoryOk(){
staticFactoryDao.saveFactory();
}
//注入对象的set方法
public void setStaticFactoryDao(FactoryDao staticFactoryDao) {
this.staticFactoryDao = staticFactoryDao;
}
}
Spring的IOC配置文件,注意看指向的class并不是FactoryDao的实现类,而是指向静态工厂DaoFactory,并且配置 factory-method=”getStaticFactoryDaoImpl”指定调用哪个工厂方法
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" >
<property name="staticFactoryDao" ref="staticFactoryDao">property>
property>
bean>
<bean name="staticFactoryDao" class="com.bless.springdemo.factory.DaoFactory" factory-method="getStaticFactoryDaoImpl">bean>
4)实例工厂的方法注入
实例工厂的意思是获取对象实例的方法不是静态的,所以需要new工厂类,再调用普通的实例方法
public class DaoFactory {
//实例工厂
public FactoryDao getFactoryDaoImpl(){
return new FactoryDaoImpl();
}
}
public class SpringAction {
//注入对象
private FactoryDao factoryDao;
public void factoryOk(){
factoryDao.saveFactory();
}
public void setFactoryDao(FactoryDao factoryDao) {
this.factoryDao = factoryDao;
}
}
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<property name="factoryDao" ref="factoryDao">property>
bean>
<bean name="daoFactory" class="com.bless.springdemo.factory.DaoFactory">bean>
<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl">bean>
循环依赖
第一种:构造器参数循环依赖
Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持
在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出
BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
bean id="a" class="com.zfx.student.StudentA">
<constructor-arg index="0" ref="b">constructor-arg>
bean>
<bean id="b" class="com.zfx.student.StudentB">
<constructor-arg index="0" ref="c">constructor-arg>
bean>
<bean id="c" class="com.zfx.student.StudentC">
<constructor-arg index="0" ref="a">constructor-arg>
bean>
第二种:setter方式单例,默认方式
修改配置文件为set方式注入
如图中前两步骤得知:Spring是先将Bean对象实例化之后再设置对象属性的
<bean id="a" class="com.zfx.student.StudentA" scope="singleton">
<property name="studentB" ref="b">property>
bean>
<bean id="b" class="com.zfx.student.StudentB" scope="singleton">
<property name="studentC" ref="c">property>
bean>
<bean id="c" class="com.zfx.student.StudentC" scope="singleton">
<property name="studentA" ref="a">property>
bean>
我们结合上面那张图看,Spring先是用构造实例化Bean对象 ,此时Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法。 结合我们的实例来看,,当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题
第三种:setter方式原型,prototype
对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容
器不进行缓存,因此无法提前暴露一个创建中的Bean。
8. spring如何实现事物管理的
答:
事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败,四个基本要素是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
最为典型的就是银行转账的案例:
A要向B转账,现在A,B各自账户中有1000元,A要给B转200元,那么这个转账就必须保证是一个事务,防止中途因为各种原因导致A账户资金减少而B账户资金未添加,或者B账户资金添加而A账户资金未减少,这样不是用户有损失就是银行有损失,为了保证转账前后的一致性就必须保证转账操作是一个事务
实现编程式事务管理和声明式事务管理,其中声明式事务管理分别使用基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式、基于注解方式进行实现
首先,我们简单看一下Spring事务管理需要提及的接口,Spring事务管理高层抽象主要包括3个接口
PlatformTransactionManager :事务管理器(用来管理事务,包含事务的提交,回滚)
TransactionDefinition :事务定义信息(隔离,传播,超时,只读)
TransactionStatus :事务具体运行状态
Spring根据事务定义信息(TransactionDefinition)由平台事务管理器(PlatformTransactionManager)真正进行事务的管理,在进行事务管理的过程中,事务会产生运行状态,状态保存在TransactionStatus中
PlatformTransactionManager:
Spring为不同的持久化框架提供了不同的PlatformTransactionManager如:
在使用Spring JDBC或iBatis进行持久化数据时,采用DataSourceTransactionManager
在使用Hibernate进行持久化数据时使用HibernateTransactionManager
TransactionDefinition:
TransactionDefinition接口中定义了一组常量,包括事务的隔离级别,事务的传播行为,超时信息,其中还定义了一些方法,可获得事务的隔离级别,超时信息,是否只读。
传播行为主要解决业务层方法之间的相互调用产生的事务应该如何传递的问题。
TransactionDefinition中定义的属性常量如下:
Field(属性)
Description(描述)
ISOLATION_DEFAULT
使用底层数据存储的默认隔离级别
ISOLATION_READ_COMMITTED
表示防止脏读;可能会发生不可重复的读取和幻像读取
ISOLATION_READ_UNCOMMITTED
表示可能会发生脏读,不可重复的读取和幻像读取
ISOLATION_REPEATABLE_READ
表示禁止脏读和不可重复读;可以发生幻影读取
ISOLATION_SERIALIZABLE
表示可以防止脏读,不可重复的读取和幻像读取
PROPAGATION_MANDATORY
支持当前交易;如果不存在当前事务,则抛出异常
PROPAGATION_NESTED
如果当前事务存在,则在嵌套事务中执行,其行为类似于PROPAGATION_REQUIRED
PROPAGATION_NEVER
不支持当前交易;如果当前事务存在,则抛出异常
PROPAGATION_NOT_SUPPORTED
不支持当前交易;而是总是非事务地执行
PROPAGATION_REQUIRED
支持当前交易;如果不存在,创建一个新的
PROPAGATION_REQUIRES_NEW
创建一个新的事务,挂起当前事务(如果存在)
PROPAGATION_SUPPORTS
支持当前交易;如果不存在,则执行非事务性的
TIMEOUT_DEFAULT
使用底层事务系统的默认超时,如果不支持超时,则为none
TransationStatus:
在该接口中提供了一些方法:
Method
Description
flush()
将基础会话刷新到数据存储(如果适用):例如,所有受影响的Hibernate / JPA会话
hasSavepoint()
返回此事务是否内部携带保存点,也就是基于保存点创建为嵌套事务
isCompleted()
返回此事务是否完成,即是否已经提交或回滚
isNewTransaction()
返回当前交易是否是新的(否则首先参与现有交易,或者潜在地不会在实际交易中运行)
isRollbackOnly()
返回事务是否已被标记为仅回滚(由应用程序或由事务基础结构)
setRollbackOnly()
设置事务回滚
9. springIOC
10. spring AOP的原理
11. hibernate中的1级和2级缓存的使用方式以及区别原理(Lazy-Load的理解)
12. Hibernate的原理体系架构,五大核心接口,Hibernate对象的三种状态转换,事务管理。
六、多线程
1. Java创建线程之后,直接调用start()方法和run()的区别
2. 常用的线程池模式以及不同线程池的使用场景
3. newFixedThreadPool此种线程池如果线程数达到最大值后会怎么办,底层原理。
4. 多线程之间通信的同步问题,synchronized锁的是对象,衍伸出和synchronized相关很多的具体问题,例如同一个类不同方法都有synchronized锁,一个对象是否可以同时访问。或者一个类的static构造方法加上synchronized之后的锁的影响。
5. 了解可重入锁的含义,以及ReentrantLock 和synchronized的区别
6. 同步的数据结构,例如concurrentHashMap的源码理解以及内部实现原理,为什么他是同步的且效率高
7. atomicinteger和Volatile等线程安全操作的关键字的理解和使用
8. 线程间通信,wait和notify
9. 定时线程的使用
10. 场景:在一个主线程中,要求有大量(很多很多)子线程执行完之后,主线程才执行完成。多种方式,考虑效率。
11. 进程和线程的区别
12. 什么叫线程安全?举例说明
13. 线程的几种状态
14. 并发、同步的接口或方法
15. HashMap 是否线程安全,为何不安全。 ConcurrentHashMap,线程安全,为何安全。底层实现是怎么样的。
16. J.U.C下的常见类的使用。 ThreadPool的深入考察; BlockingQueue的使用。(take,poll的区别,put,offer的区别);原子类的实现。
17. 简单介绍下多线程的情况,从建立一个线程开始。然后怎么控制同步过程,多线程常用的方法和结构
18. volatile的理解
19. 实现多线程有几种方式,多线程同步怎么做,说说几个线程里常用的方法
七、网络通信
1. http是无状态通信,http的请求方式有哪些,可以自己定义新的请求方式么。
2. socket通信,以及长连接,分包,连接异常断开的处理。
3. socket通信模型的使用,AIO和NIO。
4. socket框架netty的使用,以及NIO的实现原理,为什么是异步非阻塞。
5. 同步和异步,阻塞和非阻塞。
6. OSI七层模型,包括TCP,IP的一些基本知识
7. http中,get post的区别
8. 说说http,tcp,udp之间关系和区别。
9. 说说浏览器访问www.taobao.com,经历了怎样的过程。
10. HTTP协议、 HTTPS协议,SSL协议及完整交互过程;
11. tcp的拥塞,快回传,ip的报文丢弃
12. https处理的一个过程,对称加密和非对称加密
13. head各个特点和区别
14. 说说浏览器访问www.taobao.com,经历了怎样的过程。
八、数据库MySql
1. MySql的存储引擎的不同
2. 单个索引、联合索引、主键索引
3. Mysql怎么分表,以及分表后如果想按条件分页查询怎么办(如果不是按分表字段来查询的话,几乎效率低下,无解)
4. 分表之后想让一个id多个表是自增的,效率实现
5. MySql的主从实时备份同步的配置,以及原理(从库读主库的binlog),读写分离
6. 写SQL语句。。。
7. 索引的数据结构,B+树
8. 事务的四个特性,以及各自的特点(原子、隔离)等等,项目怎么解决这些问题
9. 数据库的锁:行锁,表锁;乐观锁,悲观锁
10. 数据库事务的几种粒度;
11. 关系型和非关系型数据库区别
九、设计模式
1. 单例模式:饱汉、饿汉。以及饿汉中的延迟加载,双重检查
2. 工厂模式、装饰者模式、观察者模式。
3. 工厂方法模式的优点(低耦合、高内聚,开放封闭原则)
十、算法
1. 使用随机算法产生一个数,要求把1-1000W之间这些数全部生成。(考察高效率,解决产生冲突的问题)
2. 两个有序数组的合并排序
3. 一个数组的倒序
4. 计算一个正整数的正平方根
5. 说白了就是常见的那些查找、排序算法以及各自的时间复杂度
6. 二叉树的遍历算法
7. DFS,BFS算法
9. 比较重要的数据结构,如链表,队列,栈的基本理解及大致实现。
10. 排序算法与时空复杂度(快排为什么不稳定,为什么你的项目还在用)
11. 逆波兰计算器
12. Hoffman 编码
13. 查找树与红黑树
十一、并发与性能调优
1. 有个每秒钟5k个请求,查询手机号所属地的笔试题(记得不完整,没列出),如何设计算法?请求再多,比如5w,如何设计整个系统?
2. 高并发情况下,我们系统是如何支撑大量的请求的
3. 集群如何同步会话状态
4. 负载均衡的原理
5 .如果有一个特别大的访问量,到数据库上,怎么做优化(DB设计,DBIO,SQL优化,Java优化)
6. 如果出现大面积并发,在不增加服务器的基础上,如何解决服务器响应不及时问题“。
7. 假如你的项目出现性能瓶颈了,你觉得可能会是哪些方面,怎么解决问题。
8. 如何查找 造成 性能瓶颈出现的位置,是哪个位置照成性能瓶颈。
9. 你的项目中使用过缓存机制吗?有没用用户非本地缓存
十二、其他
常用的linux下的命令