学习aop,我们需要先学习一些专业术语
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7. Aspect(切面): 是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
在连接点和切入点在现实的生活中,有一个特别形象的比喻,就好比我们去公共厕所,公共厕所一共有5个便池,但是有3个便池被使用了,那么用aop的术语来说,就是有5个连接点,3个切入点,切入点都是被增强的,连接点是所有的。
下面说一下jdk的动态代理,首先,我们需要找到我们的目标类,然后我们需要找到我们的切入点,找到我们的通知。
下面看代码
public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("a_proxy.a_jdk addUser"); } @Override public void updateUser() { System.out.println("a_proxy.a_jdk updateUser"); } @Override public void deleteUser() { System.out.println("a_proxy.a_jdk deleteUser"); } }
public class MyAspect { public void before(){ System.out.println("鸡首"); } public void after(){ System.out.println("牛后"); } }
public class MyBeanFactory { public static UserService createService(){ //1 目标类 final UserService userService = new UserServiceImpl(); //2切面类 final MyAspect myAspect = new MyAspect(); /* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面 * Proxy.newProxyInstance * 参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。 * 一般情况:当前类.class.getClassLoader(); * 目标类实例.getClass().get... * 参数2:Class[] interfaces 代理类需要实现的所有接口 * 方式1:目标类实例.getClass().getInterfaces() ;注意:只能获得自己接口,不能获得父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驱动 --> DriverManager 获得接口 Connection * 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般采用匿名内部 * 提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke * 参数31:Object proxy :代理对象 * 参数32:Method method : 代理对象当前执行的方法的描述对象(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) * 参数33:Object[] args :方法实际参数 * */ UserService proxService = (UserService)Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("addUser"==method.getName()){ //前执行 myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userService, args); //后执行 myAspect.after(); return obj; }else{ return method.invoke(userService, args); } } }); return proxService; } }
public class TestJDK { @Test public void demo01(){ UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }
运行结果,
鸡首
a_proxy.a_jdk addUser
牛后
a_proxy.a_jdk updateUser
a_proxy.a_jdk deleteUser
我在我的代理类中,根据method的方法名称,进行判断,如果方法名称是addUser,那么我执行增强,也就是加入通知。
说完了jdk的动态代理,下面我们说一下使用cglib的动态代理
首先,我们在使用spring的时候,spring的core帮我们整合的了cglib的核心jar,我们在使用cglib的时候,cglib不是像jdk的动态代理,他实现代理类,不需要借口,其次,他的代理类本质上时候被代理的子类,通过继承的方式,来实现增强。下面我们看一下代码。
public class MyBeanFactory { public static UserServiceImpl createService(){ //1 目标类 final UserServiceImpl userService = new UserServiceImpl(); //2切面类 final MyAspect myAspect = new MyAspect(); // 3.代理类 ,采用cglib,底层创建目标类的子类 //3.1 核心类 Enhancer enhancer = new Enhancer(); //3.2 确定父类 enhancer.setSuperclass(userService.getClass()); /* 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口 * intercept() 等效 jdk invoke() * 参数1、参数2、参数3:以invoke一样 * 参数4:methodProxy 方法的代理 * * */ enhancer.setCallback(new MethodInterceptor(){ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //前增强 myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userService, args); // * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系) methodProxy.invokeSuper(proxy, args); //后增强 myAspect.after(); return obj; } }); //3.4 创建代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } }
只要把上面的接口删掉,就可以,剩余的代码完全相同,下面我来说一下,使用cglib主要是对enhancer的核心类进行操作,enhancer的中文意思是增强的意思,我们首先创建一个enhance对象。然后对enhancer对象进行封装。
使用AspectJ进行动态代理
public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常通知 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最终通知"); } }
public interface UserService { public void addUser(); public String updateUser(); public void deleteUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("d_aspect.a_xml addUser"); } @Override public String updateUser() { System.out.println("d_aspect.a_xml updateUser"); int i = 1/ 0; return "阳志就是屌"; } @Override public void deleteUser() { System.out.println("d_aspect.a_xml deleteUser"); } }
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1 创建目标类 --> <bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean> <!-- 2 创建切面类(通知) --> <bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean> <!-- 3 aop编程 <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法) ref 切面类引用 <aop:pointcut> 声明一个切入点,所有的通知都可以使用。 expression 切入点表达式 id 名称,用于其它通知引用 --> <aop:config> <aop:aspect ref="myAspectId"> <aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/> <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method : 通知,及方法名 pointcut :切入点表达式,此表达式只能当前通知使用。 pointcut-ref : 切入点引用,可以与其他通知共享切入点。 通知方法格式:public void myBefore(JoinPoint joinPoint){ 参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等 例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <!-- 3.2后置通知 ,目标方法后执行,获得返回值 <aop:after-returning method="" pointcut-ref="" returning=""/> returning 通知方法第二个参数的名称 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){ 参数1:连接点描述 参数2:类型Object,参数名 returning="ret" 配置的 例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> --> <!-- 3.3 环绕通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值类型:Object 方法名:任意 参数:org.aspectj.lang.ProceedingJoinPoint 抛出异常 执行目标方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <!-- 3.4 抛出异常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二个参数名称 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 参数1:连接点描述对象 参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <!-- 3.5 最终通知 --> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>