Spring 的 AOP 和 IOC 的理解

IOC: Inversion of Control,控制反转, 控制权从应用程序转移到框架(如IOC容器),是框架共有的特性。

对于IOC的理解,可以把IOC看作是一个生产和管理bean对象的容器。原本程序中我们要手动自己创建(new)的对象统统交给Spring的IOC容器帮我们创建。同时这就意味着,要产生的单例的bean,这个对象的生命周期也是有IOC容器管理。

Spring中IOC的三种实现方式:

1.属性注入,或叫做set方法注入;
2.构造方法注入;
3.注解注入,可能因为方便的原因,这种方式免去了臃肿的配置,所以比较常用。

Spring的有点主要是根据IOC和AOP实现的,之前有简单提到AOP的实现思想,这些都是针对工厂模式和代理模式的友好封装。IOC主要解决了代码的高度耦合问题,AOP将面向切面的编程思想做出了最好的解释和应用。

这里顺带简单解释一下Spring中的IOC,DI,AOP:

       IOC就是控制反转,通俗的说就是我们不用自己创建实例对象,这些都交给Spring的bean工厂帮我们创建管理。这也是Spring的核心思想,通过面向接口编程的方式来是实现对业务组件的动态依赖。这就意味着IOC是Spring针对解决程序耦合而存在的。在实际应用中,Spring通过配置文件(xml或者properties)指定需要实例化的java类(类名的完整字符串),包括这些java类的一组初始化值,通过加载读取配置文件,用Spring提供的方法(getBean())就可以获取到我们想要的根据指定配置进行初始化的实例对象。

       AOP就是将程序功能中的频繁出现或者与主业务逻辑代码相关度不高的代码抽离出来,通过切面编程的方式在想要调用的时候引入调用的思想。而这种思想并不是只限于Spring和java,AOP(面向切面)和OOP(面向对象)一样都是一种编程思想,这种思想的实现机制在Spring中便是应用了java的动态代理和java的反射。在实际编程中,我们通常会遇到一些交叉业务逻辑(比如:日志,事务,安全等等),这是我们就可以封装一个封面,然后注入到目标对象(具体的业务逻辑)中去。ps:很多方法都会抛出异常信息,这是我们就可以写一个拦截器,在这个类中实现记录错误日志的功能,再在Spring的xml配置文件中配置一个对这些要记录日志的方法的AOP拦截器,在这个方法执行后调用这个拦截器来记录日志。这样就不用每次抛出异常都要手动的去单独处理记录,提高了程序的内聚性。这种在调用某个方法之前/后想要自动执行一系列自定义操作的就是AOP思想。


总结:

IOC即控制反转,一种将控制权转移的设计模式,由传统的程序控制转移到容器控制;

DI即依赖注入,将相互依赖的对象分离,在Spring的配置(注解)中描述它们之间的依赖关系,这些依赖关系也只在使用时才被建立。

AOP即面向切面,一种编程思想,OOP的延续。将系统中非核心的业务提取出来,进行单独处理。

Spring的AOP和IOC都是为了解决代码的耦合度的实际应用,使得代码的重用度变高,便于维护。但这些都不是Spring中特有的,我们可以说Spring将它们应用的更灵活。

引用前者:络庆钟朔

如果还是不明白,我们可以进行直白的理解:

一、什么叫Ioc、DI
Ioc:Inversion of Control —— 控制反转

DI:Dependency Injection —— 依赖注入

其实这两个概念本质上是没有区别的,那我们先来看看什么叫做Ioc?

假设这么一个场景:

在A类中调用B类的方法,那么我们就称 A依赖B,B为被依赖(对象),相信这点大家能够理解。

传统做法:

  • (1)直接在A(方法)中new出B类对象,然后调用B类方法 —— 硬编码耦合;
  • (2)通过简单工厂获取B类对象,然后调用B类的方法 —— 摆脱了与B的耦合,却又与工厂产生了耦合;

以上两种做法,都是在A中主动去new或调用简单工厂的方法产生B的对象,注意,关键字是“主动”

Spring框架

在spring中,B的实例对象被看成Bean对象,这个Bean对象由spring容器进行创建和管理,当我们在配置文件中配置<Bean>下的<property>子元素时,spring就会自动执行在A中对B对象的setter方法(即A中需要有对B对象的setter方法),如此一来,A获取B的实例对象就不是由自己主动去获取,而是被动接受spring给它设值,那么,这个主动变为被动,就可以理解为“控制反转”。

而另一种说法,从spring容器的角度上看,它负责把A的依赖对象B(B是被依赖对象)注入给了A,所以我们可以理解为“依赖注入”

(spring中依赖注入的方式可不止调用setter方法一种,还有通过调用构造器的方式来实现,这里只是为了说明Ioc和DI,就不再累赘了)

<bean id="userAction" class="com.router.action.UserAction" scope="prototype">
        <!-- 注入Service -->
        <property name="userService" ref="userService" />
