1. 什么是AOP?
  2. AOP基本概念
  3. AOP使用--注解方式
  4. AOP使用--XML方式
  5. 实例--日志

  

  写在最前面的(源码地址):

    https://github.com/xc83415134/spring_aop_demo

一、什么是AOP?

  AOP(Aspect Oriented Programmin)即面向切面编程(或者翻译成以切面为导向的编程模式?),一种OOP延续的编程思想,将系统中非核心业务提取出来,从而将其与其所影响的对象解耦,切面就是提取出来的功能模块。切面可以帮助我们模块化横切关注点,常见的有日志、安全、事物等。

  对于一个信用卡应用程序来说,存款、取款、帐单管理是它的主关注点,日志和持久化将成为横切整个对象结构的横切关注点。

二、AOP基本概念

以下为维基百科部分说明:

关注点(concern):对软件工程有意义的小的、可管理的、可描述的软件组成部分,一个关注点通常只同一个特定概念或目标相关联。
主关注点(core concern):一个软件最主要的关注点。
关注点分离(separation of concerns,SOC):标识、封装和操纵只与特定概念、目标相关联的软件组成部分的能力,即标识、封装和操纵关注点的能力。
方法(method):用来描述、设计、实现一个给定关注点的软件构造单位。
横切(crosscut):两个关注点相互横切,如果实现它们的方法存在交集。
支配性分解(dominant decomposition):将软件分解成模块的主要方式。传统的程序设计语言是以一种线性的文本来描述软件的,只采用一种方式(比如:类)将软件分解成模块;这导致某些关注点比较好的被捕捉,容易进一步组合、扩展;但还有一些关注点没有被捕捉,弥散在整个软件内部。支配性分解一般是按主关注点进行模块分解的。
横切关注点(crosscutting concerns):在传统的程序设计语言中,除了主关注点可以被支配性分解方式捕捉以外,还有许多没有被支配性分解方式捕捉到的关注点,这些关注点的实现会弥散在整个软件内部,这时这些关注点同主关注点是横切的。
侧面(aspect):在支配性分解的基础上,提供的一种辅助的模块化机制,这种新的模块化机制可以捕捉横切关注点。
从主关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在在整个应用程序中的变动就可以很好的管理起来。

三、AOP使用--注解方式

  1.启用AOP

  以下为启用AspectJ自动代理,同时需声明Spring aop 命名空间(注意标红的部分)

<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
<context:component-scan base-package="foo.bar"/> <!--启用aspectj自动代理-->
<aop:aspectj-autoproxy />
</beans>

  2.定义被监听类

  普通的类,待监听对象无特殊

package foo.bar.observed;

import org.springframework.stereotype.Component;

/**
* Created by xuc on 2018/1/16.
* 说话的人A
*/
@Component
public class HelloByAnnotation {
public void sayHello(String arg) {
System.out.println(arg);
}
}

  3.定义切面

  首先加入@Component注解,让spring扫描到,注入spring容器中。加入@Aspect注解,声明其为切面,再通过@Pointcut注解表面某一方法为切点,(括号内:execution表明为在方法执行时触发,*为返回任意类型,后面紧跟的为指定方法,String为接收参数类型,&&表示并且,arg为接收的参数)。其他注解:

注解 通知
@After 通知方法在目标方法返回或抛出异常后调用
@AfterReturning 通知方法在目标方法返回后调用
@AfterThrowing 通知方法在目标方法抛出异常后
@Around 通知方法在目标方法封装起来
@Before 通知方法在目标方法调用前执行
package foo.bar.observer;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; /**
* Created by xuc on 2018/1/16.
*
* 大脑活动
* 执行顺序:
* {@link Around} -> {@link Before} -> 目标方法 -> {@link Around}
* -> {@link After}
* -> {@link AfterReturning} 或 {@link AfterThrowing}
*/
@Component
@Aspect
public class brain { /**
* 定义切点
* *:返回任意
* *.sayHello:指定方法
* String:指定接收类型
* arg:指定接收参数
*/
@Pointcut("execution(* foo.bar.observed.HelloByAnnotation.sayHello(String)) && args(arg)")
public void speak(String arg){} /**
* 目标方法调用前执行
*/
@Before("speak(arg)")
public void think(String arg){
System.out.println("1.说话前要注意三思而后行:" + arg);
} /**
* 目标方法返回后执行
*/
@AfterReturning("speak(arg)")
public void listen(String arg){
System.out.println("2.说完后要虚心接受长辈的教诲");
} /**
* 目标方法抛出异常后
*/
@AfterThrowing("speak(arg)")
public void reflection(String arg){
System.out.println("3.说错话后要反思为什么");
} /**
* 目标方法前、后执行两次
*/
@Around("speak(arg)")
public void doThings(ProceedingJoinPoint joinPoint, String arg) throws Throwable {
System.out.println("4.准备干点其他事");
joinPoint.proceed();
System.out.println("4.其他事做完");
}
}

  4.执行main测试(执行前,可以先看下第5步)

