学习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>
相关推荐
5. **配置代理**:Spring会根据目标对象是否实现了接口来决定使用JDK动态代理还是CGLIB代理。如果目标对象实现了接口,Spring会选择JDK动态代理。动态代理类会继承自`java.lang.reflect.Proxy`,并实现目标对象的...
**Spring AOP与AspectJ详解** 在现代软件开发中,面向切面编程(Aspect-Oriented Programming,简称AOP)是一种强大的设计模式,它允许我们分离关注点,将横切关注点(如日志、事务管理、权限控制等)与核心业务...
本篇文章将深入探讨Spring的AOP(面向切面编程)特性,包括XML配置和@AspectJ注解方式的实践,同时结合源码分析,帮助开发者更全面地理解和应用这一重要概念。 **AOP概述** 面向切面编程(Aspect-Oriented ...
Spring AOP通过代理模式实现,可以创建两种类型的代理:JDK动态代理和CGLIB代理。前者适用于实现了接口的类,后者适用于未实现接口的类。 接下来,`aspectjrt-1.9.1.jar`是AspectJ运行时库,它是AspectJ编译器和...
- **动态代理**:Spring AOP主要使用JDK动态代理和CGLIB实现。如果目标类实现了接口,使用JDK动态代理;否则,使用CGLIB生成子类作为代理。 - **切点(Pointcut)**:定义连接点的筛选规则,通过表达式或注解来...
在Spring配置中,如果我们希望使用CGLIB代理,可以在`<aop:config>`或`<aop:aspectj-autoproxy>`元素下添加`<aop:proxy>`子元素,并设置`proxy-target-class="true"`。例如: ```xml <aop:config> <aop:aspect id=...
Spring支持两种AOP的实现方式:Spring AspectJ注解风格和Spring XML配置风格。使用AspectJ注解风格是最常见的,它允许开发者直接在方法上使用注解来定义切面。 Spring AOP中有五种不同类型的的通知(Advice): 1....
1. **代理**:Spring AOP支持两种类型的代理:JDK动态代理和CGLIB代理。JDK代理用于实现了接口的类,而CGLIB代理则用于没有接口或不希望使用接口的类。代理对象在调用目标方法时会执行切面逻辑。 2. **织入**:织入...
基于代理的AOP是Spring默认的方式,它主要通过JDK动态代理或者CGLIB字节码生成技术来创建代理对象,实现代理逻辑。这种方式简单易用,但仅限于处理接口。而AspectJ则是更为强大的AOP解决方案,它可以处理非接口类,...
Spring AOP,即面向切面编程,是Spring框架的核心组件之一,它允许程序员在不修改原有业务代码的情况下,对程序进行功能增强。...通过学习和实践,你可以更好地在Spring框架中利用AOP解决实际问题。
- **代理模式**:Spring AOP支持JDK动态代理和CGLIB代理,根据目标对象是否实现了接口来选择合适的代理方式。 - **自动代理生成**:Spring容器可以在启动时自动识别并生成代理对象,无需手动创建。 - **事务管理**...
### Java代理技术详解:JDK代理、CGLIB与AspectJ #### 一、代理模式简介 代理模式是一种常见的设计模式,在《Design Patterns in Java》一书中对其有明确的定义:“代理模式为一个对象提供一个代理,以控制对该...
在Spring中,AOP主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...
Spring提供了两种类型的代理:JDK动态代理和CGLIB代理。 8. **编织(Weaving)**:是指将通知插入到目标对象的过程。Spring AOP是在运行时完成的织入过程,即在程序运行时动态地添加通知。 #### 三、Spring AOP的...
- **代理(Proxy)**:Spring AOP 通过代理来实现对目标对象的增强,有 JDK 动态代理和 CGLIB 代理两种方式。 2. **AspectJ 1.6.12 Jar 包** `AspectJ` 是一个独立的 AOP 框架,提供了更强大的 AOP 支持,包括...
Spring AOP的实现基于动态代理,有两种代理方式:JDK动态代理和CGLIB动态代理。JDK动态代理主要针对实现了接口的类,而CGLIB代理则针对未实现接口的类,Spring会根据目标对象是否实现了接口来选择合适的代理方式。 ...
1. **基于代理的AOP**:Spring提供了两种代理方式,JDK动态代理和CGLIB代理。JDK动态代理主要针对接口实现,而CGLIB代理则针对类实现。 2. **基于注解的AOP**:Spring 2.5开始支持注解驱动的AOP,通过在方法上使用`...
在Spring AOP的源码中,`org.springframework.aop.framework.ProxyFactoryBean`是创建代理的主要类,它会根据目标类是否有接口选择JDK动态代理或CGLIB。`org.aopalliance.intercept.MethodInterceptor`接口定义了...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种强大的方式来实现横切关注点,如日志、事务管理、安全性等,从而解耦应用程序的核心业务逻辑。在Spring AOP中,关注点被模块化为独立的“切面”...
5. **代理(Proxy)**:Spring AOP 使用动态代理技术创建对象,有两种代理方式:JDK 动态代理和 CGLIB 代理。前者适用于接口实现类,后者适用于没有接口的类。 6. **注解使用**:如 @Aspect、@Before、@After、@...