Advice 通知类型

AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice,Spring按照通知Advice在目标方法的连接点位置,可以分为5种通知类型:

还有一种就是引介通知 org.springframework.aop.introductionInterceptor :

  • 引介(Introduction)是一种特殊的通知,它在不修改类的前提下,可以在运行期为类动态地添加一些属性(Field)或 方法,但由于spring只支持方法的连接点,不提供属性的连接点,即Spring AOP仅对方法层面进行增强,所以我们一般不考虑这种通知

Spring AOP 切面类型

  • Advisor(一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截,即对所有目标类的所有方法进行增强

    • 缺点:使用普通Advice作为切面,将会对目标类所有方法进行拦截(增强),不够灵活,比如只想对目标类中的一个或几个方法进行增强时,一般切面就无法实现效果了。
  • PointcutAdvisor(具有切点的切面,可以指定拦截目标类中哪些具体的方法

  • IntroductionAdvisor(引介切面,是针对引介通知而使用的切面,由于spring不支持引介通知,所以引介切面我们这里就不考虑了

                  一般切面

  • 以通知作为切面,对目标类的所有方法进行增强,所以无需定义切点

一般切面案例

package com.cd4356.spring_aop.beforeAdvice;public interface UserService {void save();void delete();void update();void find();
}
package com.cd4356.spring_aop.beforeAdvice;public class UserServiceImpl implements UserService {public void save() {System.out.println("保存用户. . .\n");}public void delete() {System.out.println("删除用户. . .\n");}public void update() {System.out.println("修改用户. . .\n");}public void find() {System.out.println("查询用户. . .\n");}
}

新建一个通知类来实现MethodBeforeAdvice接口,创建前置通知。在重写的before方法中编写前置通知内容

package com.cd4356.spring_aop.beforeAdvice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class BeforeAdvice implements MethodBeforeAdvice {public void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("前置通知. . .");}
}

通过Spring提供的org.springframework.aop.framework.ProxyFactoryBean类来产生代理对象。

ProxyFactoryBean的常用属性:

  • target(必须属性),要代理的目标对象

  • proxyInterfaces,目标对象实现的接口(JDK动态代理是基于接口的代理),多个接口可通过 <list> 标签配置

  • interceptorNames(必须属性),代表要织入目标对象Advice切面,多个切面可用<list> 标签配置

  • optimize,是否使用CGLib代理,为true时,表示强制CGLib代理;不写或为false时,使用JDK动态代理(默认使用JDK动态代理)

  • singleton,返回代理是否为单例,默认为单例

  • proxyTargetClass,是否对类代理而不是对接口代理,设置为 true时,使用CGLib代理,它的作用和optimize属性的作用是一样的

    • JDK动态代理,是一种面向接口的代理方式,是对目标类实现接口的代理
    • CGLib代理,是一种面向继承的代理方式,是对目标类的代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--配置目标类--><bean id="userService" class="com.cd4356.spring_aop.beforeAdvice.UserServiceImpl"/><!--配置通知类型,一般切面以通知作为切面--><bean id="beforeAdvice" class="com.cd4356.spring_aop.beforeAdvice.BeforeAdvice"/><!--spring aop产生代理对象--><bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!--目标对象--><property name="target" ref="userService"/><!--目标对象实现的接口,多个接口可用<list>配置--><property name="proxyInterfaces"><list><value>com.cd4356.spring_aop.beforeAdvice.UserService</value></list></property><!--拦截名称,即需要织入目标的Advice切面,多个切面可用<list>配置--><property name="interceptorNames"><list><value>beforeAdvice</value></list></property><!--默认使用JDK动态代理,配置optimize为true后会强制使用CGLib代理--><property name="optimize" value="false"/></bean>
</beans>

自动装配代理对象,通过代理对象来调用method。注意,注入的是代理对象,而不是目标对象

package com.cd4356.spring_aop.beforeAdvice;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;@RunWith(SpringJUnit4ClassRunner.class)
//加载spring配置文件
@ContextConfiguration("classpath:beforeAdvice.xml")
public class SpringDemo {//根据bean的id将代理对象注入@Resource(name = "userServiceProxy")private UserService userService; //这里是接口,不是实现类@Testpublic void demo(){//调用代理对象方法userService.save();userService.update();userService.delete();userService.find();}
}

前面,已经使用一般切面讲解了前置通知,还有后置,环绕等通知这里就不一一举例了,因为它们和前置通知的使用方式是差不多的,只要修改自定义通知类中实现的接口即可。

一般切面类型,是以Advice通知作为切面,并且一般切面(Advisor)会对目标类所有方法进行拦截增强。这个特点对于我们的开发而言就会显得不够灵活,因为在我们的实际开发中往往只会对目标类的部分方法进行拦截增强。所以这时候,我们就会用到另一种切面类型,带切入点的切面(PointcutAdvisor)

              PointcutAdvisor 切点切面

常用的 PointcutAdvisor 实现类

  • RegexpMethodPointcutAdvisor(构建正则表达式切点的切面

  • DefaultPointcutAdvisor(默认切点切面),通过任意 Pointcut 和 Advice 组合定义切面,是最常用的切面类型

构建正则表达式切点的切面案例

package com.cd4356.spring_aop.aroundAdvice;public class StudentService {public void save() {System.out.println("保存学生. . .");}public void delete() {System.out.println("删除学生. . .");}public void update() {System.out.println("修改学生. . .");}public void find() {System.out.println("查询学生. . .");}
}
package com.cd4356.spring_aop.aroundAdvice;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;public class AroundAdvice implements MethodInterceptor {public Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("\n环绕前增强. . .");//执行目标方法Object object = methodInvocation.proceed();System.out.println("环绕后增强. . .\n");return object;}
}

前面讲过,一般切面是使用通知作为切面,它会对目标类的所有方法进行增强。

而带有切入点的切面,它可以对目标类的一个或多个方法进行增强。在对这些方法进行增强时,就需要通过RegexMethodPointcutAdvisor正则表达式配置一个带有切入点的切面。

RegexMethodPointcutAdvisor的属性:

  • pattern 或 patterns(前者表示单个正则表达式,后置表示多个正则表达式,支持<list>配置),用来配置切面的切入点,两个属性至少配置其中一个

  • advice(必须属性),用来配置切面的通知

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--目标类--><bean id="studentService" class="com.cd4356.spring_aop.aroundAdvice.StudentService"/><!--配置通知--><bean id="aroundAdvice" class="com.cd4356.spring_aop.aroundAdvice.AroundAdvice"/><!--一般切面是使用通知作为切面,而要对目标类的某个方法进行增强时就需要配置一个带有切点的切面,一般切面已无法满足要求--><bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><!--pattern中配置正则表达式:.表示任意字符  *表示任意次数  .*表示所有方法--><!--<property name="pattern" value=".*"/>--><!--pattern中配置正则表达式:.表示任意字符  *表示任意次数  .*save表示所有save结尾方法--><!--<property name="pattern" value=".*save"/>--><!--pattern中配置正则表达式:.表示任意字符  *表示任意次数  多个正则表达式之间用‘,’隔开或用list标签配置--><property name="patterns"><list><!--所有以save结尾的方法--><value>.*save</value><!--所有含delete的方法--><value>.*delete.*</value></list></property><!--引用通知--><property name="advice" ref="aroundAdvice"/></bean><!--配置产生代理--><bean id="studentServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!--目标对象--><property name="target" ref="studentService"/><!--是否基于类来产生代理--><property name="proxyTargetClass" value="true"/><!--拦截名称,即需要织入目标的Advice切面,多个切面可用<list>标签配置--><property name="interceptorNames"><list><value>defaultPointcutAdvisor</value></list></property></bean></beans>
package com.cd4356.spring_aop.aroundAdvice;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;@RunWith(SpringJUnit4ClassRunner.class)
//加载spring配置文件
@ContextConfiguration("classpath:aroundAdvice.xml")
public class SpringDemo {/*注入目标对象,方法未被增强*///@Resource(name = "studentDao")/*注入代理对象,方法已被增强*/@Resource(name = "studentServiceProxy")private StudentService studentService;@Testpublic void Demo(){studentService.save();studentService.update();studentService.delete();studentService.find();}
}

默认切点切面案例

取,构建正则表达式切点的切面案例,其它内容不变,修改 xml 配置文件内容

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--目标类--><bean id="studentService" class="com.cd4356.spring_aop.aroundAdvice.StudentService"/><!--配置通知--><bean id="aroundAdvice" class="com.cd4356.spring_aop.aroundAdvice.AroundAdvice"/><!--配置切点--><bean id="pointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"><property name="mappedNames"><list><!--所有含save的方法--><value>*save*</value><!--以find开头的方法--><value>find*</value></list></property></bean><!--用pointcut切点 和 advice通知组合定义切面--><bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"><!--引用切点--><property name="pointcut" ref="pointcut"/><!--引用通知--><property name="advice" ref="aroundAdvice"/></bean><!--配置产生代理--><bean id="studentServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!--目标对象--><property name="target" ref="studentService"/><!--是否基于类来产生代理--><property name="proxyTargetClass" value="true"/><!--拦截名称,即需要织入目标的Advice切面--><property name="interceptorNames"><list><value>defaultAdvisor</value></list></property></bean>
</beans>

前面的案例中,都是通过ProxyFactoryBean织入切面进行代理。这种方式有个明显的缺点,就是每一个需要产生代理的对象,都需要配置一个ProxyFactoryBean。在实际开发中,可能会有非常多需要被增强的bean,每个都配置ProxyFactoryBean,开发维护量就变得非常庞大。


Spring传统AOP中,提供的自动创建代理方式,解决了每产生一个代理对象都需配置一个ProxyFactoryBean的缺点。

Spring AOP自动创建代理

  • BeanNameAutoProxyCreator,基于bean名称的自动代理

  • DefaultAdvisorAutoProxyCreator,基于切面信息的自动代理

基于bean名称的自动代理

新建两个目标类(一个实现了接口,一个不实现接口)和 两个通知类(一个前置通知,一个后置通知)来进行测试

package com.cd4356.spring_aop.beanNameAutoProxy;public interface UserService {void save();void delete();void update();void find();
}
package com.cd4356.spring_aop.beanNameAutoProxy;public class UserServiceImpl implements UserService {public void save() {System.out.println("保存用户");}public void delete() {System.out.println("删除用户");}public void update() {System.out.println("修改用户");}public void find() {System.out.println("查询用户");}
}
package com.cd4356.spring_aop.beanNameAutoProxy;public class TeacherService {public void save() {System.out.println("保存教师");}public void delete() {System.out.println("删除教师");}public void update() {System.out.println("修改教师");}public void find() {System.out.println("查询教师");}
}
package com.cd4356.spring_aop.beanNameAutoProxy;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class BeforeAdvice implements MethodBeforeAdvice {public void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("\n前置通知. . .");}
}
package com.cd4356.spring_aop.beanNameAutoProxy;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;public class AfterAdvice implements AfterReturningAdvice {public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {System.out.println("后置通知. . .");}
}

配置BeanNameAutoProxyCreator对象,它会根据注入的对象名称和切面,自动创建相应的代理对象。
BeanNameAutoProxyCreator的属性:

  • beanNames(必须属性),需要被代理的bean名称(多个bean之间用 号隔开 或 使用<list> 配置)
  • interceptorNames(必须属性),代表要织入目标对象Advice切面,多个切面可用<list> 标签配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--配置目标类--><bean id="userService" class="com.cd4356.spring_aop.beanNameAutoProxy.UserServiceImpl"/><bean id="teacherService" class="com.cd4356.spring_aop.beanNameAutoProxy.TeacherService"/><!--配置通知--><bean id="beforeAdvice" class="com.cd4356.spring_aop.beanNameAutoProxy.BeforeAdvice"/><bean id="afterAdvice" class="com.cd4356.spring_aop.beanNameAutoProxy.AfterAdvice"/><!--配置BeanNameAutoProxyCreator自动产生代理--><bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><!--bean名称,这里的*是通配符不是正则表达式,表示所有以Service结尾bean--><property name="beanNames"><list><value>*Service</value></list></property><!--拦截名称,即需要织入目标的Advice切面--><property name="interceptorNames"><list><value>beforeAdvice</value><value>afterAdvice</value></list></property></bean>
</beans>
package com.cd4356.spring_aop.beanNameAutoProxy;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beanNameAutoProxy.xml")
public class SpringDemo {@Resource(name = "userService")private UserService userService;@Resource(name = "teacherService")private TeacherService teacherService;@Testpublic void demo(){userService.save();userService.delete();userService.update();userService.find();teacherService.save();teacherService.delete();teacherService.update();teacherService.find();}
}

总结:

  • BeanNameAutoProxyCreator 是基于bean名称的自动代理,这种方式会对注入的所有目标对象的所有方法产生进行增强,不够灵活

基于切面信息的自动代理

取前面基于bean名称自动代理案例的代码,其它内容不变,修改 xml 配置文件内容。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--配置目标类--><bean id="userService" class="com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator.UserServiceImpl"/><bean id="teacherService" class="com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator.TeacherService"/><!--配置通知--><bean id="beforeAdvice" class="com.cd4356.spring_aop.beanNameAutoProxy.BeforeAdvice"/><bean id="afterAdvice" class="com.cd4356.spring_aop.beanNameAutoProxy.AfterAdvice"/><!--配置切面--><bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><!--正则表达式,配置要增强的方法--><property name="patterns"><list><!--com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator包下任意类的save方法--><value>com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator.*.save</value><!--com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator包下UserServiceImpl类的delete方法--><value>com.cd4356.spring_aop.DefaultAdvisorAutoProxyCreator.UserServiceImpl.delete</value></list></property><!--引用通知--><property name="advice" ref="afterAdvice"/></bean><!--配置DefaultAdvisorAutoProxyCreator自动产生代理--><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>

总结:

  • DefaultAdvisorAutoProxyCreator,基于切面信息的自动代理,该方式可以针对某些的某些方法进行增强。和前面讲过的带切入点的切面基本一样的,只不过该方式是通过DefaultAdvisorAutoProxyCreator自动产生代理,而不是通过ProxyFactoryBean来创建代理对象

其实,通过ProxyFactoryBean创建代理 或者是自动创建代理都不是当前最常用的开发方式,现在开发中最常用的是基于AspectJ的AOP开发,我会在下一个章节中进行讲解。

Spring AOP自动创建代理 和 ProxyFactoryBean创建代理相关推荐

  1. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  2. Spring AOP中的JDK代理和Cglib代理

    JDK动态代理是java JDK自身提供的基于接口的代理,代理类的生成速度快,而代理类的运行速度慢,适合于prototype类型 Cglib代理是基于之类继承的方式的代理,能代理非基于接口的类,适合于 ...

  3. Spring AOP实现原理详解之Cglib代理实现

    引入 我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理. 要了解动态代理是如何工作 ...

  4. Spring AOP理论 +代理模式详解

    目录 1.理解AOP 1.1.什么是AOP 1.2.AOP体系与概念 1.3.Spring AOP 通知的执行顺序 2.代理模式 2.1.静态代理 2.2.静态代理的缺点 3.动态代理 JDK 动态代 ...

  5. Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

    ① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口.JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类 ...

  6. Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践--AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...

  7. AOP的实现原理 —— 静态代理 和 动态代理( Spring AOP)

    文章目录 一.AOP是什么? 二.静态代理 -- AspectJ 2.1.举例说明 三. 动态代理 3.1.JDK 动态代理 3.1.1. 核心类: 3.1.2. 示例1--JDK动态代理 3.2.C ...

  8. Spring AOP知识详解

    本文来详细说下spring中的aop内容 文章目录 Spring AOP概述 Spring AOP一代 Pointcut Jointpoint Advice Advisor 织入 Spring AOP ...

  9. Spring AOP 学习总结

    一.AOP概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续 ...

最新文章

  1. weakself的一种写法
  2. html标签教案,第1章 HTML的基本标签-教案
  3. WPF Grid添加边框的两种方法
  4. 具有GlassFish和一致性的高性能JPA –第1部分
  5. 为Cubieboard打造完美Debian系统
  6. 运用大数据提高政协协商能力
  7. JavaScript 中 substr 和 substring的区别
  8. java测试脚本怎么写_Jmeter测试脚本编写(初学者熟悉篇)
  9. matlab 给图像双三次,图像灰度的双三次插值的MATLAB实现
  10. mkdir命令、chmod修改权限、利用scp 远程上传下载文件/文件夹
  11. java exif_Java读取图片EXIF信息的方法
  12. 基础算法(一)零基础学算法---总结大篇
  13. 大数据之路——数据挖掘
  14. PMP第13章知识点回顾,练习题
  15. 苹果电脑如何使用Siri语音助手!
  16. 4 第二式:左右开弓似射雕
  17. 计算机网络——网络层功能概述
  18. slot-scope写{}
  19. 如何在CSDN个人主页左侧栏添加微信二维码?
  20. 主流浏览器对HTML5的支持情况

热门文章

  1. ajax兼容低版本浏览器
  2. Android SDK安装与环境配置
  3. 代码示例_标准IO_fseek
  4. Spark 学习(六) Spark 的线程安全和序列化问题
  5. docker利用Dockerfile来制作镜像
  6. JS 浏览器扩展storage
  7. 用递归方法判断字符串是否是回文(Recursion Palindrome Python)
  8. iOS项目的完整重命名方法图文教程
  9. 2015 8月31号 本周计划
  10. 不同.net版本实现单点登录