package foo.bar;

import foo.bar.observed.HelloByAnnotation;
import foo.bar.observed.IDeclareHello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
excuteByAnnotion(context);
} /**
* 基于注解配置
* @param context
*/
private static void excuteByAnnotion(ApplicationContext context) {
HelloByAnnotation helloByAnnotation = context.getBean(HelloByAnnotation.class);
helloByAnnotation.sayHello("Hello world! -- by annotation"); IDeclareHello declareHello = (IDeclareHello)helloByAnnotation;
declareHello.sayBye();
}
}

  5.通过注解引入新功能

  通过aop对原类进行功能加强(装饰模式),即可以动态的对一个类添加方法(有意思不?)。

  定义一个普通的接口和实现类:

package foo.bar.observed;

/**
* Created by xuc on 2018/1/16.
*/
public interface IDeclareHello {
void sayBye();
}
package foo.bar.observed;

import org.springframework.stereotype.Component;

/**
* Created by xuc on 2018/1/16.
*
* {@link HelloByAnnotation} 的装饰类,为其添加方法
*/
@Component
public class DeclareHello implements IDeclareHello{
public void sayBye(){
System.out.println("执行再见方法:Bye!");
}
}

  定义一个切面,再通过@DeclareParents注解(就当她是个媒婆,撮合原类和加强类,哈哈哈哈...),value为原类(男方),变量为加强接口(女方),最后就可以生娃娃了……^.^

package foo.bar.declaretion;

import foo.bar.observed.DeclareHello;
import foo.bar.observed.IDeclareHello;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component; /**
* Created by xuc on 2018/1/16.
*
* 将{@link DeclareHello}介绍给{@link HelloIntroducer}
* 这是一种装饰模式,是对原类的加强
*/
@Component
@Aspect
public class HelloIntroducer { @DeclareParents(value = "foo.bar.observed.HelloByAnnotation", defaultImpl = DeclareHello.class)
public static IDeclareHello declareHello;
}

  四、AOP使用--XML方式

  与上面的基于注解方式无异,只是切面定义无需破坏原代码,可以再XML中实现,下面简单说明下。

  1.定义一个普通的类,待监听对象

package foo.bar.observed;

import org.springframework.stereotype.Component;

/**
* Created by xuc on 2018/1/17.
*/
@Component
public class HelloByXml { public void sayHello(String arg) {
System.out.println(arg);
}
}

  2.再定义一个普通的类,切面类(对,切面累,不是切糕累。。),无需声明其为切面

package foo.bar.observer;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component; /**
* Created by xuc on 2018/1/17.
* 小脑活动
*/
@Component
public class Cerebellum { public void think(String arg){
System.out.println("1.说话前要注意三思而后行:" + arg);
} /**
* 目标方法返回后执行
*/
public void listen(String arg){
System.out.println("2.说完后要虚心接受长辈的教诲s");
} /**
* 目标方法抛出异常后
*/
public void reflection(String arg){
System.out.println("3.说错话后要反思为什么");
} /**
* 目标方法前、后执行两次
*/
public void doThings(ProceedingJoinPoint joinPoint, String arg) throws Throwable {
System.out.println("4.准备干点其他事");
joinPoint.proceed();
System.out.println("4.其他事做完");
}
}

  3.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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
<context:component-scan base-package="foo.bar"/> <!--启用aspectj自动代理-->
<aop:aspectj-autoproxy /> <!-- XML方式 演示-->
<!--此处仅列举一个前置通知,其他与注解形式类似-->
<aop:config>
<aop:aspect ref="cerebellum">
<aop:pointcut id="speak" expression="execution(* foo.bar.observed.HelloByXml.sayHello(String)) and args(arg)"/>
<aop:before method="think" pointcut-ref="speak"/>
</aop:aspect>
</aop:config>
</beans>

  4.执行main测试

package foo.bar;

import foo.bar.observed.HelloByAnnotation;
import foo.bar.observed.HelloByXml;
import foo.bar.observed.IDeclareHello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class HelloApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
excuteByXml(context);
} /**
* 基于XML配置
* @param context
*/
private static void excuteByXml(ApplicationContext context) {
HelloByXml helloByXml = context.getBean(HelloByXml.class);
helloByXml.sayHello("Hello world! -- by xml");
}
}

  五、实例--日志

  以上为Spring AOP的基本使用方法,下面举一个实际开发的例子,基于自定义注解与切面结合实现异步日志入库(实际上也是个小例子。。。)。

  1.定义一个注解

