你好,游客 登录 注册 搜索
背景:
阅读新闻

Spring AOP 简介以及简单用法

[日期:2016-12-11] 来源:Linux社区  作者:nvd11 [字体: ]

如果你去面试Java开发, 那么spring的AOP和DI几乎是必问的问题。

那么AOP是什么呢?

一. AOP

所谓Aop就是 Aspect-OrientedProgramming, 中文就是面向切面编程。

我们之前听说面向过程编程, 以及面向对象编程, 而这个面向切面编程我们可以视为是面向对象编程的1个补充(增强);

在一般的方法(函数)中, 为了方便,可能只写了业务代码
1. 业务代码

而我们我们可以将其补充成4个部分。

  1. 验证参数
  2. 前置日志
  3. 业务代码
  4. 后置日志

1.1 横切关注点 (Crosscutting Concerns)

大家看上面的方法, 就像用刀子把1个方法横向切成4块, 我们把上面除业务代码外任意一个部分就叫做横切关注点.

1.2 切面 (Aspect)

我们可以把横切关注点进行分组, 其中任意一组就叫做切面

例如上面的例子中, 我们可以分成

  1. 验证切面 (1. 验证参数)
  2. 日志切面 (2. 前置日志, 4.后置日志)

1.3 通知 (Advice)

所谓通知就是切面要完成的工作。

例如对于日志通知来讲, 里面执行日志的方法我们就可以称为1个通知。

1.4 目标 (Target)

所谓目标就是被通知的对象, 也就是指上面例子中的原方法本身啦。

1.5 代理 (Proxy)

当目标被通知后产生的对象就叫做代理, 因为AOP的原理就是利用代理来实现的, 如果想了解动态代理的可以参考这里

1.6 连接点 (Joinpoint)

所谓Joinpoint就是程序执行到的某个位置,

上面例子中, 业务代码方法的执行之前, 执行之后, 抛出异常后 都可以视为某个连接点。

1.7 切点 (PointCut)

每个方法都存在多个连接点, 而Spring AOP利用切点来定位到具体那些连接点。
Joinpoint 和 PointCut 的关系可以作如下比喻,假如Joinpoint的数据里的记录, 那么PointCut就相当于查询条件

1.8 小结

而Spring的AOP 能在不修改具体某个方法的前提下, 利用动态代理技术将通知注入到这个方法的各个连接点中, 令到这个方法得到了必要的补充。

对于上面的例子种, 我们原来的方法只有业务代码, 但是我们可以利用Spring AOP加入 验证参数, 日志等功能!

二. 具体例子

2.1 还没使用AOP的例子

我们首先利用spring创建两个计算类。

1个加法类, 1个减法类。

bean config 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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <!-- auto scan -->
    <context:component-scan base-package="com.home.aop"></context:component-scan>
</beans>

注意引入aop命名空间

计算接口 Calculator

package com.home.aop;

public interface Calculator {
    public double getResult(double a, double b);
}

加法类 AddCalculator

package com.home.aop;

import org.springframework.stereotype.Component;

@Component
public class AddCalculator implements Calculator {

    @Override
    public double getResult(double a, double b) {
        return a + b;
    }
}

减法类 SubCalculator

package com.home.aop;

import org.springframework.stereotype.Component;

@Component
public class SubCalculator implements Calculator {

    @Override
    public double getResult(double a, double b) {
        return a - b;
    }
}

Client 代码

package com.home.aop;

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

public class AopMain {
    public static void f(){
        g();
    }

    public static void g(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-aop.xml");

        Calculator addCal = (Calculator) ctx.getBean("addCalculator");
        Calculator subCal = (Calculator) ctx.getBean("subCalculator");

        System.out.println(addCal.getResult(3, 1));
        System.out.println(subCal.getResult(3, 1));
    }
}

执行结果

Jul 05, 2016 10:10:00 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5d79a4c9: startup date [Tue Jul 05 22:10:00 CST 2016]; root of context hierarchy
Jul 05, 2016 10:10:00 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
4.0
2.0

2.2 小结

上面例子中, 加法类和减法类中只包含了业务代码, 我们可以将视为两个Target(目标), 下面我们就利用AOP技术为这两个Target加入通知。

2.3 AOP 前置通知

这个任务的需求很简单。

  1. 不能修加法类和减法类
  2. 在加法类和减法类的getResult()方法执行时, 在业务代码执行前输出两个参数(log).

首先maven里要引入这些lib

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

        <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${org.springframework.version}</version>
 </dependency>

 <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.8</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.8</version>
    </dependency>

然后在bean-config xml里加入下面这个句话, enable spring aop 功能

<!--  enable @Aspect -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

然后新建1个类LoggingAspect

package com.home.aop;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Component;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
@Component
public class LoggingAspect {

    //@Before("execution(public double com.home.aop.AddCalculator.getResult(double,double))")
    @Before("execution(public * com.home.aop.*.*(..))")
    public void beforeExecute(JoinPoint joinPoint){
        String classname = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("before Execute! --class name: " + classname + ", method name: " + methodName + " " + args );
    }

}

方法beforeExecute的意思就是我们要为目标执行之前 而注入的方法。

上面@Before 注解表示这是1个前置通知。 括号里面的就是PointCut(切点), 上面说过了, 相当于数据库里的查询条件

然后Spring AOP 会根据PointCut 查找出所有符合条件的目标。

内容就很简单了, 无非就是输出被执行的类名方法名和参数…

执行结果:

Jul 06, 2016 12:30:03 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6924181b: startup date [Wed Jul 06 00:30:03 CST 2016]; root of context hierarchy
Jul 06, 2016 12:30:03 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [bean-aop.xml]
before Execute! --class name: AddCalculator, method name: getResult [3.0, 1.0]
4.0
before Execute! --class name: SubCalculator, method name: getResult [3.0, 1.0]
2.0

三 总结

项目中, 如果不用AOP 我们往往要为每个方法添加日志代码, 十分难于维护, 可读性也大大下降, 而AOP的出现, 就能解决这些问题。

Spring AOP四种实现方式  http://www.linuxidc.com/Linux/2016-10/135993.htm

Spring AOP自定义注解方式实现日志管理 http://www.linuxidc.com/Linux/2015-11/125019.htm

Spring AOP进行日志记录  http://www.linuxidc.com/Linux/2015-11/124731.htm

使用Spring AOP进行性能监控  http://www.linuxidc.com/Linux/2012-07/64681.htm

利用Spring AOP 更新Memcached 缓存策略的实现  http://www.linuxidc.com/Linux/2012-03/56503.htm

Spring AOP的两种代理  http://www.linuxidc.com/Linux/2015-11/125017.htm

Spring AOP的注解实例 http://www.linuxidc.com/Linux/2015-11/125018.htm

本文永久更新链接地址http://www.linuxidc.com/Linux/2016-12/138188.htm

linux
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款