12.控制反转IOC与面向切面编程AOP

Posted 醉梦依依惜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12.控制反转IOC与面向切面编程AOP相关的知识,希望对你有一定的参考价值。

目录

一、IOC-控制反转。

二、AOP-面向切面编程。


一、IOC-控制反转。

IOC(Inversion of Control)是控制反转的缩写,指的是将对象的创建过程和依赖关系的处理交给外部容器来实现,从而实现对象之间的解耦。因此,IOC不是容器,是一种编程思想,将对象之间的依赖关系的处理从程序中转移到容器(或者其他外部实现)中,控制程序的对象创建和依赖关系的管理。而容器是实现IOC思想的一种技术手段,即通过容器来实现对象之间的解耦。因此,可以说IOC既是控制反转,也是一种容器。

当使用IoC容器时,应用程序不直接调用对象,而是通过容器请求依赖关系。下面是一个简单的示例:

服务接口:

```
public  interface  IMyService  
        void  doSomething();

```

服务实现:

```
public  class  MyService  implements  IMyService  
        @Override
        public  void  doSomething()  
                System.out.println(  Doing  something...  );
        

```

客户端类:

```
public  class  Client  
        private  IMyService  myService;
        
        public  Client(IMyService  myService)  
                this.myService  =  myService;
        
        
        public  void  doSomething()  
                myService.doSomething();
        

```

使用Spring的IoC容器配置:

```
<bean id=myService class=com.example.MyService />

<bean id=client class=com.example.Client>
        <constructor-arg  ref=myService />
</bean>
```

在这个示例中,客户端类依赖于接口IMyService。通过使用IoC容器,容器创建服务对象,并将其注入到客户端类的构造函数中,从而协调对象之间的依赖关系。这种设计方法通过将依赖关系从代码中移出来,使程序易于维护和测试。

二、AOP-面向切面编程。

AOP(Aspect-Oriented  Programming)是面向切面编程,是一种软件的编程思想。它是在OOP的基础上,通过预编译方式和运行期动态代理技术实现程序功能的统一维护的技术。在AOP中,通过预编译方式和运行期动态代理技术实现程序功能的统一维护。

AOP的作用是在不修改原有类的前提下,动态地为原有类提供额外的功能,比如安全验证、事务管理、日志记录、缓存等。下面通过一个简单的代码示例说明AOP的作用:  

我们假设有一个简单的Java类,其中包含了一个add()方法:

```java
public  class  Calculator  
        public  int  add(int  num1,  int  num2)  
                return  num1  +  num2;
        

```

现在我们需要在add()方法执行时记录方法的执行时间,并打印日志。使用AOP,我们不需要修改原有类,只需要在AOP中实现这个功能,并在运行时将其注入到Calculator类中。

```java
public  class  LogAspect  
        public  void  logMethodExecutionTime(JoinPoint  joinPoint)  
                long  start  =  System.currentTimeMillis();
                joinPoint.proceed();
                long  end  = System.currentTimeMillis();
                System.out.println(Method + joinPoint.getSignature().getName()  
                 + executed  in +  (end  -  start) + ms);
        


public  class  Calculator  
        public  int  add(int  num1,int  num2)  
                return  num1+num2;
        
<bean  id=  logAspect    class=  com.example.LogAspect  />

<aop:config>
        <aop:aspect  ref=  logAspect  >
                <aop:pointcut  id=  calculation    expression=  execution(*  com.example.Calculator.add(..))  />
                <aop:before  method=  logMethodExecutionTime    pointcut-ref=  calculation  />
        </aop:aspect>
</aop:config>
```

以上代码中,LogAspect类实现了在方法执行时记录执行时间的功能。通过AOP配置,我们将LogAspect注入到Calculator类中,并指定在add()方法执行前执行logMethodExecutionTime()方法。当调用Calculator的add()方法时,AOP会自动执行LogAspect中的logMethodExecutionTime()方法,并在控制台输出方法执行时间的日志信息。

Spring 面向切面编程(AOP)


Spring 系列教程


面向切面编程(Aspect Oriented Programming/AOP)是Spring框架核心技术之一。

面向切面编程的主要作用是,在不修改源代码的情况下,可以给目标类打补丁,让其执行补丁中的代码。

例如,用户类中有个登录方法,现在需要加用户登录日志。使用AOP就不需要修改用户登录方法,只需把日志代码注入到用户登录方法前后,让其执行。日志代码就是“切面”,插入代码的地方(用户类的登录方法)就是“连接点”。

面向切面编程概念

