一.AOP(面向切面编程)中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样
类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方
法,因为spring只支持方法类型的连接点,实际上joinpoint还可
以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置
通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态地
添加一些方法或Field.
二.实现AOP
<?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">
<!-- 配置AOP 注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:component-scan base-package="com.zyy.service"></context:component-scan>
<context:component-scan base-package="com.zyy.aop"></context:component-scan>
</beans>
代理对象:
package com.zyy.service;
/**
* Created by CaMnter on 2014/8/20.
*/
public interface PersonService_5 {
String user = null;
public void save(String name);
public void update(Integer personId, String name);
public void delete(Integer personId);
public String getName();
public String getUser();
}
SpringAOP设计:
package com.zyy.aop;
import com.zyy.service.PersonService_5;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;
/**
* Created by CaMnter on 2014/8/21.
*/
/**
* 切面 使用注解
* <p/>
* 必须交给Spring容器管理
*/
/*
* @Aspect (切面) 声明切面
*
* @Pointcut 声明切入点
*
* @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")
* 当执行com.zyy.service.impl.PersonServiceBean_5类下所有方法的时候就会被拦截到
*
* @Before("anyMethod()") 前置通知 这里表示在执行anyMethod方法前先执行doBefore
*
* @AfterReturning("anyMethod()") 后置通知 这里表示在执行anyMethod方法后先执行doAfterReturning
*
* @After("anyMethod()") 最后通知 无论怎样 最后都执行的
*
* @AfterThrowing("anyMethod()") 出现异常的时候的通知
*
* @Around("anyMethod()") 环绕通知 (应用比较多) 适合做权限
* 注意:如果使用了环绕通知 就一定要执行 pjp.proceed(); 否则业务Bean中拦截的 Before...等方法不会执行
*
*
* @Before("anyMethod() && args(name)") 表示 在切入点
* @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
* 还要求改切入点类的方法只有有一个是String类型 返回给doBefore 作为参数
*
*
* @AfterReturning(pointcut = "anyMethod()", returning = "user") 表示 在切入点
* @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
* 还要求改切入点类的方法返回一个String的时候 返回给doAfterReturning 作为参数
*
*/
@Aspect
@Service("springInterceptor")
public class SpringInterceptor {
//@Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")
/*
* 切入点是 PersonService_5接口
*/
@Pointcut("execution(* com.zyy.service.PersonService_5.*(..))")
//定一个切入点
private void anyMethod() {
}
/*
* @Before("anyMethod() && args(name)") 表示 在切入点
* @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
* 还要求改切入点类的方法只有有一个是String类型 返回给doBefore 作为参数
*/
@Before("anyMethod() && args(xxx)")
public void doBefore(String xxx) {
System.out.println("***** SpringInterceptor 前置通知 " + xxx + " *****");
}
/*
* @AfterReturning(pointcut = "anyMethod()", returning = "user") 表示 在切入点
* @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
* 还要求改切入点类的方法返回一个String的时候 返回给doAfterReturning 作为参数
*/
@AfterReturning(pointcut = "anyMethod()", returning = "user")
public void doAfterReturning(String user) {
System.out.println("***** SpringInterceptor 后置通知 " + user + " *****");
}
@After("anyMethod()")
public void doAfter() {
System.out.println("***** SpringInterceptor 最终通知 *****");
}
@AfterThrowing(pointcut = "anyMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("***** SpringInterceptor (异常)通知 " + e + " *****");
}
/*
* @Around("anyMethod()") 环绕通知 (应用比较多) 适合做权限
* 注意:如果使用了环绕通知 就一定要执行 pjp.proceed(); 否则业务Bean中拦截的 Before...等方法不会执行
*/
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
//因为我们已知切入点@Pointcut("execution(* com.zyy.service.PersonService_5.*(..))")
//所以可以得到代理对象
PersonService_5 personService_5 = (PersonService_5) pjp.getTarget();
Object result = null;
//权限过滤 如果有user值
if (!(personService_5.getUser() == null || "".equals(personService_5.getUser()))) {
System.out.println("***** SpringInterceptor (进入)环绕通知 *****");
System.out.println("***** 代理对象有user值,允许调用save *****");
//如果不执行这个方法,那么代理对象的方法也不执行了
result = pjp.proceed();
System.out.println("***** SpringInterceptor (退出)环绕通知 *****");
return result;
}
System.out.println("***** 代理对象无user值,不允许调用 *****");
return result;
}
}
代理对象实现bean:
有user值的bean:
package com.zyy.service.impl;
/**
* Created by CaMnter on 2014/8/21.
*/
import com.zyy.service.PersonService_5;
import org.springframework.stereotype.Service;
/**
* 实现Spring AOP
*
* 有user值
*/
@Service("personService_5")
public class PersonServiceBean_5 implements PersonService_5 {
private String user = "CaMnter";
public PersonServiceBean_5() {
}
public PersonServiceBean_5(String user) {
this.user = user;
}
public void save(String name) {
System.out.println("***** save " + name + " *****");
//throw new RuntimeException("Remember") ;
}
public void update(Integer personId, String name) {
System.out.println("***** update *****");
}
public void delete(Integer personId) {
System.out.println("***** delete *****");
}
public String getName() {
return "Save you from anything 07";
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
无user值的bean:
package com.zyy.service.impl;
/**
* Created by CaMnter on 2014/8/21.
*/
import com.zyy.service.PersonService_5;
import org.springframework.stereotype.Service;
/**
* 实现Spring AOP
*
* 无user值
*/
@Service("personService_7")
public class PersonServiceBean_7 implements PersonService_5 {
private String user = null;
public PersonServiceBean_7() {
}
public PersonServiceBean_7(String user) {
this.user = user;
}
public void save(String name) {
System.out.println("***** save " + name + " *****");
//throw new RuntimeException("Remember") ;
}
public void update(Integer personId, String name) {
System.out.println("***** update *****");
}
public void delete(Integer personId) {
System.out.println("***** delete *****");
}
public String getName() {
return "Save you from anything 07";
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
junit4.4测试代码:
有user值的测试:
@Test
public void springAopTest_1() {
AbstractApplicationContext abstractApplicationContext = new ClassPathXmlApplicationContext("beans_3.xml");
System.out.println("***** SpringAOP动态代理 *****");
System.out.println("***** 动态代理对象有user值 *****");
System.out.println("");
System.out.println("***** 调用save()并且有参数String类型 *****");
PersonService_5 personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_5");
personService_5.save("07");
System.out.println("");
System.out.println("***** getName()返回String类型 *****");
personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_5");
personService_5.getName();
}
无user值的测试:
@Test
public void springAopTest_2() {
AbstractApplicationContext abstractApplicationContext = new ClassPathXmlApplicationContext("beans_3.xml");
System.out.println("***** SpringAOP动态代理 *****");
System.out.println("***** 动态代理对象无user值 *****");
System.out.println("");
System.out.println("***** 调用save()并且有参数String类型 *****");
PersonService_5 personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_7");
personService_5.save("07");
System.out.println("");
System.out.println("***** 调用save()返回String类型 *****");
personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_7");
personService_5.getName();
}
由此可见,SpringAOP动态代理足够强大。其原理也是很简单:如果目标有接口那么
SpringAOP调用的是JDK动态代理,如果没接口SpringAOP调用用的是CGlib动态
代理,这就导致了为什么cglib-nodep.jar出现在Spring 的依赖包之中。
三.aspectj的切入点语法定义细节
@Pointcut("execution(java.lang.Stringcom.zyy.service.PersonService_5.*(..))")
拦截返回String的方法
@Pointcut("execution(* com.zyy.service.PersonService_5.*(java.lang.String,..))")
拦截第一个的参数为String类型的方法
@Pointcut("execution(!void com.zyy.service.PersonService_5.*(java.lang.String,..))")
拦截非void返回值的方法
@Pointcut("execution(java.lang.Stringcom.zyy.service.*.*(..))")
拦截com.zyy.service子包内所有的类的所有方法
@Pointcut("execution(java.lang.Stringcom.zyy.service.*.s*(..))")
拦截以s开头的方法