本文可作为北京尚学堂spring课程的学习笔记

首先谈谈什么是AOP 它能干什么

AOP Aspect Oriented Programming(面向切面的编程)

什么叫面向切面?

就是我们可以动态的在原来的程序逻辑前后(或其他特定的位置)加上我们自己想要的其他逻辑 这个原始的程序并不"知道" 我们在他前后做了补充

就例如我在前几篇文章里谈动态代理时说的 我要记录坦克运行所耗的时间 在它运动之前记录时间点 运动后记录时间点 然后记录时间差 而这个记录时间的动作与坦克本身的运动是没有关系的

既然谈到了动态代理 我建议如果没有看过动态代理这个设计模式的朋友在学习aop之前最好还是先看看 否则会有听不懂看不懂的危险(个人推荐 大家可以看看我前几篇文章 从坦克聊代理模式)



AOP有如下的几个概念

JoinPoint

PointCut

Aspect(切面)

Advice

Target

Weave

不过我现在并不准备和大家聊这个 等我们做出一个实例后 再说



我们还是做这样一个例子 在用户对数据库做插入操作前后 加一点别的操作

先是完整的项目图

最基本的spring

首先我们提炼出一个接口 往数据库里插入一个用户

package com.bjsxt.dao;

import com.bjsxt.model.User;

public interface UserDao {
    public void save(User u);

}

下面是UserDao的实现类UserDaoMysql 为了简便 我们只是打印出已经写入mysql

package com.bjsxt.dao;

import org.springframework.stereotype.Component;
import com.bjsxt.model.User;

@Component
public class UserDaoMysql implements UserDao {

    @Override
    public void save(User u) {
        // TODO Auto-generated method stub
        System.out.println("已经写入mysql");
    }
}

相应的service 不解释了

package com.bjsxt.services;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;
import com.bjsxt.dao.UserDao;
import com.bjsxt.dao.UserDaoMysql;

import com.bjsxt.model.User;

@Component
public class UserService {

    @Resource
    private UserDao userDaoMysql;

    public UserDao getUserDao() {
        return userDaoMysql;
    }

    public void setUserDao(UserDaoMysql userDao) {
        this.userDaoMysql = userDao;
    }

    public void Save(User u){
        userDaoMysql.save(u);
    }
}

相应的配置xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:security="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="

		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

		<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
		<context:annotation-config/>
		<context:component-scan base-package="com.bjsxt"/>
	<!--	<aop:aspectj-autoproxy></aop:aspectj-autoproxy> -->
</beans>

这是测试函数 最简单的spring应用

package com.gc.test;

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

import com.bjsxt.model.User;
import com.bjsxt.services.UserService;
import com.gc.action.HelloWorld;

public class TestHelloWorld
{
    public static void main(String args[])
    {
        ApplicationContext actx=new ClassPathXmlApplicationContext("Spring-Customer.xml");
        User user=new User();
        UserService u=(UserService)actx.getBean("userService");
        u.Save(user);
    }
}

至于User就不赘述了 User里面一个username一个password 然后是getset方法



测试结果很简单 就是打印出一句

已经写入mysql

现在我们就给这个插入动作的前后加上日志记录功能

package com.bjsxt.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogInterceptor {
    @Pointcut("execution(public * com.bjsxt.services.UserService.Save(..))")
    public void myMethod(){};

    @Before("execution(public * com.bjsxt.services.UserService.Save(..))")
    public void beforess() {
        System.out.println("method before");
    }

    @After("execution(public * com.bjsxt.services.UserService.Save(..))")
    public void afterss() {
        System.out.println("method after");
    }

//    @Around("myMethod()")
//    public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
//        System.out.println("method around start");
//        pjp.proceed();
//        System.out.println("method around end");
//    }

}

写好这个类后 得引入四个jar文件

如下图

aopalliance-1.0.jar    aspectjrt.jar   aspectjweaver-1.6.12.jar  cglib-nodep-2.1_3.jar

多说几句 如出现error at ::0 can't find referenced pointcut这个问题

是因为 引入的aspectjweaver 版本太低

我自己用的是jdk1.7 eclipse3.7 spring3.2.0 aspectjweaver1.6.12

再一方面就是就是把xml里面

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

前后的注释去掉

///////////////以下为2015年10月2日 修订

最近在看spring的说明文档的时候,看到下面的示例:

咦?befor中还可以直接写方法,而不用加execution?

这个befor里面加不加execution有什么区别么?

百度了半天,没有找到区别,似乎都是加execution的

先不管了,试试不加execution的,结果报下面这个错误

error at ::0 can't find referenced pointcut

我升级了aspectjweaver,从1.8.6到1.7.3到1.7.4都是不行呀!!

在磨蹭了很近之后,我得出一个结论,before后面必须得加execution

后来,看了pointcut之后,我的结论又被推翻了,请看后面关于pointcut的说明

///////////////以上为2015年10月2日 修订

运行后结果

method before

已经写入mysql

method after



好 大功告成 现在咱们慢慢聊聊aop的实现过程