</bean>
<bean id="userService" class="com.router.serviceimpl.UserServiceImpl"></bean>
// 注入service
private UserService userService;
        
public void setUserService(UserService userService) {
        this.userService = userService;
    }

        以上代码,spring通过<property>元素(当然内部有它的实现方法)自动调用userService的setter方法,userAction中就获得了userService对象了。

        那么我们来分析一下,通过使用spring的依赖注入功能,是怎么达到解耦了呢?

        首先,我们的编程是面向接口编程(在实际开发开发中也是需要我们面向接口编程的),上面代码中的UserService就是一个接口,UserServiceImpl就是其中的一个实现类。那么当我们通过直接new的方式创建对象,则是UserService userService = new UserServiceImpl();,这句话是写在源代码里头中的,当实现类UserServiceImpl内部放生改变时,或者是不再想使用这个类,而是另一个新的实现类(比如说是UserServiceImpl2),那么我们就得在源代码中将UserService userService = new UserServiceImpl2();,而以后或许需求还会变,那么就得不停地修改源代码。

而使用spring框架后,只需在配置文件中的<Bean>配置所需要的相应接口的实现方法,然后通过setter方法注入进去即可,setter方法不管以后变不变实现类,都不需要修改,要改的只是在spring的配置文件中改掉实现类的全路径即可,如此看来,这确实是达到了解耦!

二、什么是AOP?
AOP —— Asepct-Orentid-Programming,面向切面编程

        那么我们该怎么理解AOP呢?我们可以通过OOP —— 面向对象编程来进行比较分析

        相信大家对于OOP的理解不难,就以人(people)来说,我们就可以把它看做一类对象,people有身高、体重、年龄等属性,也有跑步、吃饭、睡觉、娱乐等行为,把这些属于people的属性和行为封装在people类中,然后以统一调用的方式(创建一个people类实例对象,通过这个对象实例来调用这些属性和行为)就叫做OOP思想,OOP给我们的感觉就是结构清晰,高内聚,易维护等。这些属于一种从上到下的关系(即这个类封装的所有属性和方法都是属于people的),而我们的AOP思想就是一种从左到右的关系,以切入的方式将业务逻辑功能应用到每一层结构中(可以理解为类方法,类方法也是一种对象的行为实现)。举个例子,people也可以分为少年、青年、中年、和老年,这几类人除了拥有自己的属性和行为外,生活中,或许还需要去医院看病,但是医院看病这一个逻辑业务功能并不是属于哪一类,而是谁生病了,才需要到医院看病,而基于面向对象编程的思想,我们是不可能把这一个业务逻辑行为加到每一个类中的,这不符合OOP思想,而这个就是AOP所做也可以做到事情了,AOP就是把医院看病这一个业务逻辑功能抽取出来,然后动态把这个功能注入到需要的方法(或行为)中,以后,不管是谁需要看病,就到医院这个第三方机构看病(AOP就是相当于把这个第三方机构独立出来),这样从业务逻辑角度上,AOP达到了更近一步的的解耦,所以我们也称AOP是对OOP的完善和增强。

而我们的编程中,常用到AOP的就是安全校验、日志操作、事务操作等,接下来一张图认识AOP思想

        AOP就是使用上图所示的“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

AOP的相关术语

1.通知(Advice)
  就是你想要的功能,也就是上面说的 安全,事务,日志等。你给先定义好,然后在想用的地方用一下。

2.连接点(JoinPoint)
  这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点,其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

3.切入点(Pointcut)
  上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4.切面(Aspect)
  切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

5.引入(introduction)
  允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

6.目标(target)
  引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.代理(proxy)
  怎么实现整套aop机制的,都是通过代理,也就是说,AOP的实现原理是基于动态代理实现的。

8.织入(weaving)
  把切面应用到目标对象来创建新的代理对象的过程。

        在此笔者建议,如果不是很了解java动态代理的代理,可以先去熟悉下动态代理,这样能更好的理解AOP的实现原理

        可看笔者另一篇文章 静态代理与动态代理

AOP应用实例1 —— 动态代理的形式模拟AOP

(以下应用实例都是基于接口编程,笔者就不示出接口了)

public class UserAImpl implements UserA{
 
    @Override
    public void save() {
        System.out.println("正在保存A类用户……");
        
    }
 
    @Override
    public void update() {
        System.out.println("正在更新A类用户……");
        
    }
 
}

 

public class UserBImpl implements UserB {
 
    @Override
    public void save() {
        System.out.println("正在保存B类用户……");
 
    }
 
    @Override
    public void update() {
        System.out.println("正在更新B类用户……");
 
    }
 
}


AOP业务增强(通知)类

public class DataValidateImpl implements DataValidate {
 
    @Override
    public void validate() {
        System.out.println("正在进行数据校验……");
        System.out.println("数据校验完毕!");
        
    }
 
