aop编程就是使用了动态代理,但是spring的aop来做面向切面编程会比较麻烦,Aspectj这个包也提供了类似于spring的aop编程,通过注解来配置以及表达式的配置实现并且可以动态给对象添加方法(但是要做类型转换),用起来十分方便
1.@Before
前置通知,在方法执行前调用
2.@AfterReturning
返回通知,在方法返回结果之后调用
3.@AfterThrowing
异常通知,在方法抛出异常后调用
4.@After
后置通知,在方法执行完成调用
5.@Around
环绕通知,包含上面4种,围绕着方法执行
1.@DeclareParents
用来动态给对象添加属性和方法
2.@Pointcut
定义切入点,用于后面方法表达式的复用
1、2、3、4种通知,调用方法的时候可以自动传入一个JoinPoint 对象,用来获取方法名字或者代理对象或者传入的参数等等,其中第2种AfterReturning可以定义returning = “xxx” 来获取方法调用之后的返回值,第3种AfterThrowing可以定义throwing=”xxx” 来获取抛出的异常对象
5种通知,调用方法适合可以传入一个ProceedingJoinPoint 对象,同上一样可以获取各种信息并且有执行目标对象的方法proceed()
上面的5种的execution表达式都可以配合注解来进行筛选执行方法
execution(* *(..)) && @annotation(myAnnotation)
下面写了个小例子:
首先创建一个目标类:
@Component
public class Mytest01 {
public String show() {
System.out.println("show time");
return "show 执行完成";
}
}
创建一个类,里面包含第一种到第四种的注解:
@Component
@Aspect
public class Mytest02 {
@Before("execution(* spring06.Mytest01.*(..))")
public void before(JoinPoint joinPoint) {
showJoinPoint(joinPoint);
System.out.println("@Before 执行");
}
@AfterReturning(value = "execution(* spring06.Mytest01.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, String result) {
System.out.println("@AfterReturning 执行,返回值为:" + result);
}
@AfterThrowing(value = "execution(* spring06.Mytest01.*(..))", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
System.out.println("@AfterThrowing 执行,异常为:" + exception.getMessage());
}
@After("execution(* spring06.Mytest01.*(..))")
public void after(JoinPoint joinPoint) {
System.out.println("@After 执行");
}
/**
* joinPoint 常用获取属性
*
* @param joinPoint
*/
private void showJoinPoint(JoinPoint joinPoint) {
//获取调用的方法名字
System.out.println("JoinPoint 常用:" + joinPoint.getSignature().getName());
//获取目标类
System.out.println("JoinPoint 常用:" + joinPoint.getTarget());
//获取代理对象
System.out.println("JoinPoint 常用:" + joinPoint.getThis());
//获取参数
System.out.println("JoinPoint 常用:" + joinPoint.getArgs());
}
}
创建另一个类,里面只包含第五种的注解,以及DeclareParents和Pointcut注解:
@Component
@Aspect
public class Mytest03 {
/**
* 给被切面编程的Mytest01对象添加NewServiceImpl里面的参数和方法
*/
@DeclareParents(value = "spring06.Mytest01", defaultImpl = NewServiceImpl.class)
private NewService service;
/**
* 这里是定义一个切入点,后面的方法都可以直接引用
*/
@Pointcut("execution(* spring06.Mytest01.*(..))")
public void myPoint() {
}
@Around("myPoint()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("@Around 开始");
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
System.out.println("@Around 获取出异常:" + throwable.getMessage());
} finally {
System.out.println("@Around 执行结束");
}
System.out.println("@Around 开始返回结果");
return result;
}
}
DeclareParents注解需要用到的接口:
public interface NewService {
String newShow(String show);
}
DeclareParents注解需要用到的实现类:
public class NewServiceImpl implements NewService {
public String newShow(String show) {
System.out.println("这是调用的newShow,value = " + show);
return show;
}
}
创建一个main入口:
public static void main(String[] args) {
AbstractApplicationContext appContext = new ClassPathXmlApplicationContext("application06.xml");
Mytest01 test = appContext.getBean(Mytest01.class);
test.show();
NewService service = (NewService) test;
service.newShow("new");
appContext.registerShutdownHook();
}
xml配置:
<context:component-scan base-package="spring06"/>
测试前4中注解,先注释掉Mytest03的Aspect注解,执行main方法,结果如下:
测试第5种的环绕注解,注释掉Mytest02的Aspect注解,执行main方法,结果如下:
小demo完成,我们可以使用它去做一些日志的处理比如Controller被访问前后都打印日志,就可以用aspectj来完成,可以参考java注解结合aspectj AOP进行日志打印