Java面试04|Spring框架

1、动态代理的几种方式 

 Java主要有两种代理,JDK和Cglib动态代理。先看JDK代理实例如下:

JDK创建代理有一个限制,即它只能为接口创建代理实例。举个例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public  interface  Advice {
     void  beforeMethod();
     void  afterMethod();
}
 
public  class  TimeAdvice  implements  Advice {
     long  startTime;
     long  endTime;
 
     public  void  beforeMethod() {
         startTime = System.nanoTime();  // 获取开始时间
         System.out.println( "开始计算程序运行时间"  + startTime);
     }
 
     public  void  afterMethod() {
         endTime = System.nanoTime();  // 获取结束时间
         System.out.println( "计算程序运行时间: "  + (endTime - startTime) +  "ns" );
     }
}

 

1
2
3
4
5
6
7
8
public  interface  SalaryInterface { 
     public  void  doSalary(); 
}
public  class  Salary   implements  SalaryInterface{ 
     public  void  doSalary() { 
           System.out.println( "进行薪资计算的逻辑处理" ); 
      
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import  java.lang.reflect.InvocationHandler; 
import  java.lang.reflect.Method; 
import  java.lang.reflect.Proxy; 
/*
  * 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时,
  * 该方法会指派到调用处理器的invoke()中去。代理的方法封装成
  * invoke中的method对象,其中的参数封装成Object[].
  */ 
public  class  MyProxy  implements  InvocationHandler{ 
     
      private  Object obj;    // 希望被代理的对象 
      private  Advice advice; 
      
     // 绑定代理对象 
     public  Object bind(Object obj, Advice advice) { 
         this .obj = obj; 
         this .advice = advice; 
         return  Proxy.newProxyInstance( 
                 obj.getClass().getClassLoader(),  // 类加载器   
                 obj.getClass().getInterfaces(),   // 创建目标类所需要使用的一组接口 
                 this   // 一个实现InvocationHandler的实例,用来整合横切与业务逻辑 
             ); 
        
   
     /*
      * 实现代理
      * method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象
      */ 
     public  Object invoke(Object proxy, Method method, Object[] args)  throws  Throwable { 
         Object result =  null
         try 
             advice.beforeMethod(); 
             result = method.invoke(obj, args);  
             advice.afterMethod(); 
         catch  (Exception e){ 
             e.printStackTrace(); 
        
         return  result; 
    

 

1
2
3
4
5
6
7
8
9
10
11
12
13
public  class  Bootstrap {
 
     public  static  void  main(String[] args) {
         
         Advice advice =  new  TimeAdvice();  
         SalaryInterface p =  new  Salary(); 
         // Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象 
         MyProxy proxy =  new  MyProxy(); 
           
         SalaryInterface y = (SalaryInterface)proxy.bind(p, advice); 
         y.doSalary();  // 相当于调用proxy.invoke(proxy, "doSalary, null); 
     }
}

 

1
2
3
开始计算程序运行时间 43041762316001
进行薪资计算的逻辑处理
计算程序运行时间: 882610ns

  

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。  

CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。也就是生命周期长的实例用CGLib比较合适。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
  * 使用cglib动态代理
  */ 
public  class  Cglib  implements  MethodInterceptor { 
     private  Object target; 
     long  startTime;
     long  endTime;
   
     /**
      * 创建代理对象
      */ 
     public  Object getInstance(Object target) { 
         this .target = target; 
         Enhancer enhancer =  new  Enhancer(); 
         enhancer.setSuperclass( this .target.getClass());   // 设置需要创建子类的类
         enhancer.setCallback( this );    // 回调方法  
         return  enhancer.create();      // 通过字节码技术动态创建子类实例  
    
   
     @Override 
     // 回调方法 ,拦截所有的父类方法调用
     public  Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy)  throws  Throwable { 
         startTime = System.nanoTime();  // 获取开始时间
         System.out.println( "开始计算程序运行时间"  + startTime);
         Object result = proxy.invokeSuper(obj, args);    // 通过代码类调用父类中的方法
         endTime = System.nanoTime();    // 获取结束时间
         System.out.println( "计算程序运行时间: "  + (endTime - startTime) +  "ns" );
         return  result; 
    
    

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
  * 采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截
  * 所有父类方法的调用,并顺势织入横切逻辑
  *
  * singletom的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,比较适合用CGLib动态代理技术
  *
  * 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行代理
  */
public  class  TestCglib {
        
     public  static  void  main(String[] args) { 
         Cglib cglib= new  Cglib(); 
         Salary salary=(Salary)cglib.getInstance( new  Salary()); 
         salary.doSalary(); 
    
}

 

 

2、Spring AOP(Aspect Oriented Programming面向切面编程)与IoC(Inverse of Control控制反转)的实现

 Spring AOP主要就是通过动态代理来实现,而Ioc是通过反射来实现,将创建对象和对象之间的依赖管理交给IoC容器来做,完成对象之间的解耦。

 举个反射的例子,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public  class  Car {
     private  String brand;
     private  String color;
     private  int  maxSpeed;
 
     public  Car() {
         System.out.println( "init car!!" );
     }
 
     public  Car(String brand, String color,  int  maxSpeed) {
         this .brand = brand;
         this .color = color;
         this .maxSpeed = maxSpeed;
     }
 
     public  void  introduce() {
         System.out.println( "brand:"  + brand +  ";color:"  + color +  ";maxSpeed:"  + maxSpeed);
     }
 
     public  String getBrand() {
         return  brand;
     }
 
     public  void  setBrand(String brand) {
         this .brand = brand;
     }
 
     public  String getColor() {
         return  color;
     }
 
     public  void  setColor(String color) {
         this .color = color;
     }
 
     public  int  getMaxSpeed() {
         return  maxSpeed;
     }
 
     public  void  setMaxSpeed( int  maxSpeed) {
         this .maxSpeed = maxSpeed;
     }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  class  ReflectTest {
 
     public  static  Car initByDefaultConst()  throws  Throwable {
         // loadClass()方法必须使用全额限定名
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         // 每一个类在JVM中都拥有一个对应的java.lang.Class对象,用来描述类结构信息
//      Class clazz = loader.loadClass("reflect.Car");
         Class clazz = Class.forName( "reflect.Car" );
         Constructor cons = clazz.getDeclaredConstructor((Class[])  null );
         Car car = (Car) cons.newInstance();
 
         Method setBrand = clazz.getMethod( "setBrand" , String. class );
         setBrand.invoke(car,  "红旗CA72" );
         Method setColor = clazz.getMethod( "setColor" , String. class );
         setColor.invoke(car,  "黑色" );
         Method setMaxSpeed = clazz.getMethod( "setMaxSpeed" int . class );
         setMaxSpeed.invoke(car,  200 );
         return  car;
     }
 
     public  static  Car initByParamConst()  throws  Throwable {
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         Class clazz = loader.loadClass( "reflect.Car" );
         Constructor cons = clazz.getDeclaredConstructor( new  Class[] { String. class , String. class int . class  });
         Car car = (Car) cons.newInstance( new  Object[] {  "吉利TOPMIX" "绿色" 120  });
         return  car;
     }
 
     public  static  void  main(String[] args)  throws  Throwable {
          Car car1 = initByDefaultConst();
          Car car2 = initByParamConst();
          car1.introduce();
          car2.introduce();
     }
}

  

1
2
3
init car!!
brand:红旗CA72;color:黑色;maxSpeed: 200
brand:吉利TOPMIX;color:绿色;maxSpeed: 120

每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解甚至void都有对应的Class对象。可以从Class对象中获取构造函数、成员变量、方法等类元素的反射对象,并以编程的方式通过这些反射对目标类对象进行操作。

  

3、Spring事务的传播行为 

Spring中的事务完全基于数据库的事务,如果数据库引擎使用MyISAM引擎,那Spring的事务其实是不起作用的。另外,Spring为开发者提供的与事务相关的特性就是事务的传播行为,如下:

事务传播行为类型

说明

propagation_required

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(Spring默认的事务传播行为)

propagation_supports

支持当前事务,如果当前没有事务,就以非事务方式执行

propagation_mandatory(托管)

使用当前的事务,如果当前没有事务,就抛出异常

propagation_requireds_new

新建事务,如果当前存在事务,把当前事务挂起

propagation_not_supported

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

propagation_never

以非事务方式执行,如果当前存在事务,则抛出异常

propagation_nested

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作,也就是新建一个事务。父事务回滚,嵌套的事务也要回滚。

 

 Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

 Spring可以配置事务的属性,但是隔离级别、读写事务属性、超时时间与回滚设置等都交给了JDBC,真正自己实现的只有事务的传播行为。那么什么时候发生事务的传播行为呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public  class  ForumService {
     private  UserService userService;
 
     @Transactional (propagation = Propagation.REQUIRED)
     public  void  addTopic() {
         // add Topic
         this .updateTopic();
         userService.addCredits();
     }
     
     @Transactional (propagation = Propagation.REQUIRED)
     public  void  updateTopic() {
         // add Topic
     }
 
     public  void  setUserService(UserService userService) {
         this .userService = userService;
     }
}

看一下userService中的addCredits()方法,如下:

1
2
3
4
5
6
7
public  class  UserService {
     
     @Transactional (propagation = Propagation.REQUIRES_NEW)
     public  void  addCredits() {
         
     }
}

然后测试下:

1
forumService.addTopic();

开启了Spring4日志的DEBUG模式后,输出如下:  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- Returning cached instance of singleton bean  'txManager'
- Creating  new  transaction with name [com.baobaotao.service.ForumService.addTopic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;  ''
- Acquired Connection [org.apache.commons.dbcp.PoolableConnection @1948ea69 for  JDBC transaction
- Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection @1948ea69 ] to manual commit
- Suspending current transaction, creating  new  transaction with name [com.baobaotao.service.UserService.addCredits]
- Acquired Connection [org.apache.commons.dbcp.PoolableConnection @773e2eb5 for  JDBC transaction
- Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection @773e2eb5 ] to manual commit
- Initiating transaction commit
- Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection @773e2eb5 ]
- Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection @773e2eb5 ] after transaction
- Returning JDBC Connection to DataSource
- Resuming suspended transaction after completion of inner transaction
- Initiating transaction commit
- Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection @1948ea69 ]
- Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection @1948ea69 ] after transaction
- Returning JDBC Connection to DataSource

清楚的看到调用addCredis()方法时创建了一个新的事务,而在这个方法中调用addCredits()方法时,由于这个方法的事务传播行为为progation_required_new,所以挂起了当前的线程,又创建了一个新的线程。但是对于this.updateTopic()方法调用时,由于这个

方法的事务仍然为propagation_required,所以在当前线程事务中执行即可。

 

在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞,根据不同的数据库设计和性能要求进行所需要的隔离级别,才是最恰当的。具体以下方面需要特别注意:

A、 事务操作过程要尽量小,能拆分的事务要拆分开来

B、 事务操作过程不应该有交互(系统交互,接口调用),因为交互等待的时候,事务并未结束,可能锁定了很多资源

C、 事务操作过程要按同一顺序访问对象。(避免死锁的情况产生)

D、 提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。

E、 查询时可以用较低的隔离级别,特别是报表查询的时候,可以选择最低的隔离级别(未提交读)。

   

 4、spring是如何做到为普通类创建单例的呢?


参考:http://blog.csdn.net/arvinrong/article/details/7756167 


5、Spring MVC

 

SpringMVC设计思路:将整个处理流程规范化,并把每一个处理步骤分派到不同的组件中进行处理。

这个方案实际上涉及到两个方面:

l 处理流程规范化 —— 将处理流程划分为若干个步骤(任务),并使用一条明确的逻辑主线将所有的步骤串联起来

l 处理流程组件化 —— 将处理流程中的每一个步骤(任务)都定义为接口,并为每个接口赋予不同的实现模式

处理流程规范化是目的,对于处理过程的步骤划分和流程定义则是手段。因而处理流程规范化的首要内容就是考虑一个通用的Servlet响应程序大致应该包含的逻辑步骤:

l 步骤1—— 对Http请求进行初步处理,查找与之对应的Controller处理类(方法)   ——HandlerMapping

l 步骤2—— 调用相应的Controller处理类(方法)完成业务逻辑                    ——HandlerAdapter

l 步骤3—— 对Controller处理类(方法)调用时可能发生的异常进行处理            ——HandlerExceptionResolver

l 步骤4—— 根据Controller处理类(方法)的调用结果,进行Http响应处理       ——ViewResolver

正是这基于组件、接口的设计,支持了SpringMVC的另一个特性:行为的可扩展性。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值