    @Override
    public void advice() {
        System.out.println("操作成功");
        
    }
 
}


代理工厂类

public class ProxyFactoryImpl implements ProxyFactory {
 
    //单例模式创建工厂
    private static ProxyFactoryImpl proxyFactorySingleton;
    
    private ProxyFactoryImpl() {}
    
    public static ProxyFactoryImpl getProxyFactorySingleton() {
        if (proxyFactorySingleton == null) {
            proxyFactorySingleton = new ProxyFactoryImpl();
        }
        return proxyFactorySingleton;
    }
    
    @Override
    public Object newProxyInstance(Object obj, InvocationHandler handler) {
        
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), 
                obj.getClass().getInterfaces(), 
                handler);
    }
 
}


测试类

public class AOPTest {
 
    public static void main(String[] args) {
        ProxyFactoryImpl proxyFactory = ProxyFactoryImpl.getProxyFactorySingleton();
        
        //操作A类用户数据
        UserA ua = (UserA) proxyFactory.newProxyInstance(new UserAImpl(), 
                new UserAHandler(new UserAImpl(), new DataValidateImpl()));
        //得到的是代理对象
        System.out.println(ua.getClass().getName());
        
        ua.save();
        ua.update();
        
        System.out.println("********************");
        
        //操作B类用户数据
        UserB ub = (UserB) proxyFactory.newProxyInstance(new UserBImpl(), 
                new UserBHandler(new UserBImpl(), new DataValidateImpl()));
        
        //得到的是代理对象
        System.out.println(ub.getClass().getName());
        
        ub.save();
        ub.update();
        
        //如果不用代理来调用,就是这样的结果
        System.out.println("======================");
        UserB ub2 = new UserBImpl();
        ub2.save();
        ub2.update();
    }
}


运行结果:

AOP应用实例 —— spring注解方式使用AOP

User类

public class User {
 
    public void addUser(){
        System.out.println("添加成功!");
    }
}


增强类

@Aspect
public class MyUser {
 
    @Before(value = "execution(* com.xian.entity.User.*(..))")
    public void before() {
        System.out.println("before……");
    }
    
    @After(value = "execution(* com.xian.entity.User.*(..))")
    public void after() {
        System.out.println("after……");
    }
}


配置文件

<bean id="user" class="com.xian.entity.User"></bean>
<bean id="myUser" class="com.xian.entity.MyUser"></bean>
    
    <!-- 配置文件方式使用AOP -->
    <!-- <aop:config>
        配置切入点
        <aop:pointcut expression="execution(* com.xian.entity.User.*(..))" id="userPC1"/>
        
        配置切面
            将增强使用于方法上
        
         <aop:aspect ref="myUser">
             配置增强的类型
             <aop:before method="before" pointcut-ref="userPC1"/>
             <aop:after method="after" pointcut-ref="userPC1"/>
         </aop:aspect>
    </aop:config> -->
    
    <!-- 注解方式使用AOP -->
    <!-- 开启AOP代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试类

public class UserTest {
    //private static Logger userLog = Logger.getLogger(User.class);
    @Test
    public void testUser(){
        
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");//通过bean容器获得 的user其实只是一个代理对象
        User user2 = new User();
        System.out.println(user == user2);
        MyUser mu = (MyUser) context.getBean("myUser");
        //userLog.info("开始调用User的Add方法……");
        user.addUser();//把这里变成user2来调用add,就不会执行切面的增强逻辑功能了
        
        //userLog.info("正常结束……");
    }
}

运行结果:


希望对读者有帮助!转载请注明出处!
--------------------- 
作者:回梦游先 
来源:CSDN 
原文:https://blog.csdn.net/h_xiao_x/article/details/72774496 
版权声明:本文为博主原创文章,转载请附上博文链接!

上面都是你自己理解的,一下是面试的标准回答:

什么是 Spring IOC 容器?
IOC 控制反转:Spring IOC 负责创建对象,管理对象。通过依赖注入(DI),装配对象,配置对象,并且管理这
些对象的整个生命周期

 IOC 的优点是什么?
IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试不再需要单例和 JNDI 查找机制。最小
的代价和最小的侵入性使松散耦合得以实现。IOC 容器支持加载服务时的饿汉式初始化和懒加载。

 

简单解释一下 spring 的 AOP
       AOP(Aspect Oriented Programming),即面向切面编程,可以说是 OOP(Object Oriented Programming,
面向对象编程)的补充和完善。OOP 引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一
个集合。不过 OOP 允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散
布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持
续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在 OOP 设计中,它导致了大量代码的
重复,而不利于各个模块的重用。
      AOP 技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
     使用"横切"技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,
与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,
比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
AOP 核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组 API 提供横切功能。比如,一个日
志模块可以被称作日志的 AOP 切面。根据需求的不同,一个应用程序可以有若干切面。在 Spring AOP 中,切面通过
带有@Aspect 注解的类实现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值