spring使用之旅(二) ---- AOP的使用
- 什么是AOP?
- AOP基本概念
- AOP使用--注解方式
- AOP使用--XML方式
- 实例--日志
写在最前面的(源码地址):
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();
}
}
spring使用之旅(二) ---- AOP的使用的更多相关文章
- Spring学习记录(十二)---AOP理解和基于注解配置
Spring核心之二:AOP(Aspect Oriented Programming) --- 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...
- spring源码分析(二)Aop
创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...
- Spring学习之旅(五)--AOP
什么是 AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善. OO ...
- Spring学习之旅(二)--容器
在 Spring 应用中,所有的对象都在 Spring 容器(container) 里,容器负责对象的创建.配置.装配并管理它们的整个生命周期. Spring 容器 Spring 容器 并不是只有一个 ...
- spring(二) AOP之AspectJ框架的使用
前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以 ...
- [ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)
一.Spring的Bean管理(注解方式) 1.1 什么是注解 要使用注解方式实现Spring的Bean管理,首先要明白什么是注解.通俗地讲,注解就是代码里的特殊标记,使用注解可以完成相应功能. 注解 ...
- spring学习(二) ———— AOP之AspectJ框架的使用
前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以 ...
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- Spring学习笔记(二)Spring基础AOP、IOC
Spring AOP 1. 代理模式 1.1. 静态代理 程序中经常需要为某些动作或事件作下记录,以便在事后检测或作为排错的依据,先看一个简单的例子: import java.util.logging ...
- 学习 Spring (十二) AOP 基本概念及特点
Spring入门篇 学习笔记 AOP: Aspect Oriented Programming, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 主要功能是:日志记录.性能统计.安全控 ...
随机推荐
- ansible编译httpd playbook示例
以下是playbook的内容.它的处理流程是: 1.先在本地下载apr,apr-util,httpd共3个.tar.gz文件. 2.解压这3个文件. 3.安装pcre和pcre-devel依赖包. 4 ...
- MFC中应用对象的成员:窗口指针m_pMainWnd说明
CVC_MFC_firstDlg dlg; //定义对话框对象m_pMainWnd = &dlg; //这个定义的对话框 dlg 成为主窗口 应用程序对象成员变量m_pMainWnd是一个窗 ...
- VS2010灵活运用快捷操作功能(总结)
转载于:http://blog.csdn.net/trassion/article/details/7667814 1.快速using(这个的快捷键是ctrl+.) 2.快速回到之前编辑的代码页面现在 ...
- 带你深度解析Maven
一.What`s Maven? Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说 ...
- 有关Ajax跨域请求的解决方案
前言 最近博主在赶项目进度.所以微信二次开发那边的博文一直没有更新.后续时间会慢慢记录这个学习历程的.来年公司要开发微信小程序.到时也会记录一下历程. 闲话少说,今天在工作中遇到了SpringMVC接 ...
- Eclipse上Maven环境配置使用 (全)
Eclipse上Maven环境配置使用 (全) 1. 安装配置Maven: 1.1 从Apache网站 http://maven.apache.org/ 下载并且解压缩安装Apache Maven. ...
- Targets选项下Other linker flags的设置
-ObjC:加了这个参数后,链接器就会把静态库中所有的Objective-C类和分类都加载到最后的可执行文件中 -all_load:会让链接器把所有找到的目标文件都加载到可执行文件中,但是千万不要随便 ...
- ext.net在使用水晶报表时页面无数据显示,并报错误Uncaught ReferenceError: bobj is not defined.
一.错误描述 在公司做项目的时候,有时会需要用到水晶报表显示数据,水晶报表在ASP.NET中使用时没有问题,winform项目开发也没有问题,但是在ext.net开发使用时却报错了,错误:Uncaug ...
- SpringBoot初步
1.创建maven 项目 quickstart类型 2.pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" ...
- CentOS 7.x上gitlab搭建教程(https可用,邮件可用)
目录 知识要求 搭建感想 搭建过程 参考 知识要求: nginx基础知识 搭建感想 注:以下是我搭建gitlab时的思考,需要nginx的基础知识,Docker的基础知识才容易理解,与下面的搭建过程是 ...