AOP方法上的注解有:
@Before():这个是前置调用,在方法之前掉调用
@After() 这个称为后置调用,不管方法执行是正常还是异常都会调用
@Around() 这个表示的是环绕 在调用方法之前和之后调用 一般用来记录日志,需要joinPoint.proceed()推动目标进行
@AfterReturning()这个表示的是正常运行后,返回数据的时候调用
@AfterThrowing()这个表示的是抛出异常的时候调用
@Aspect:告诉容器这是一个切面类
@Pointcut() :切入点表达式的位置
@EableAspectjAutoProxy:开启基于注解的aop模式,原来是在xml中配置
他有一个属性 proxyTargetClass默认是false.
- 即使用jdk默认代理模式,AspectJ代理模式是CGLIB代理模式
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop) (设置proxyTargetClass属性为false)
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
注:jdk代理是重新在原来的类中动态插入代码生成新的代理类(.java文件),在重新编译成.class文件;而CGLIB代理是直接在class文件中插入二进制代码,不会生成新的代理类。所以CGLIB代理效率比jdk代理效率高。 操作字节码可以使用ASM框架技术实现。
例子:
1.需要导入包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
2.业务类
public class Caculator {
public Integer cal(int a,int b) {
System.out.println("Caculator ..cal");
return a/b;
}
}
3.切面类
@Aspect
public class LogsAspects {
@Pointcut("execution( * com.wusu.aop.Caculator.*(..))")
public void pointcut(){};
@Before("pointcut()")
public void logStart(JoinPoint joinPoint){
System.out.println("开始计算"+joinPoint.getSignature().getName()+": 参数是:"+ Arrays.asList(joinPoint.getArgs()).toString());
}
@After("pointcut()")
public void logEnd(JoinPoint joinPoint){
System.out.println("结束计算"+joinPoint.getSignature().getName());
}
//这个JoinPoint必须放在第一个才能使用
@AfterReturning(value = "pointcut()",returning = "result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println("开始返回"+joinPoint.getSignature().getName()+",return :"+ result);
}
@Around("pointcut()") //这个必须要有返回值值,否则正常的有返回值的没有结果
public Object logRound(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕执行前。。。参数是:"+joinPoint.getArgs());
Object object = joinPoint.proceed(joinPoint.getArgs());
System.out.println("环绕执行后。。。参数是:"+joinPoint.getArgs());
return object;
}
@AfterThrowing(value = "pointcut()",throwing = "exception")
public void logThrowing(Exception exception){
System.out.println("抛出异常"+exception);
}
}
对于@PointCut里面的表达式解释:
定义切入点表达式
1>:定义一个没有实现的方法
2>:在这个方法上 使用切入点表达式的注解Pointcut
execution ()你记成语法规则
第一个 * 代表的是返回注入方法的返回数据的类型
第二个 * 代表的是UserDAO这个类里面的所有方法
“..” 代表的是注入方法里面的参数类型 这里因为可以是任意类型 所以使用 “..” ,这里是两个点
4. 配置类,业务类和切面类都需要放入IOC容器中
需要使用@EnableAspectJAutoProxy开启自动代理
@EnableAspectJAutoProxy
@Configuration
public class MainConfig {
@Bean
public Caculator caculator(){
return new Caculator();
}
@Bean
public LogsAspects logsAspects(){
return new LogsAspects();
}
}
5.测试类,需要从IOC容器中获取这个业务类才是被代理后的类
public class TestAop {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Caculator caculator = applicationContext.getBean(Caculator.class);
int cal = caculator.cal(16, 0);
}
}
结果显示:
开始计算cal: 参数是:[16, 0]
Caculator ..cal
结束计算cal
抛出异常java.lang.ArithmeticException: / by zero