AOP(Aspect-Oriented-Programming)
面向切面编程
首先来模拟下AOP:这里要用到动态代理的有关知识。动态代理是能在运行过程中根据接口的类型动态的调用实现该接口的类,是相对于静态的来说的。我们还是先来看实现。
我们写了一个LogInterceptor类来在接口方法调用前后加入一部份处理工作这里仅仅在方法调用前后向后台输出两句字符串,代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogInterceptor implements InvocationHandler{
private Object target;
public void beforeMethod(){
System.out.println("save start");
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
beforeMethod();
m.invoke(target, args);
return null;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
其中的target是要代理的原始对象此处为UserDAO,invoke用来调用原始对象的方法,测试代码如下:
public void testProxy(){
UserDAO userDAO = new UserDAOImpl();
LogInterceptor li = new LogInterceptor();
li.setTarget(userDAO);
UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), new Class[]{UserDAO.class}, li);
userDAOProxy.save(new User());
}
首先获取一个业务接口的实现对象为UserDAO;再获取一个InvocationHandler实现为LogInterceptor对象;创建动态代理对象userDAOProxy;通过动态代理对象调用save方法,此时会在原始对象UserDAOImpl.save()方法前输出一句字符串。
使用Annotation来实现spring的aop:
在beans.xml中需要加入这样的代码:
xmlns:aop=http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
上面表示命名空间,下面是为了用annotation做的配置,它是用aspectj来实现动态代理的
然后把LogInterceptor重写代码如下:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogInterceptor {
@Before("execution(public void com.bjsxt.dao.impl.UserDAOImpl.save(com.bjsxt.model.User))")
public void before(){
System.out.println("method start");
}
}
@Aspect关键字是在LogInterceptor类中引入一个切面,@component关键字是把LogInterceptor作为一个组件@Before意思是在调用save方法前先执行before方法,整个过程就是面向切面的编程的过程。
织入语法:
· the execution of any public method:
execution(public * *(..))
表示具有任何返回值的任何方法
· the execution of any method with a name beginning with "set":
execution(* set*(..))
以set开始的任何方法
· the execution of any method defined by the AccountService
interface:
execution(* com.xyz.service.AccountService.*(..))
在com.xyz.service包下的类AccountService中的任何方法
· the execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))
在com.xyz.service包下的任何类中的任何方法(注意和上面的区别)
· the execution of any method defined in the service package or a sub-package:
execution(* com.xyz.service..*.*(..))
在com.xyz.service下的任何子包下任何类的任何方法
所以可以将上面LogInterceptor类中@Before中的内容改为如下:
"execution(public * com.bjsxt.dao..*.*(..))"
当需要在返回是插入内容可以使用关键字@AfterReturning用法和@Before一样
当有很多切入点是可以用@Pointcut定义一个切入点集合,源程序如下:
@Aspect
@Component
public class LogInterceptor {
@Pointcut("execution(public * com.bjsxt.dao..*.*(..))")
public void myMethod(){}
@Before("myMethod()")
public void before(){
System.out.println("method before");
}
@AfterReturning("myMethod()")
public void afterReturning(){
System.out.println("method after returning");
}
}
@Around关键字将“包围“匹配方法代码如下:
@Around("myMethod()")
public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("method around start");
pjp.proceed();
System.out.println("method around end");
}
注意在spring3.1.1版本中要用@pointcut需要再添加asm-all.jar否则会抛NoClassDefFoundError
使用xml来实现spring的AOP:
过程如下当执行到service类中的add方法时发现符合expression表达式,而且在这个表达式上面有一个切面,它引用了一个切面逻辑类对象logInterceptor在这里面要求在add方法执行之前还要执行before方法,然后找到对应的方法并执行。