当前位置: 首页 > news >正文

关于面试别问及Spring如何回答思路总结!

首先要知道 Spring两大核心IOC和AOP(Java轻量级业务层框架Spring两大核心IOC和AOP原理)

IOC:

1.从Java最基本的创建对象开始

  如Interface Driven Design接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:AInterface a =   new AInterfaceImp(); 这样一来,耦合关系就产生了,如:

classA
{
    AInterface a;
 
    A(){}
     
    AMethod()//一个方法
    {
        a = new AInterfaceImp();
    }
}
  Class A与AInterfaceImp就是依赖关系,如果想使用AInterface的另外一个实现就需要更改代码了。当然我们可以建立一个Factory来根据条件生成想要的AInterface的具体实现,即:
InterfaceImplFactory
{
   AInterface create(Object condition)
   {
      if(condition == condA)
      {
          return new AInterfaceImpA();
      }
      else if(condition == condB)
      {
          return new AInterfaceImpB();
      }
      else
      {
          return new AInterfaceImp();
      }
    }
}
 
  表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。
 
2.再开始说Spring的原理——IOC
  通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。
   IoC模式,系统中通过引入实现了IoC模式的IoC容器,即可由IoC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
  可以把IoC模式看做是工厂模式的升华(动态代理),可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
(JVM允许Java程序在运行时,去查找Java对象Class.forName(包名+类名),也是动态代理实现的方式,简单工厂定义一个外部键值文件,在工厂里面加载该文件,当要创建某个对象的时候,通过读取文件用键获取值,然后用Class.forName(包名+类名)可以实现一个比较简单的动态代理创建一个对象)
  IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,像Hibernate、Spring中都是用“反射”做为最基本的技术手段。
  IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拔(有点像USB接口和SCSI硬盘了)。
  IoC最大的缺点是什么?(1)生成一个对象的步骤变复杂了(事实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺陷所在。
 
AOP
 
1.先说为什么要用AOP
  比如说登录拦截,按照OOP思想,创建一个抽象类,然后让各个要检验是否登录的类继承该抽象类进行登录拦截,但是由于Java单继承的特点,有些类是无法再次继承的,这样就处理起来就很麻烦,不可能在每个类中写拦截的方法并在需要判断的地方做判断拦截处理,这样代码盈余,重用性低,维护性极差。因此这时候OOP就不适用了。(用接口的做法也是不行的,每个实现类都要实现该方法,当然如果用公共类去做这块的处理,耦合太高,不便于维护。)  
但是要记住AOP实际上是一种编程思想,AOP面向切面编程基于IoC,是对OOP的有益补充。
 
2.再开始说Spring的原理——AOP
  Filter的实现、interceputor的实现以及struts2的拦截器的实现都是AOP思想的体现
  将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

  下面一个例子来说明问题
引用于:http://supben.iteye.com/blog/1520126
  先看 advice 类
package com.supben.advice;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

/**
 * 实现spring advice 接口
 * 
 * @author shencl
 * 
 */
public class TestAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {

    private static final Logger log = LoggerFactory.getLogger(TestAdvice.class);

    /**
     * before 通知
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        log.info(target.getClass().getSimpleName() + "类的" + method.getName() + "方法,执行TestAdvice的before通知");
     // 通知要做的业务
        if (method.getName().startsWith("get")) {
            log.info("只有方法名是以get开始的方法,才会执行到这句话....");
        }
    }

    /**
     * after 通知
     */
    public void afterReturning(Object arg0, Method method, Object[] arg2, Object target) throws Throwable {
        log.info(target.getClass().getSimpleName() + "类的" + method.getName() + "方法,执行TestAdvice的after通知");
    }

    /**
     * 异常通知
     */
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
        log.info(target.getClass().getSimpleName() + "类的" + method.getName() + "方法,执行TestAdvice的throwing通知");
    }

}

 



配置文件
<?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"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <context:annotation-config />
    <!-- 扫描com.supben 下所有的包-->
    <context:component-scan base-package="com.supben" />

    <bean id="testAdvice" class="com.supben.advice.TestAdvice" />
    <aop:config>
       <aop:advisor pointcut="execution(* *..service.*Service.*(..))"
            advice-ref="testAdvice" />
    </aop:config>

</beans>

 



service接口
    package com.supben.service;  
      
    public interface FirstService {  
      
        public void get();  
      
        public void exception();  
    }  

 


service实现类
package com.supben.service.impl;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Service;  
  
import com.supben.service.FirstService;  
  
@Service("firstService")  
public class FirstServiceImpl implements FirstService {  
    private static final Logger log = LoggerFactory.getLogger(FirstServiceImpl.class);  
  
    public void get() {  
        log.info("方法执行ing.....");  
    }  
  
    public void exception() {  
        throw new RuntimeException("测试异常");  
    }  
}

 