先介绍一些AOP的概念

  • 切面(Aspect) - 一些横跨多个类的公共模块,如日志、安全、事务等。简单地说,日志模块就是一个切面。
  • 连接点(Joint Point) - 目标类中插入代码的地方。连接点可以是方法、异常、字段,连接点处的切面代码会在方法执行、异常抛出、字段修改时触发执行。
  • 建议(Advice) - 在连接点插入的实际代码(即切面的方法),有5种不同类型(后面介绍)。
  • 切点(Pointcut) - 定义了连接点的条件,一般通过正则表达式。例如,可以定义所有以loadUser开头的方法作为连接点,插入日志代码。

建议类型

  • before - 在方法之前运行建议(插入的代码)
  • after - 不管方法是否成功执行,在方法之后运行插入建议(插入的代码)
  • after-returning - 当方法执行成功,在方法之后运行建议(插入的代码)
  • after-throwing - 仅在方法抛出异常后运行建议(插入的代码)
  • around - 在方法被调用之前和之后运行建议(插入的代码)

实现

与Bean配置一样,切面也需要配置,然后由Spring容器加载。切面配置可以使用XML,或者使用“AspectJ”语法,“AspectJ”语法使用Java代码实现切面配置。

为更深理解AOP,下面实现一个日志切面的例子,例子使用XML配置。

User类

一个简单的用户类,是日志切面插入的目标类。用户类实现了几个不同的方法,这些方法会作为连接点。

User.java

public class User 
  private Integer id;
  private String name;
  
  public void setId(Integer id) 
    this.id = id;
  
  
  public Integer getId() 
    System.out.println("Id: " + id);
    return id;
  
  
  public void setName(String name) 
    this.name = name;
  
  
  public String getName() 
    System.out.println("Name: " + name );
    return name;
  
  
  public void printThrowException()
    System.out.println("Exception raised");
    throw new IllegalArgumentException();
  

切面 - Logging

日志切面类,定义了要插入目标类执行的方法。

Logging.java

public class Logging 
  public void beforeAdvice()
    System.out.println("Before Advice");
  
    
  public void afterAdvice()
    System.out.println("After Advice");
  
  
  public void afterReturningAdvice(Object retVal)
    System.out.println("After Advice Executed Successfully ... Returning: " + retVal.toString() );
  
  
  public void AfterThrowingAdvice(IllegalArgumentException ex)
    System.out.println("There has been an exception when executing the advice: " + ex.toString());
  

retVal是目标类连接点(方法)返回的值。例如,如果连接点是User类的getName()方法,该方法返回用户名称,那么retVal将被赋值用户名称。

配置

本例使用XML配置切面。

首先定义切面类的Bean,然后切面定义中引用该Bean。

切面定义中,会指明切点、插入的代码(建议),以及插入的代码怎么执行(建议类型)。

下面的示例定义了一个名为UserAllMethod的切点,使用expression="execution(* User.*(..))"匹配User类中的所有方法作为连接点。

如果想指定特定方法作为连接点,可使用execution(* User.getName(..))

示例:beans.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
        
  <aop:config>
    <!-- Aspect -->
    <aop:aspect id="loggingAspect" ref="logging">
      
      <!-- Pointcut -->
      <aop:pointcut id="UserAllMethods" expression="execution(* User.*(..))"/>
      
      <!-- Advice(s) -->
      <aop:before pointcut-ref="UserAllMethods" method="beforeAdvice"/>
      <aop:after  pointcut-ref="UserAllMethods" method="afterAdvice"/>
      <aop:after-returning pointcut-ref="UserAllMethods" returning="retVal" method="afterReturningAdvice"/>
      <aop:after-throwing  pointcut-ref="UserAllMethods" throwing="ex" method="AfterThrowingAdvice"/>
    </aop:aspect>
  </aop:config>
  
  <!-- The user bean -->
  <bean id="user" class="User">
      <property name="name" value="隔壁老王" />
      <property name="id" value="99"/>
  </bean>
 
  <!-- logging 切面定义 -->
  <bean id="logging" class="Logging"/>
</beans>

确保已经添加了依赖的JAR包,我们使用Maven,pom.xml中添加依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>$springframework.version</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.10</version>
</dependency>

测试切面

在main类中调用用户类的方法,查看切面是否被执行:

Test.java

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test 
  public static void main(String[] args) 
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
    User user = (User) context.getBean("user"); 
    user.getName();
    user.printThrowException();
  

以上是关于12.控制反转IOC与面向切面编程AOP的主要内容,如果未能解决你的问题,请参考以下文章

控制反转(ioc)和 面向切面(AOP)

IOC/DI-控制反转----AOP-面向切面编程

Spring基础IOC(控制反转)AOP(面向切面编程)Log4j注解配置

Spring框架使用(控制反转,依赖注入,面向切面AOP)

ioc与aop

Spring