- 一、Aop
- 二、动态代理举例
- 三、Aop五种通知
- 1. 添加依赖3个
- 2. 定义切点
- 3. 新建切面
- 4. 推荐使用@Pointcut注解定义切点
Aspect Oriented Programming ,面向切面编程,这是对面向对象思想的一种补充。
面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能,常见的使用场景非常多:
- 日志
- 事务 --公共代码抽取出来
- 数据库 *** 作
- …
以上 *** 作有很多模板化的代码
AOP常见概念
切点:要添加代码的地方,称作切点
通知(增强):通知就是向切点动态添加的代码
切面:切点+通知
连接点:切点的定义
AOP实现
Aop实际上是基于Java的动态代理来实现的
Java中的动态代理有两种实现方式:cglib,jdk
- 定义一个计算器接口
package org.kk.aop; public interface MyCalculator { int add(int a,int b); }
- 实现接口
MyCalculatorImpl是接口的实现类
package org.kk.aop; public class MyCalculatorImpl implements MyCalculator{ @Override public int add(int a, int b) { return a+b; } }
- 实现代理类 利用Proxy类
上面的实体经过代理类的加工,返回了一个新实体(添加了方法)
package org.kk.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class CalculatorProxy { public static Object getInstance(MyCalculatorImpl myCalculator) { //第一个参数:classLoader,第二个参数实现的接口,第三个参数为new的InvocationHandler return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法执行前"); Object invoke = method.invoke(myCalculator, args); System.out.println("方法执行后"); return invoke; //也可以return 99,那么调用add后的结果也就是99了 } }); } }
- 调用,获得一个代理类加工后的对象
public class Main { @Test public void test1() { MyCalculatorImpl myCalculator=new MyCalculatorImpl(); MyCalculator calculator = ((MyCalculator) CalculatorProxy.getInstance(myCalculator)); int add=calculator.add(3,4); System.out.println("add="+add); } }
调用结果:
方法执行前 方法执行后 add=7
- 总结
Aop的原理与上面的动态代理一样,都是不需要改变原来的代码(Impl),添加新的方法(通过代理类)
Aop的通知类型有五种:
在以上代码中,在method.invoke之前执行——前置通知,在method.invoke之后执行——后置通知,异常通知,返回通知,环绕通知
1. 添加依赖3个2. 定义切点org.springframework spring-context5.2.19.RELEASE org.aspectj aspectjweaver1.9.7 org.aspectj aspectjrt1.9.7
两种方式:使用注解(不推荐)、使用规则
使用注解
- 自定义Action注解
package org.kk.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Action { }
- Impl中对方法添加自定义注解
package org.kk.aop; import org.springframework.stereotype.Component; @Component public class MyCalculatorImpl implements MyCalculator{ @Override @Action //对需要增强的方法添加这个自定义注解 侵入式--要改源码 public int add(int a, int b) { return a+b; } @Override public void min(int a, int b) { System.out.println(a+"-"+b+"="+(a-b)); } //然后想要给这两个方法增加一个日志 在不改变源码的前提下 }
- 新建类,用于定义通知
@Component
@Aspect
其中的方法用于通知:前置通知 @Before("@annotation(org.javaboy.app.Action)" ) 注解的包路径
package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect //表示这是一个切面 五种通知 public class LogAspect { @Before("@annotation(Action)")//有Action注解的方法 执行之前会有前置通知 public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法开始执行了"); } }
配置类添加注解,需要开启自动代理
package org.kk.aop; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; @Configuration @ComponentScan //还要开启自动代理 @EnableAspectJAutoProxy public class JavaConfig { }
测试
@Test public void test2() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class); MyCalculator bean = context.getBean(MyCalculator.class); int res = bean.add(3, 4); System.out.println(res); }
结果
add方法开始执行了 7
四种通知方法
package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect //表示这是一个切面 五种通知 public class LogAspect { @Before("@annotation(Action)")//有Action注解的方法 执行之前会有前置通知 public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法开始执行了"); } //后置通知 @After("@annotation(Action)") public void after(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法执行结束了"); } //返回通知 @AfterReturning(value = "@annotation(Action))",returning = "r") public void returning(JoinPoint joinPoint,Integer r)//类型不匹配方法返回类型,是不会返回结果的;可以用Object { String name=joinPoint.getSignature().getName(); System.out.println(name+"返回通知:"+r);//r是方法执行的结果 } @AfterThrowing(value = "@annotation(Action)",throwing = "e") public void afterThrowing(JoinPoint joinPoint,Exception e) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法异常通知:"+e.getMessage()); } @Around("@annotation(Action)") public Object around(ProceedingJoinPoint pjp) throws Throwable { //这个有点类似于method.invoke方法,我们可以在这个方法前后分别添加日志,相当于前置和后置通知 Object proceed=pjp.proceed(new Object[]{5,5}); return proceed; } }
//优化1 通过方法来定义切点 下面的注解都改 @Before("pointcut()") @Pointcut("@annotation(Action)") public void pointcut() { }
非侵入式定义切点:
4. 推荐使用@Pointcut注解定义切点//非侵入式定义切点 @Pointcut("execution(* org.kk.aop.service.*.*(..))") //任意类 任意方法 参数类型、个数任意 public void pointcut() { } package org.kk.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect //表示这是一个切面 五种通知 public class LogAspect { // //优化1 通过方法来定义切点 下面的注解都改 @Before("pointcut()") // @Pointcut("@annotation(Action)") // public void pointcut() // { // // } //非侵入式定义切点 @Pointcut("execution(* org.kk.aop.service.*.*(..))") //任意类 任意方法 参数类型、个数任意 public void pointcut() { } @Before("pointcut()")//有Action注解的方法 执行之前会有前置通知 public void before(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法开始执行了"); } //后置通知 @After("pointcut()") public void after(JoinPoint joinPoint) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法执行结束了"); } //返回通知 @AfterReturning(value = "pointcut())",returning = "r") public void returning(JoinPoint joinPoint,Integer r)//类型不匹配方法返回类型,是不会返回结果的;可以用Object { String name=joinPoint.getSignature().getName(); System.out.println(name+"返回通知:"+r);//r是方法执行的结果 } @AfterThrowing(value = "pointcut()",throwing = "e") public void afterThrowing(JoinPoint joinPoint,Exception e) { String name=joinPoint.getSignature().getName(); System.out.println(name+"方法异常通知:"+e.getMessage()); } @Around("pointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { //这个有点类似于method.invoke方法,我们可以在这个方法前后分别添加日志,相当于前置和后置通知 Object proceed=pjp.proceed(new Object[]{5,5}); //return了10 return proceed; } }
xml配置Aop
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)