转自:http://www.cnblogs.com/yulinfeng/p/7811965.html,https://www.cnblogs.com/liuruowang/p/5711563.html
通知类型介绍
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint
代码实例
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /** * Created by wuwf on 17/4/27. * 日志切面 */ @Aspect @Component public class LogAspect { @Pointcut("execution(public * com.example.demo.controller.*.*(..))") public void webLog(){} @Before("webLog()") public void deBefore(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 System.out.println("URL : " + request.getRequestURL().toString()); System.out.println("HTTP_METHOD : " + request.getMethod()); System.out.println("IP : " + request.getRemoteAddr()); System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { ret="ADASDAASDASDADA"; // 处理完请求,返回内容 System.out.println("方法的返回值 : " + ret); } //后置异常通知 @AfterThrowing("webLog()") public void throwss(JoinPoint jp){ System.out.println("方法异常时执行....."); } //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 @After("webLog()") public void after(JoinPoint jp){ System.out.println("方法执行后....."); } //环绕通知,环绕增强,相当于MethodInterceptor @Around("webLog()") public Object arround(ProceedingJoinPoint pjp) { System.out.println("方法环绕start....."); try { Object o = pjp.proceed(); System.out.println("方法环绕proceed,结果是 :" + o); // String AA=(String) o; // AA="CES"; return "CESAAA"; } catch (Throwable e) { e.printStackTrace(); return null; } } }
通知执行的优先级
进入目标方法时,先进入Around,再进入Before
退出目标方法时,先进入Around,再进入AfterReturning,最后才进入After。
注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!同时使用也没啥意义。
切入点的定义和表达式
Spring AOP支持的切入点指示符:
(1)execution:用来匹配执行方法的连接点
A:@Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示
方法的任意参数个数
B:@Pointcut("within(com.aijava.springcode.service.*)")
within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
C:@Pointcut("this(com.aijava.springcode.service.UserService)")
this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
D:@Pointcut("bean(userService)")
bean也是非常常用的,bean可以指定IOC容器中的bean的名称
动态代理介绍
JDK提供:
- 基于反射,效率低
- 只能代理实现了接口的目标对象
CGLIB:
- 不需要目标对象实现接口
- 基于字节码实现
- 不能代理final方法(它的动态代理实际是生成目标对象的子类)
从Proxy.newProxyInstance开始,来研究JDK是如何生成代理类的。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
该方法有3个参数,了解JVM类加载的可能知道确定为同一个类需要有2个条件:
-
类的全限定名称相同
-
加载类的类加载器相同
要想生成目标对象的代理首先就要确保其类加载器相同,所以需要将目标对象的类加载器作为参数传递;其次JDK动态代理技术需要代理类和目标对象都继承自同一接口,所以需要将目标对象的接口作为参数传递;最后,传递InvocationHandler,这是主角,因为我们对目标对象的增强逻辑在这个实现类中,传递该对象使得代理类能够对其进行调用。
在Proxy.newProxyInstance方法中创建代理类的过程主要有3步:
在开头提到了CGLib的性能比JDK高,这实际上并不准确。或许这在特别条件下的确如此,因为在我实测发现JDK8的动态代理效率非常高,甚至略高于CGLib,但是在JDK6的环境下的效率就显得比较低了。所以,通常所说的CGLib性能比JDK动态代理要高,是传统的挂念,实际上Java一直都在不断优化动态代理性能,在比较高版本的JDK条件下可以放行大胆的使用JDK原生的动态代理。