package foo.bar.annotation;

import java.lang.annotation.*;

/**
* Created by xuc on 2018/1/18.
* 日志生成注解(切点)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface PrintLog {
int type();
}

  2.定义一个切面,声明上面的注解为其切点

package foo.bar.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component; /**
* Created by xuc on 2018/1/18.
*/
@Aspect
@Component
public class PrintLogOperation {
@Around("within(foo.bar.observed..*) && @annotation(printLog)")
public void offerMailPo(ProceedingJoinPoint jp, PrintLog printLog) throws Throwable {
if (printLog.type() == 0){
System.out.println("你好啊,我是一条日志...");
}
jp.proceed();
}
}

 

    环境: IDEA、Spring4.0
    参考资料: 《spring实战》

spring使用之旅(二) ---- AOP的使用的更多相关文章

  1. Spring学习记录(十二)---AOP理解和基于注解配置

    Spring核心之二:AOP(Aspect Oriented Programming) --- 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...

  2. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

  3. Spring学习之旅(五)--AOP

    什么是 AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善. OO ...

  4. Spring学习之旅(二)--容器

    在 Spring 应用中,所有的对象都在 Spring 容器(container) 里,容器负责对象的创建.配置.装配并管理它们的整个生命周期. Spring 容器 Spring 容器 并不是只有一个 ...

  5. spring(二) AOP之AspectJ框架的使用

    前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以 ...

  6. [ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)

    一.Spring的Bean管理(注解方式) 1.1 什么是注解 要使用注解方式实现Spring的Bean管理,首先要明白什么是注解.通俗地讲,注解就是代码里的特殊标记,使用注解可以完成相应功能. 注解 ...

  7. spring学习(二) ———— AOP之AspectJ框架的使用

    前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以 ...

  8. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  9. Spring学习笔记(二)Spring基础AOP、IOC

    Spring AOP 1. 代理模式 1.1. 静态代理 程序中经常需要为某些动作或事件作下记录,以便在事后检测或作为排错的依据,先看一个简单的例子: import java.util.logging ...

  10. 学习 Spring (十二) AOP 基本概念及特点

    Spring入门篇 学习笔记 AOP: Aspect Oriented Programming, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 主要功能是:日志记录.性能统计.安全控 ...

随机推荐

  1. java基础数据类型包装类

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  2. MAC系统里JDK版本切换

    1.首先安装需要的JDK版本 JDK7,JDK8则需要自己到Oracle官网下载安装对应的版本.自己安装的JDK默认路径为:/Library/Java/JavaVirtualMachines/jdk1 ...

  3. Axure学习笔记(一)

    Axture是一种快速制作原型的工具,在产品经理和体验设计师之中非常流行,不过现在产品经理比较难找,所以我只好上阵研究了一下.        经过几天的研究,看了小楼老师的一些视频,看了一些文档,做了 ...

  4. 日期函数ADD_MONTHS,MONTHS_BETWEEN,LAST_DAY,NEXT_DAY

  5. iOS 页面之间的专长动画控制器间的转换

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; line-height: 24.0px; font: 14.0px "Heiti SC Light" ...

  6. VR\AR 使用 SceneKit

    VR\AR 使用 SceneKit http://www.jianshu.com/c/70d63e3941fd

  7. 【NOIP模拟】cut

    Description F大爷热爱切树.今天他找到一棵黑白树,不到两秒钟,F大爷就把这棵树切掉了.已知原先树上共n个点,每个点都是黑点或者白点,F大爷切去若干条边后,分成的若干个连通子树中每块恰有一个 ...

  8. mouseout、mouseover和mouseleave、mouseenter区别

    今天在使用鼠标事件时,用错了mouseout,于是做个测试总结. 结论: mouseenter:当鼠标移入某元素时触发. mouseleave:当鼠标移出某元素时触发. mouseover:当鼠标移入 ...

  9. Ionic2 cordova angular2 打包到Android apk环境搭建

    一.前言 前段时间,公司有个APP项目需要支持不同平台,于是采用了Ionic2 + cordova + angular2,在搭建环境过程中遇到了不少问题,刚好最近有时间整理出来. 二.开发环境搭建 参 ...

  10. MQ NameServer模块划分

    上图是之前讨论确定的系统架构(后续内容会按照这个架构来叙述),其中: NameServer做Broker的服务发现,即客户端可以通过NameServer拿到Broker的信息 Broker汇报数据到N ...