aop的过程

第一关于cglib

如果看了动态代理的朋友 现在肯定要抛出一个问题

     我们要代理的类 不是在jdk里要实现InvocationHandler接口么? 这里怎么没有

     (如果你没有想到这个问题 说明你动态代理掌握的还不够)

答案是spring用的是cglib这个工具来直接操纵二进制文件 自然就不用实习接口了 看看上面那个所需要的jar包图 是不是有一个cglib jar

第二 几个术语

JoinPoint

@Before("execution(public * com.bjsxt.services.UserService.Save(..))")
    public void beforess() {
        System.out.println("method before");
    }

上面的代码表示 我要在com.bjsxt.services包下UserService类里面的Save方法之前(参数不论 返回值不论 另外这个之前是因为 @Befor 而不是函数名 所以我给函数名后面加了ss 以示区别)执行beforess这个方法

这下说的够清楚了吧

com.bjsxt.services包下UserService类里面的Save方法就是JoinPoint

/////////////////以下为2015年10月2日修订

咱们再说说说execution里面的表达式

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)

            throws-pattern?)

modifiers-pattern指的是方法的修饰符,就是public private protected 后面有一个问号 表示这个参数可以不要

ret-type-pattern 返回值 不用多说

declaring-type-pattern 表示特定的类

name-pattern 特定的方法

param-pattern 参数

throws-pattern 异常的类型

关于这个,给几个例子大家看看就明白了了

掌握这么多就够了,剩下的那种特别复杂的语法,大家不看也罢

另外

	@Before("execution( public * com.bjsxt.service..*.add(..) ) ||  execution( public * com.bjsxt.service..*.delete(..)  ) ")
	public void before() {
		System.out.println("my method before");
	}

多个表达式也可以用||连接起来,既然有||那自然就用&&,有!喽

/////////////////以上为2015年10月2日修订

PointCut

@Pointcut("execution(public * com.bjsxt.services.UserService.Save(..))")
    public void myMethod(){};

这里我还是用了上面的那个JoinPoint 其实execution括号里面可以写一个"方法簇"

这一系列JoinPoint 合起来就是一个PointCut 我们给他起了一个名字叫myMethod

其他的操作在指定插入点的时候在Advice里面指定这个名字即可

    @Around("myMethod()")
    public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("method around start");
        pjp.proceed();
        System.out.println("method around end");
    }

请注意,这个myMehod不是一个方法,而是一个pointcut的名字,这pointcut是com.bjsxt.services.UserService.Save的所有不同返回值,不同参数的方法组成的集合

现在想想之前那个before,它后面是可以直接加一个字符串,并且不以execution开头

问题是那个字符串并不是指定在什么地方织入我们的逻辑,它只是用来表面我引用的是那个pointcut而已

Aspect

我们这个LogInterceptor类就是一个切面 类的前面我们打上了标签 @Aspect

Advice

就是执行附加逻辑的时间

@Before @After @AfterReturning    @AfterThrowing @Around

最常用的也就是这几个 前两个我想不用解释了

/////////////////一下为2015年10月2日修订

@AfterThrowing这个是抛出异常后执行的,这个我们举个例子

	@AfterThrowing(pointcut="myMethod()",throwing="ex")
	public void afterThrowdMethod(DataAccessException ex)  {
		System.out.println(ex.getMessage()+" ex message");
		System.out.println("after throw");
	}
上面的代码说明,在myMethod指定的pointcut范围内,如果抛出了DataAccessException类型的exception,就执行下面的方法(当然,如果抛出的是其他类型的,这里就不管了)
AfterThrowing在什么地方有应用?
struts2的异常处理。

////////////////以下为2015年10月2日修订

@Around是可以同时在所拦截方法的前后执行一段逻辑

例如上面那个aroundMethod方法 单个运行它(为什么是单个 大家可能会想pjp.proceed()前面的逻辑和 @Before里面的逻辑哪个先执行呢? 这个没有什么意思 大家自己做个实验就ok 总之一句话 不要把 前后的逻辑顺序按照这个around和before来区别)

结果是

method around start

已经写入mysql

method around end

当我把aroundMethod方法改成如下

@Around("myMethod()")
    public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("method around start");
        pjp.proceed();
        System.out.println("method around end");
        pjp.proceed();
        System.out.println("method around endssss");
    }

结果就成了

method around start

已经写入mysql

method around end

已经写入mysql

method around endssss

这个 @Around里面pjp.proceed();就是指代原始逻辑的运行

Weave

weave是编织的意思 aop是这样的运作的 "横"着运行业务逻辑 "纵"着加入其它如日志权限等操作。

就像织布一样横着看是一种逻辑 纵着看又是一种逻辑。

Weave一般翻译成织入 就表示这个过程。

使用xml配置aop

这个很简单,大家一看就懂

package com.bjsxt.aop;

import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;

@Component
public class LogInterceptorXML {

	public void beforess() {
		System.out.println("my method before");
	}

