1.AOP开发中的相关术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只
支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置
通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类
动态地添加一些方法或 Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
Proxy(代理) :一个类被 AOP 织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
2.Spring使用AspectJ进行AOP的开发:XML的方式
2.1导包
spring-aop-4.2.4.RELEASE.jar com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-4.2.4.RELEASE.jar
2.2引入Spring的配置文件 :AOP约束(xml catalog引入约束,xml配置文件中导入命名空间)
2.3编写目标类
package com.lzj.service.Impl;
import com.lzj.service.UserService;
@Service(value="useService")//注意在xml中配置自动beansao'miao<context:component-scan base-package="com.lzj"></context:component-scan>
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("添加记录");
}
@Override
public void delete() {
System.out.println("删除记录");
}
@Override
public void find() {
System.out.println("查找记录");
}
@Override
public void update() {
System.out.println("更新记录");
}
}
2.4编写通知类
前置通知 :在目标方法执行之前执行.
后置通知 :在目标方法执行之后执行
环绕通知 :在目标方法执行前和执行后执行
异常抛出通知:在目标方法执行出现 异常的时候 执行
最终通知 :无论目标方法是否出现异常 最终通知都会 执行.
package com.lzj.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvise {
//前置通知
public void before(){
System.out.println("前置通知");
}
//后置通知
public void afterReturn(){
System.out.println("后置通知 (只出现在没有发生异常)");
}
//环绕通知 常用
//需要ProceedingJoinPoint接口作为参数
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知的之前部分");
Object proceed = pjp.proceed();
System.out.println("环绕通知的之后部分");
return proceed;
}
//异常抛出通知
public void afterException(){
System.out.println("异常出现之后的通知");
}
//最终通知
public void after(){
System.out.println("后置通知(不管是否发生异常)");
}
}
2.5切入点表达式execution(表达式)
表达式:
[方法访问修饰符]方法返回值 包名.类名.方法名(方法的参数)
public * cn.itcast.spring.dao.*.*(..) //参数任意用..表示
* cn.itcast.spring.dao.*.*(..)
* cn.itcast.spring.dao.*UserDao.*(..)
* cn.itcast.spring.dao..*.*(..)
例如定义切入点表达式 execution (* com.sample.service.impl..*. *(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点..表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
2.6配置xml完成增强<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
<context:component-scan base-package="com.lzj"></context:component-scan>
<aop:config>
<!-- 配置切入点 expression填写切入点表达式 -->
<!-- 切记写上execution前缀 刚刚就没写 启动报错! -->
<aop:pointcut expression="execution(* com.lzj.service.Impl..*.add(..))" id="pointcut"/>
<!-- 配置切面 切面是切入点和通知的结合 -->
<aop:aspect ref="myAdvise">
<!-- 指定名为before方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturn" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试方法
package com.lzj.demo;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.lzj.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.add();
}
}
测试结果:
3.注解方式配置
非常简单 擦亮你的双眼 !
只需要在通知类中注解写上切入点(PointCut)
第一步 很重要!!!!!!!!!!!!!!!!!!!我第一步忘记做了,足足debug了两个小时 真tm气!暴打梦婷
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
<context:component-scan base-package="com.lzj"></context:component-scan>
<!-- 3.开启使用注解完成织入 这一步非常重要-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
第二步 在通知类中织入
package com.lzj.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Repository;
//声明这是一个切面(切入点+通知)
@Repository(value="myAdvise")
@Aspect
public class MyAdvise {
@Before("execution(* com.lzj.service.*Impl..*.add(..))")
//前置通知
public void before(){
System.out.println("前置通知");
}
//后置通知
@AfterReturning("execution(* com.lzj.service.*Impl..*.add(..))")
public void afterReturn(){
System.out.println("后置通知 (只出现在没有发生异常)");
}
@Around("execution(* com.lzj.service.*Impl..*.add(..))")
//环绕通知 常用
//需要ProceedingJoinPoint接口作为参数
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知的之前部分");
Object proceed = pjp.proceed();
System.out.println("环绕通知的之后部分");
return proceed;
}
@AfterThrowing("execution(* com.lzj.service.*Impl..*.add(..))")
//异常抛出通知
public void afterException(){
System.out.println("异常出现之后的通知");
}
@After("execution(* com.lzj.service.*Impl..*.add(..))")
//最终通知
public void after(){
System.out.println("后置通知(不管是否发生异常)");
}
}