本方法与面向切面编程(一)和(二)的实现结果是一样的,但是实现方法不一样。
再介绍一遍spring AOP中的专业术语(其中连接点、切入点、切面很重要):
1.通知(Advice):通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
2.连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3.切入点(Pointcut):通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,spring中允许我们方便的用正则表达式来指定
4.切面(Aspect):通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5.引入(Introduction):引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6.目标(Target):即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)
7.代理(proxy):应用通知的对象,详细内容参见设计模式里面的代理模式
8.织入(Weaving):把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
示例代码:
BizImpl.java(实现类的接口):
/*
* 时间:2016年9月28日20:41:10
* 程序功能:实现类的接口
* */
package com.sc.biz;
public interface BizImpl {
public void buy(String username,String bookname,double price);
public void pinlun(String username,String pinlunleirong);
}
BookBizImpl.java(实现类):
/*
* 时间:2016年9月28日20:41:35
* 程序功能:实现类,继承接口并实现方法
* */
package com.sc.biz;
public class BookBizImpl implements BizImpl {
public void buy(String username, String bookname, double price) {
System.out.println("用户名:"+username);
System.out.println("书名:"+bookname);
System.out.println("价格"+price);
}
public void pinlun(String username, String pinlunleirong) {
System.out.println("评论人:"+username);
System.out.println("评论类容:"+pinlunleirong);
}
}
DaiLiLei1.java(使用xml配置的代理类):
/*
* 时间:2016年9月28日20:42:12
* 程序功能:代理类
* */
package com.sc.daililei;
import java.util.Arrays;
import java.util.Date;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class DaiLiLei1 {
// 后置通知,jp.getSignature().getName()可以获取当前执行的方法名
// Arrays.toString(jp.getArgs())获取属性名
public void logBefore(JoinPoint jp) {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 开始执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
public void logAfter(JoinPoint jp) {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
public void logAround(ProceedingJoinPoint jp) throws Throwable {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
Object obj = jp.proceed();
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
}
DaiLiLei.java(使用注解配置的代理类):
/*
* 时间:2016年9月28日20:42:12
* 程序功能:代理类,使用注解方配置
* */
package com.sc.daililei;
import java.util.Arrays;
import java.util.Date;
import org.aspectj.lang.JoinPoint;
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;
@Aspect
public class DaiLiLei {
// 该方法相当于在这个位置定义一个切点,以下方法都使用这个切点
@Pointcut("execution(* com.sc.biz.BookBizImpl .*(..))")
public void heHe() {
}
// 后置通知,jp.getSignature().getName()可以获取当前执行的方法名
// Arrays.toString(jp.getArgs())获取属性名
@Before("heHe()")
public void logBefore(JoinPoint jp) {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 开始执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
@After("heHe()")
public void logAfter(JoinPoint jp) {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
@Around("heHe()")
public void logAround(ProceedingJoinPoint jp) throws Throwable {
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
Object obj = jp.proceed(); //此处为前置通知和后置通知的分界点
System.out.println("[系统日志][" + new Date().toLocaleString() + "] 结束执行:"
+ jp.getSignature().getName() + "参数是:"
+ Arrays.toString(jp.getArgs()));
}
}
applicationContext.xml(Spring配置文件):
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans ;
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop ;
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 使用xml配置 -->
<!-- <bean id="bookBizImpl" class="com.sc.biz.BookBizImpl"></bean>
<bean id="daiLiLei" class="com.sc.daililei.DaiLiLei1"></bean>
<aop:config>
<aop:aspect id="dai" ref="daiLiLei">
<aop:pointcut expression="execution(* com.sc.biz.BookBizImpl .*(..))" id="p"/>
<aop:before method="logBefore" pointcut-ref="p"/>
<aop:after method="logAfter" pointcut-ref="p"/>
<aop:around method="logAround" pointcut-ref="p"/>
</aop:aspect>
</aop:config> -->
<!-- 使用注解配置 -->
<bean id="bookBizImpl" class="com.sc.biz.BookBizImpl"></bean>
<bean id="daiLiLei" class="com.sc.daililei.DaiLiLei"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
切面其实是一个普通的类,然后在配置文件中将它声明称切面
关于在配置文件中声明切面:
<aop:pointcut expression="execution(* com.sc.biz.BookBizImpl .*(..))" id="p"/>
expression里面的值设置方法:
1、任意公共方法的执行:
expression(public * *(..))
2、任意一个以set开始的方法的执行:
expression(* set* (..))
3、AccountService接口定义的任何方法的执行:
expression(* com.sc.service. AccountService.*(..) )
4、在service包中定义的任何方法的执行:
expression(* com.sc.service.*.*(..))
expression(* com.sc.service..*.*(..))
在配置文件中使用切面:
例如:<aop:before method="logBefore" pointcut-ref="p"/>
声明通知:
常用: <aop:before/>