	public void afterThrowdMethod(DataAccessException ex)  {
		System.out.println("after throw");
	}

}
<?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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
	<context:component-scan base-package="com.bjsxt"/>

	<aop:config>
		<aop:pointcut
			expression="execution( public * com.bjsxt.service..*.add(..) ) ||
						execution( public * com.bjsxt.service..*.delete(..)  ) "
			id="myPointCut" />

		<aop:aspect ref="logInterceptorXML">
			<aop:before method="beforess" pointcut-ref="myPointCut" />
		</aop:aspect>

	</aop:config>

</beans>

如果会用注解形式的aop,那么xml式的真的就是看一眼就懂。

这里,我还得说,相比较于注解形式,我们更得掌握xml式的配置。 为什么? 因为spring与hibernate结合后,aop的一大亮点就是声明式事务管理

参考资料

http://***/forum/posts/list/281.html

http://outofmemory.cn/code-snippet/3025/spring-AOP-Around-Before-After-differentiate

http://blog.csdn.net/wanglang3081/article/details/17164207

http://jinnianshilongnian.iteye.com/blog/1415606

Spring AOP 初探的更多相关文章

  1. Spring入门(9)-AOP初探

    Spring入门(9)-AOP初探 0. 目录 什么是面向切面编程 AOP常见术语 AOP实例 参考资料 1. 什么是面向切面编程 Aspect Oriented Programming(AOP),即 ...

  2. spring.net AOP初探

    AOP是什么? 面向切面编程,在OO中有一个开放关闭原则,及对修改关闭,对扩展开放.AOP可以说是设计模式的集合加强版,使用代理.工厂.策略等等模式,来实现方法的结合.这样说还比较模糊,我们先往下看. ...

  3. Spring学习之旅(六)Spring AOP工作原理初探

    AOP(Aspect-Oriented  Programming,面向切面编程)是Spring提供的关键技术之一. AOP基于IoC,是对OOP(Object-Oriented Programming ...

  4. spring源码学习(一)--AOP初探

    LZ以前一直觉得,学习spring源码,起码要把人家的代码整体上通读一遍,现在想想这是很愚蠢的,spring作为一个应用平台,不是那么好研究透彻的,而且也不太可能有人把spring的源码全部清楚的过上 ...

  5. Spring AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)

    1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下让你不知所措,心想着:管不得很多人都和我说AOP多难多难.当我看进去以后, ...

  6. Spring AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)(转)

    1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下让你不知所措,心想着:管不得很多人都和我说AOP多难多难.当我看进去以后, ...

  7. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  8. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  9. spring aop

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...

随机推荐

  1. WUOJ-ACM :1003: 零起点学算法78——牛牛

    武汉科技大学ACM :1003: 零起点学算法78--牛牛Problem Description牛牛是一种纸牌游戏,总共5张牌,规则如下: 如果找不到3张牌的点数之和是10的倍数,则为没牛: 如果其中 ...

  2. 关于go语言的通道

    1.记一次gorountine导致的泄漏 在项目中使用https://github.com/deckarep/golang-set这个三方包造成了gorountine泄漏.先来看一下这个包的迭代器设置 ...

  3. 蚂蚁代理免费代理ip爬取(端口图片显示+token检查)

    分析 蚂蚁代理的列表页大致是这样的: 端口字段使用了图片显示,并且在图片上还有各种干扰线,保存一个图片到本地用画图打开观察一下: 仔细观察蓝色的线其实是在黑色的数字下面的,其它的干扰线也是,所以这幅图 ...

  4. MySQL PHP 语法

    MySQL PHP 语法 MySQL 可应用于多种语言,包括 PERL, C, C++, JAVA 和 PHP. 在这些语言中,MySQL在PHP的web开发中是应用最广泛. 在本教程中我们大部分实例 ...

  5. Latex:TexStudio的使用

    http://blog.csdn.net/pipisorry/article/details/54565608 Texsdudio 快捷键 The keyboard shortcuts can be ...

  6. Cassandra User 问题汇总(1)------------repair

    Cassandra Repair 问题 问1: 文档建议每周或者每月跑一次full repair.那么如果我是使用partition rangerepair,是否还有必要在cluster的每个节点上定 ...

  7. RxJava(11-线程调度Scheduler)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51821940 本文出自:[openXu的博客] 目录: 使用示例 subscribeOn原理 ...

  8. 剑指Offer——常用SQL语句、存储过程和函数

    剑指Offer--常用SQL语句.存储过程和函数 常用SQL语句 1.在MySQL数据库建立多对多的数据表关系 2.授权.取消授权 grant.revoke grant select, insert, ...

  9. Android TV开发总结(四)通过RecycleView构建一个TV app列表页(仿腾讯视频TV版)

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52854131 前言:昨晚看锤子手 ...

  10. Shell脚本生成网页版相册浏览器

    今天学到了一招,那就是使用脚本制作一款网页版相册浏览器.先上图吧. 必备基础 操作系统: 以linux为内核的操作系统都行 编程语言:Shell(bash)脚本,相关基础知识即可 下载工具:wget ...