一、AspectJ实现AOP
Aspect定义:是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。Spring2.0之后 为了简化 AOP编程,支持AspectJ 技术
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面。
新版本Spring框架,建议使用AspectJ方式来开发AOP ,而不需要使用传统 Spring AOP 编程。
@Aspect是定义切面
二、通知的类型
@Before 前置通知,相当于BeforeAdvice@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行无论程序是否正常执行,最终通知的代码会得到执行 类似于 try{}cathc{} finally{}
1.切点的表达式定义
标识切面织入到哪些类的那些方法当中,通过execution函数,可以定义切点的方法切入。语法: execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
2.特殊符号
a: * 代表0到多个任意字符b: .. 放在方法参数中 ,代表任意个参数 ,放在包名后面表示当前包及其所有子包路径
c: + 放在类名后,表示当前类及其子类,放在接口后,表示当前接口及其实现类
3.举例说明
a:execution(public * *(..)) 表示任意的public方法b:execution(* set*(..)) 表示任意包含以set字符开头的方法
c:execution(* com.tz.spring.dao.impl.*.*(..)) 表示com.tz.spring.dao.impl的任意类的任意方法
d:execution(* com.tz.spring.dao..*.*(..))
表示com.tz.spring.dao包下面的所有方法以及所有子包下面的所有方法
e:execution(* com.tz.spring.dao.UserDao+.*(..))
f:execution(* add(String,int)) 带包任意返回类型的add方法 有两个参数,类型分别为String,int
三、AspectJ+spring的环境搭建
1. 导入AspectJ的开发包(AspectJ依赖AOP的jar包)2.引入aop的约束
3.基于xml的配置
首先创建一个IUserDao 的接口
public interface IUserDao {
public void addUser();
public void getUser();
public void updateUser();
public void deleteUser();
}
实现这个接口
import org.springframework.stereotype.Component;
import com.dqsy.spring.dao.IUserDao;
@Component(value="userDao")
public class IUserDaoImpl implements IUserDao {
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("添加用户");
}
@Override
public void getUser() {
// TODO Auto-generated method stub
System.out.println("查找用户");
}
@Override
public void updateUser() {
// TODO Auto-generated method stub
System.out.println("更改用户");
}
@Override
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("删除用户");
int i = 1/0;
}
}
编写切面MyAspect
package com.dqsy.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 定义通知,基于XML配置的方式
* @author Administrator
*
*/
public class MyAspect {
//定义前置通知
public void beforeAdvice(){
System.out.println("前置通知,方法之前运行");
}
// 后置通知
public void afterAdvice(){
System.out.println("后置通知,方法之后运行");
}
//定义后置通知,含返回值
public void afterAdvice(Object result){
System.out.println("后置通知,方法之后运行,带返回值 : " + result);
}
// 定义环绕通知
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知,方法之前运行");
//这个表示目标对象的方法的运行
pjp.proceed();
System.out.println("环绕通知,方法之后运行");
}
//定义异常通知
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//定义异常通知
public void afterThrowingAdvice(Exception ex){
System.out.println("异常通知 " + ex.toString());
}
// 最终通知
public void lastAdvice(){
System.out.println("最终通知");
}
}
编写applicationContext.xml
<!-- 注册UserDao -->
<bean id="userDao" class="com.dqsy.spring.dao.impl.IUserDaoImpl"></bean>
<!-- 注册切面 -->
<bean id="myAspect" class="com.dqsy.spring.aspect.MyAspect"></bean>
<!-- AspectJ的AOP配置 -->
<aop:config>
<!-- * add*(..) : 第一个*表示包路径,空格,add*:表示方法名称的表达式匹配, (..) :表示方法的参数 -->
<aop:pointcut expression="execution(* add*(..))" id="beforePointCut" />
<aop:pointcut expression="execution(* update*(..))" id="afterPointCut" />
<aop:pointcut expression="execution(* get*(..))" id="aroundPointCut" />
<aop:pointcut expression="execution(* delete*(..))" id="throwingPointCut" />
<aop:pointcut expression="execution(* delete*(..))" id="lastPointCut" />
<!-- 这里是配置切面的通知/顾问 的方法 -->
<aop:aspect ref="myAspect">
<aop:before method="beforeAdvice" pointcut-ref="beforePointCut"/>
<aop:after-returning method="afterAdvice(java.lang.Object)"
pointcut-ref="afterPointCut" returning="result"/>
<aop:around method="aroundAdvice" pointcut-ref="aroundPointCut"/>
<aop:after-throwing method="afterThrowingAdvice(java.lang.Exception)"
pointcut-ref="throwingPointCut" throwing="ex"/>
<aop:after method="lastAdvice" pointcut-ref="lastPointCut"/>
</aop:aspect>
</aop:config>
编写测试方法
package com.dqsy.spring.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.dqsy.spring.dao.IUserDao;
public class AspectJTest {
private ApplicationContext ctx=null;
@Before
public void init(){
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void aspectJTest(){
IUserDao userDao = (IUserDao) ctx.getBean("userDao");
userDao.addUser();
System.out.println();
userDao.getUser();
System.out.println();
userDao.updateUser();
System.out.println();
userDao.deleteUser();
}
}
结果:
基于注解的配置
同样的我们重新写一个MyAspectAnnotation基于注解的切面
package com.dqsy.spring.aspect;
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;
@Aspect
public class MyAspectAnnotation {
//定义前置通知
@Before(value="execution(* add*(..))")
public void beforeAdvice(){
System.out.println("前置通知,方法之前运行");
}
// 后置通知
@AfterReturning(value="execution(* update*(..))")
public void afterAdvice(){
System.out.println("后置通知,方法之后运行");
}
//定义后置通知,含返回值
@AfterReturning(value="execution(* update*(..))", returning="result")
public void afterAdvice(Object result){
System.out.println("后置通知,方法之后运行,带返回值 : " + result);
}
// 定义环绕通知
@Around(value="execution(* get*(..))")
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知,方法之前运行");
//这个表示目标对象的方法的运行
pjp.proceed();
System.out.println("环绕通知,方法之后运行");
}
//定义异常通知
@AfterThrowing(value="execution(* delete*(..))")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//定义异常通知
@AfterThrowing(value="execution(* delete*(..))", throwing="ex")
public void afterThrowingAdvice(Exception ex){
System.out.println("异常通知 " + ex.toString());
}
// 最终通知
@After(value="execution(* delete*(..))")
public void lastAdvice(){
System.out.println("最终通知");
}
}
重新写一个配置文件
<!-- 注册IUserDao -->
<bean id="userDao" class="com.dqsy.spring.dao.impl.IUserDaoImpl"></bean>
<!-- 注册MyAspect -->
<bean id="myAspect" class="com.dqsy.spring.aspect.MyAspectAnnotation"></bean>
<!-- 开启aop的自动扫入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试结果同上一样
我们也可以通过前面的学习,通过注解将bean自动装配,同样也是ok的
四、小结
AOP是面向对象编程的一个强大补充。通过AspectJ,我们现在可以把之前分散在应用各处的行为放入可重用的模块中。我们显示地声明在何处如何应用该行为。这有效减少了代码冗
余,并让我们的类关注自身的主要功能。
Spring提供了一个AOP框架,让我们把切面插入到方法执行的周围。现在我们已经学会如何
把通知织入前置、后置和环绕方法的调用中,以及为处理异常增加自定义的行为。
关于在Spring应用中如何使用切面,我们可以有多种选择。通过使用@AspectJ注解和简化
的配置命名空间,在Spring中装配通知和切点变得非常简单。
最后,当Spring AOP不能满足需求时,我们必须转向更为强大的AspectJ。对于这些场景,我
们了解了如何使用Spring为AspectJ切面注入依赖。
此时此刻,我们已经覆盖了Spring框架的基础知识,了解到如何配置Spring容器以及如何为
Spring管理的对象应用切面。正如我们所看到的,这些核心技术为创建松散耦合的应用奠定
了坚实的基础。