测试类
    package com.supben.test;  
      
    import junit.framework.TestCase;  
      
    import org.junit.Test;  
    import org.springframework.context.support.ClassPathXmlApplicationContext;  
      
    import com.supben.service.FirstService;  
    import com.supben.spring.SpringContextUtil;  
      
    public class ServiceTest extends TestCase {  
        /** 
         * 装载spring 配置文件 
         */  
        static {  
            new ClassPathXmlApplicationContext("application.xml");  
        }  
      
        @Test  
        public void testGet() {  
            FirstService service = SpringContextUtil.getBean("firstService");  
            service.get();  
        }  
      
        @Test  
        public void testGet2() {  
            FirstService service = SpringContextUtil.getBean("firstService");  
            service.exception();  
        }  
    }  

 



测试结果:

2012-05-09 15:10:10,028 INFO [com.supben.advice.TestAdvice] - FirstServiceImpl类的get方法,执行TestAdvice的before通知
2012-05-09 15:10:10,030 INFO [com.supben.advice.TestAdvice] - 只有方法名是以get开始的方法,才会执行到这句话....
2012-05-09 15:10:10,032 INFO [com.supben.service.impl.FirstServiceImpl] - 方法执行ing.....
2012-05-09 15:10:10,032 INFO [com.supben.advice.TestAdvice] - FirstServiceImpl类的get方法,执行TestAdvice的after通知



2012-05-09 15:10:10,035 INFO [com.supben.advice.TestAdvice] - FirstServiceImpl类的exception方法,执行TestAdvice的before通知
2012-05-09 15:10:10,035 INFO [com.supben.advice.TestAdvice] - FirstServiceImpl类的exception方法,执行TestAdvice的throwing通知

结果分析:
get方法满足执行之前会 执行 before通知,执行完成后会执行after通知。

exception方法执行之前会执行before 通知,因为方法名不是以get开头,所以不会执行before通知的业务逻辑。然后出现异常了会执行throwing通知,因为出异常了,方法没有执行完毕,所以不会触发after通知。


概念:

切面(aspect): 翻译成外貌更合适:整个程序相当于一个密封的圆柱体,即一个外貌,现在要面向这个东西编程,在不改变原来类(FirstServiceImpl)的情况下,改变里边的代码。通知(advice):TestAdvice里边的before,after,throwing方法都是通知。 常见的有前置通知,后置通知,异常通知。
切点(cut-point):定义通知应该应用在哪些地方,本例是FirstServiceImpl中的get方法和exception方法,一般用正则表达式定义。
切点表达式:配置文件中的execution(* *..service.*Service.*(..)) 是一个切点表达式,表示的是一个一个的方法.比如本例中的表达式,意思是 包目录的最后一级是service,类/接口名 后缀为Service的 class文件里的,方法名为任意名称,参数个数不限的 方法。 * 表示任意,(..)表示方法参数个数不限。
目标对象(traget):FirstService就是目标对象。

此外还有两个重要的概念
引入(Introduction):允许为已存在类添加新方法和属性。
代理(Proxy):将通知应用到目标对象后创建的对象。

 

转载于:https://www.cnblogs.com/onlymate/p/6212444.html

相关文章:

  • Js 根据身份证号获取年龄-性别
  • linux下正确安装jsoncpp
  • hive 复杂类型
  • SQL Case when 的使用方法
  • 设计模式--适配器模式Adapter(结构型)
  • 各种文件的mime类型
  • [游戏开发-学习笔记]菜鸟慢慢飞(三)-官方教程学习小心得
  • Object类中getClass()
  • dubbo问题求解
  • 单例模式浅析
  • Django基于Pycharm开发之二 [使用django adminSite]
  • bodyParser中间件的研究
  • asp.net mvc adminlte第一波
  • 【转】TCP/IP协议栈及OSI参考模型详解
  • 闭包2
  • 收藏网友的 源程序下载网
  • 【译】理解JavaScript:new 关键字
  • ➹使用webpack配置多页面应用(MPA)
  • Android Studio:GIT提交项目到远程仓库
  • Android Volley源码解析
  • Android 架构优化~MVP 架构改造
  • export和import的用法总结
  • js如何打印object对象
  • JS专题之继承
  • Mithril.js 入门介绍
  • PhantomJS 安装
  • rc-form之最单纯情况
  • RxJS: 简单入门
  • scrapy学习之路4(itemloder的使用)
  • tweak 支持第三方库
  • 从零开始的无人驾驶 1
  • 后端_MYSQL
  • 扑朔迷离的属性和特性【彻底弄清】
  • 前端知识点整理(待续)
  • 如何解决微信端直接跳WAP端
  • 数据结构java版之冒泡排序及优化
  • 写代码的正确姿势
  • 阿里云ACE认证学习知识点梳理
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • $L^p$ 调和函数恒为零
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (poj1.2.1)1970(筛选法模拟)
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • . NET自动找可写目录
  • .describe() python_Python-Win32com-Excel
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core WebAPI中封装Swagger配置
  • .NET Micro Framework 4.2 beta 源码探析
  • .net网站发布-允许更新此预编译站点
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .net中生成excel后调整宽度