本文可作为北京尚学堂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. mongo数据删除和游标

    数据删除 db.集合.remove(删除条件,是否只删除一个数据);默认删多条(false)true删除一条db.集合.remove({}) 删除所有元素但集合还在db.集合.drop() 删除集合 ...

  2. Oracle中SQL语句分类

    Oracle中SQL语句分类如下:1.DML语句 insert/delete/update/select/merge/explan plan/lock table2.DDL语句 create/atlt ...

  3. async/await 的一些知识

    博文 Don't Block on Async Code What is the purpose of "return await" in C#? Any difference b ...

  4. CTR预估算法

    GBRT(Gradient Boost Regression Tree)渐进梯度回归树,XGBoost是GBRT的一个工程实现 LR(Logistics Regression )逻辑回归 Spark ...

  5. jQuery 遍历 – 过滤

    缩小搜索元素的范围 三个最基本的过滤方法是:first(), last() 和 eq(),它们允许您基于其在一组元素中的位置来选择一个特定的元素. 其他过滤方法,比如 filter() 和 not() ...

  6. MySQL DATEDIFF() 函数

    定义和用法 DATEDIFF() 函数返回两个日期之间的天数. 语法 DATEDIFF(date1,date2) date1 和 date2 参数是合法的日期或日期/时间表达式. 注释:只有值的日期部 ...

  7. Spring Security基于Java配置

    Maven依赖 <dependencies> <!-- ... other dependency elements ... --> <dependency> < ...

  8. Kafka系列之-Kafka Protocol实例分析

    本文基于A Guide To The Kafka Protocol文档,以及Spark Streaming中实现的org.apache.spark.streaming.kafka.KafkaClust ...

  9. activiti源码分析

    http://blog.csdn.net/vote/candidate.html?username=qq_30739519 欢迎大家投票吧谢谢

  10. 【SSH系列】hibernate映射 -- 一对一双向关联映射

    开篇前言 上篇博文[SSH进阶之路]hibernate映射--一对一单向关联映射,小编介绍了一对一的单向关联映射,单向是指只能从人(Person)这端加载身份证端(IdCard),但是反过来,不能从身 ...