Java开发必读,谈谈对Spring IOC与AOP的理解

打印 上一主题 下一主题

主题 853|帖子 853|积分 2561

本文分享自华为云社区《超详细的Java后台开发面试题之Spring IOC与AOP》,作者:GaussDB 数据库。
一、前言

IOC和AOP是Spring中的两个核心的概念,下面谈谈对这两个概念的理解。
二、IOC(Inverse of Control)

控制反转,也可以称为依赖倒置。
所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B,反正A要用到B,则A依赖于B。所谓倒置,你必须理解如果不倒置,会怎么着,因为A必须要有B,才可以调用B,如果不倒置,意思就是A主动获取B的实例:B b = new B(),这就是最简单的获取B实例的方法(当然另有各种设计模式可以帮助你去获得B的实例,比如工厂、Locator等等),然后你就可以调用b对象了。所以,不倒置,意味着A要主动获取B,才能使用B;到了这里,就应该明白了倒置的意思了。倒置就是A要调用B的话,A并不需要主动获取B,而是由其它人自动将B送上门来。
形象的举例就是:
通常情况下,假如你有一天在家里口渴了,要喝水,那么你可以到你小区的小卖部去,告诉他们,你需要一瓶水,然后小卖部给你一瓶水!这原来没有太大题目,关键是如果小卖部很远,那么你必须知道:从你家如何到小卖部;小卖部里是否有你需要的水;你还要思量是否开着车去;等等等等,也许有太多的题目要思量了。也就是说,为了一瓶水,你还可能需要依赖于车等等这些交通工具或别的工具,题目是不是变得复杂了?那么如何解决这个题目呢?
解决这个题目的方法很简单:小卖部提供送货上门服务,凡是小卖部的会员,你只要告知小卖部你需要什么,小卖部将主动把货物给你送上门来!这样一来,你只需要做两件事故,你就可以活得更加轻松自在:
第一:向小卖部注册为会员。
第二:告诉小卖部你需要什么。
这和Spring的做法很雷同!Spring就是小卖部,你就是A对象,水就是B对象
第一:在Spring中声明一个类:A
第二:告诉Spring,A需要B
假设A是UserAction类,而B是UserService类。
  1. <bean id="userService" class="org.leadfar.service.UserService"/>  
  2. <bean id="documentService" class="org.leadfar.service.DocumentService"/>  
  3. <bean id="orgService" class="org.leadfar.service.OrgService"/>  
  4.   
  5. <bean id="userAction" class="org.leadfar.web.UserAction">  
  6.      <property name="userService" ref="userService"/>  
  7. </bean>  
复制代码
在Spring这个商店(工厂)中,有很多对象/服务:userService,documentService,orgService,也有很多会员:userAction等等,声明userAction需要userService即可,Spring将通过你给它提供的通道主动把userService送上门来,因此UserAction的代码示例雷同如下所示:
  1. package org.leadfar.web;  
  2. public class UserAction{  
  3.      private UserService userService;  
  4.      public String login(){  
  5.           userService.valifyUser(xxx);  
  6.      }  
  7.      public void setUserService(UserService userService){  
  8.           this.userService = userService;  
  9.      }  
  10. }  
复制代码
在这段代码里面,你无需自己创建UserService对象(Spring作为背后无形的手,把UserService对象通过你定义的setUserService()方法把它主动送给了你,这就叫依赖注入!),当然咯,我们也可以使用注解来注入。Spring依赖注入的实现技术是:动态代理。
三、AOP:面向切面编程

面向切面编程的目标就是分离关注点。什么是关注点呢?就是你要做的事,就是关注点。假如你是个公子哥,没啥人生目标,天天就是衣来伸手,饭来张口,整天只知道玩一件事!那么,每天你一睁眼,就光想着吃完饭就去玩(你必须要做的事),但是在玩之前,你还需要穿衣服、穿鞋子、叠好被子、做饭等等等等事故,这些事故就是你的关注点,但是你只想吃饭然后玩,那么怎么办呢?这些事故通通交给别人去干。在你走到饭桌之前,有一个专门的仆人A帮你穿衣服,仆人B帮你穿鞋子,仆人C帮你叠好被子,仆人D帮你做饭,然后你就开始吃饭、去玩(这就是你一天的正事),你干完你的正事之后,返来,然后一系列仆人又开始帮你干这个干那个,然后一天就结束了!
AOP的好处就是你只需要干你的正事,其它事故别人帮你干。也许有一天,你想裸奔,不想穿衣服,那么你把仆人A开除就是了!也许有一天,出门之前你还想带点钱,那么你再雇一个仆人E专门帮你干取钱的活!这就是AOP。每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。
从Spring的角度看,AOP最大的用途就在于提供了事务管理的本领。事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务!
我们在使用Spring框架的过程中,其实就是为了使用IOC(依赖注入)和AOP(面向切面编程),这两个是Spring的灵魂。主要用到的设计模式有工厂模式和代理模式。IOC就是典范的工厂模式,通过sessionfactory去注入实例;AOP就是典范的代理模式的表现。
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及过后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类对象与一个委托类对象关联,代理类对象本身并不真正实现服务,而是通过调用委托类的对象相干方法,来提供特定的服务。
Spring IoC容器是spring的核心,spring AOP是spring框架的重要组成部门。
在传统的程序设计中,当调用者需要被调用者的帮忙时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一个概念。
面向方面编程(AOP)是从另一个角度来思量程序结构,通过分析程序结构的关注点来完善面向对象编程(OOP)。OOP将应用程序分解成各个层次的对象,而AOP将程序分解成多个切面。spring AOP只实现了方法级别的连接点,在J2EE应用中,AOP拦截到方法级别的操作就已经充足。在spring中为了使IoC方便地使用结实、灵活的企业服务,需要利用spring AOP实现为IoC和企业服务之间创建联系。
IOC:控制反转也叫依赖注入。利用了工厂模式。
将对象交给容器管理,你只需要在spring配置文件中配置相应的bean,以及设置相干的属性,让spring容器来天生类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
注意:面试的时候,如果有条件,绘图,这样更加显得你懂了.
四、AOP(Aspect-Oriented Programming):面向切面编程补充阐明

AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来创建一种对象层次结构,用以模仿公共行为的一个聚集。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不恰当定义从左到右的关系。比方日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致了大量代码的重复,而倒霉于各个模块的重用。
将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。
实现AOP的技术,主要分为两大类:一是接纳动态代理技术,利用截取消息的方式,对该消息进行装饰,以代替原有对象行为的实行;二是接纳静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码.
简单点解释,比方说你想在你的biz层所有类中都加上一个打印‘你好’的功能,这时就可以用aop头脑来做.你先写个类写个类方法,方法经实现打印‘你好’,然后Ioc这个类 ref=“biz.*”让每个类都注入即可实现。
五、Spring中对 AOP的支持

Spring中 AOP代来由Spring IoC容器负责天生、管理,其依赖关系也由 IoC容器负责管理。因此,AOP代理可以直接使用容器中的其他 Bean实例作为目标,这种关系可由 IoC容器的依赖注入提供。Spring默认使用 Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。当需要代理的类不是代理接口的时候,Spring自动会切换为使用 CGLIB代理,也可强制使用 CGLIB。
5.1 程序员参与部门

AOP编程其实是很简单的事故。纵观 AOP编程,其中需要程序员参与的只有三个部门:

  • 定义平凡业务组件。
  • 定义切入点,一个切入点可能横切多个业务组件。
  • 定义增强处理,增强处理就是在AOP框架为平凡业务组件织入的处理动作。
所以进行AOP编程的关键就是定义切入点和定义增强处理。一旦定义了符合的切入点和增强处理,AOP框架将会自动天生AOP代理,即:代理对象的方法 =增强处理 +被代理对象的方法。
5.2 Spring中使用方式

基于 Annotation的“零配置”方式。
(1)启动注解,配置文件applicationContext.xml
  1.       
  2. <aop:aspectj-autoproxy/>  
  3. <bean id="user" class="com.tgb.spring.aop.IUserImpl"/>  
  4. <bean id="check" class="com.tgb.spring.aop.CheckUser"/>  
复制代码
(2)编写切面类
  1. package com.tgb.spring.aop;  
  2. import org.aspectj.lang.ProceedingJoinPoint;  
  3. import org.aspectj.lang.annotation.After;  
  4. import org.aspectj.lang.annotation.Around;  
  5. import org.aspectj.lang.annotation.Aspect;  
  6. import org.aspectj.lang.annotation.Before;  
  7. import org.aspectj.lang.annotation.Pointcut;  
  8.   
  9. @Aspect   
  10. public class CheckUser {  
  11.     @Pointcut("execution(* com.tgb.spring.aop.*.find*(..))")  
  12.     public void checkUser(){  
  13.       System.out.println("The System is Searching Information For You");  
  14.     }  
  15.    
  16.     @Pointcut("execution(* com.tgb.spring.aop.*.add*(..))")  
  17.     public void checkAdd(){  
  18.         System.out.println("<< Add User >> Checking.....");  
  19.     }  
  20.       
  21.     @Before("checkUser()")  
  22.     public void beforeCheck(){  
  23.         System.out.println(">>>>>>>> 准备搜查用户..........");  
  24.     }  
  25.       
  26.     @After("checkUser()")  
  27.     public void afterCheck(){  
  28.         System.out.println(">>>>>>>> 搜查用户完毕..........");  
  29.     }  
  30.   
  31.     @Before("checkAdd()")  
  32.     public void beforeAdd(){  
  33.         System.out.println(">>>>>>>> 增加用户--检查ing..........");  
  34.     }  
  35.       
  36.     @After("checkAdd()")  
  37.     public void afterAdd(){  
  38.         System.out.println(">>> 增加用户--检查完毕!未发现异常!........");  
  39.     }  
  40.      //声明环绕通知   
  41.     @Around("checkUser()")   
  42.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {   
  43.         System.out.println("进入方法---环绕通知");   
  44.         Object o = pjp.proceed();   
  45.         System.out.println("退出方法---环绕通知");   
  46.         return o;   
  47.     }
  48. }  
复制代码
(3)定义接口
  1. package com.tgb.spring.aop;  
  2. public interface IUser {  
  3.     public String findUser(String username);  
  4.     public void addUser(String username);  
  5.     public void findAll();  
  6. }
复制代码
(4)定义实现
[code]package com.tgb.spring.aop;  import java.util.HashMap;  import java.util.Map;  public class IUserImpl implements IUser {      public static Map map = null;      public static void init(){          String[] list = {"Lucy", "Tom", "小明", "Smith", "Hello"};          Map tmp = new HashMap();          for(int i=0; i
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81428

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表