Spring中AOP实现
1.什么是SpringAOP
什么是aop:Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的
统一维护的一种技术
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等
2.SpringAOP框架的用途
允许用户控制自己的方面,以完成OOP和AOP的互补使用
OOP:模拟真实的世界,一切皆是对象
3.AOP的实现方式
3.1预编译
-AspectJ 完整的面向切面编程解决方案--》spring不是完整的解决方案,不过spring提供比较好的实现方式,当然spring是同时也是支持这种方式的,这也是一种常用的方式
3.2运行期间动态代理(JDK动态代理,CGLib动态代理)
-SpringAop,JbossAop
Spring的AOP使用纯java实现,无需特殊的编译过程,不需要控制类的加载器层次,目前只支持方法的执行的连接点(通知Spring Bean某个方法执行)
不是为了提供完整AOP实现;而是侧重于一种AOP于IOC容器之间的整合,SpringAOP不会AspectJ(完整的AOP解决方案)竞争
Spring没有使用AspectJ的时候,也可以通过如下方式实现AOP
Spring AOP 默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者集合)都可以被代理
Spring AOP 中也可以使用CGLIB代理(如果一个业务对象没有实现一个接口)
有接口的:使用JDK的动态里
无接口的:使用CGLIB代理
4.SpringAOP中的一些概念
SpringAOP主要围绕以下概念展开:
连接点(Joinpoint)程序执行过程中某个特定的连接点
通知(Advice) 在切面的某个特的连接点上执行的动作
切入点(Pointcut)匹配连接的断言,在Aop中通知和一个切入点表达式关联
引入(Intruduction) 在不修改类代码的前提下,为类添加新的方法和属性
目标对象(Target Object) 被一个或者多个切面所通知的对象
Aop代理(AOP Proxy) AOP框架创建的对象,用来实现切面契约(aspect contract)(包括方法执行等)
织入(Weaving)把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,氛围:编译时织入,类加载时织入,执行时织入
通知类型Advice:
返回后通知(after returning advice)在某个连接点(jion point)正常执行完后执行通知
抛出异常通知(after throwing advice) 在方法异常退出时执行的通知
后通知(after(finally) advice)在方法抛出异常退出时候的执行通知(不管正常返回还是异常退出)
环绕通知(around advice) 包围一个连接点(jion point)的通知
切入点Pointcut:SpringAOP占时仅仅支持方法的连接点
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型,*号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
execution(public * *(..)) 切入点为执行所有的public方式时
execution(* set*(..)) 切入点执行所有的set开始的方法时
execution(* com.xyz.service.Account.*(..)) 切入点执行Account类的所有方法时
execution(* com.xyz.service.*.*(..))切入点执行com.xyz.service包下的所有方法时
execution(* com.xyz.service..*.*(..)) 切入点执行com.xyz.service包以及其子包的所有的方法时
上边这种方式aspectj和springaop通用的,其他方式可以自己查找资料
推荐:
http://blog.csdn.net/abcd898989/article/details/50809321
http://blog.csdn.net/peng658890/article/details/7223046
5.SpringAOP实现
我们在Java项目开发中,AOP常见的配置有如下3种:
a.基于SpringAOP容器的实现,使用AspectJ(Spring封装了AspectJ的使用),这是一种比较常用的方式,可以很容易看清代码结构
b.运行期间的动态代理实现,这是一种比较老的实现方式,比较繁琐
c.注解实现,这种方式开发起来简单,但是不利于后期维护,比如说很难找出你所有使用了SpringAOP注解的地方
这里我主要介绍第一种,和第二种的使用
5.1SpringAOP中的容器实现
5.1.1 前置通知 before
和aspect元素(注意这些元素的出现是由顺序的)
- <?xml version="1.0" encoding="UTF-8"?>
- <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-3.2.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
- <!-- 切面类 -->
- <bean id="myAspect" class="com.xxx.spring.aop.bean.annotation.aop.MyAspect"></bean>
- <!-- 业务类 -->
- <bean id="aspectBiz" class="com.xxx.spring.aop.bean.annotation.aop.AspectBiz"></bean>
- <!-- aop配置 可以配置多个-->
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名包含Biz的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法-->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
- </beans>
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- }
业务类:
- //业务类
- public class AspectBiz {
- public void biz(){
- System.out.println("Aspect biz");
- }
- }
测试:
- @Test
- public void aspectBefore(){
- ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/spring-aop-schema-advice.xml");
- AspectBiz aspectBiz = (AspectBiz) ac.getBean("aspectBiz");
- aspectBiz.biz();
- }
结果:
- aspect before
- Aspect biz
5.1.2后置通知after-returning
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名以Biz结尾的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法 在执行切入点指定的方法之前执行 -->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- <!-- 返回之后的通知 -->
- <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
切面类:
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- public void afterReturning(){
- System.out.println("aspect afterReturning");
- }
- }
结果:
- aspect before
- Aspect biz
- aspect afterReturning
结果可以看出,后置通知会在业务方法的执行之后
5.1.3异常通知after-throwing
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名以Biz结尾的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法 在执行切入点指定的方法之前执行 -->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- <!-- 返回之后的通知 -->
- <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
- <!-- -->
- <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
切面类:
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- public void afterReturning(){
- System.out.println("aspect afterReturning");
- }
- public void afteThrowing(){
- System.out.println("aspect afteThrowing");
- }
- }
业务类:
- public void biz(){
- System.out.println("Aspect biz");
- throw new RuntimeException(); //出现异常的时候afteThrowing才会执行
- }
测试和结果:
- aspect before
- Aspect biz
- aspect afteThrowing
- 上边的结果由于抛出异常后,不会正常的返回所有没有aspect afterReturning输出
5.1.4最终通知after(finally) advice
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名以Biz结尾的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法 在执行切入点指定的方法之前执行 -->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- <!-- 返回之后的通知 -->
- <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
- <!-- -->
- <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
- <!-- after(finally) advice 不管是否抛出异常,最后都会执行的方法 -->
- <aop:after method="after" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
切面类:
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- public void afterReturning(){
- System.out.println("aspect afterReturning");
- }
- public void afteThrowing(){
- System.out.println("aspect afteThrowing");
- }
- public void after(){
- System.out.println("aspect after(finally)");
- }
- }
业务类:
- //业务类
- public class AspectBiz {
- public void biz(){
- System.out.println("Aspect biz");
- throw new RuntimeException();
- }
- }
测试结果:
- aspect before
- Aspect biz
- aspect afteThrowing
- aspect after(finally)
从结果中可以看出,抛出异常后,切面类中的after方法还是可以执行
- 如果业务类如下:没有异常,
- public class AspectBiz {
- public void biz(){
- System.out.println("Aspect biz");
- }
- }
结果:
- aspect before
- Aspect biz
- aspect after(finally)
由以上结果可以看到,不管怎么样after方法还是会执行,很像try...catch..finally
5.1.5环绕通知around
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名以Biz结尾的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法 在执行切入点指定的方法之前执行 -->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- <!-- 返回之后的通知 -->
- <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
- <!-- -->
- <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
- <!-- after(finally) advice 不管是否抛出异常,最后都会执行的方法 -->
- <aop:after method="after" pointcut-ref="myPointcut"/>
- <!-- adroun advice 环绕通知,方法的第一参数必须是ProceedingJoinPoint类型 -->
- <aop:around method="around" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
切面类:
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- public void afterReturning(){
- System.out.println("aspect afterReturning");
- }
- public void afteThrowing(){
- System.out.println("aspect afteThrowing");
- }
- public void after(){
- System.out.println("aspect after(finally)");
- }
- public void around(ProceedingJoinPoint joinPoint){
- Object object = null;
- try{
- System.out.println("aspect around 1"); //方法执行前
- object = joinPoint.proceed(); //代表业务方法的执行
- System.out.println("aspect around 1"); //方法执行后
- }catch(Throwable e){
- e.printStackTrace();
- }
- }
- }
测试结果:
- aspect around 1
- Aspect biz
- aspect around 1
从结果可以看出,joinPoint.proceed其实就是执行业务方法,我们可以在其之前做和之后做一些操作
- <?xml version="1.0" encoding="UTF-8"?>
- <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-3.2.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
- <!-- 切面类 -->
- <bean id="myAspect" class="com.xxx.spring.aop.bean.annotation.aop.MyAspect"></bean>
- <!-- 业务类 -->
- <bean id="aspectBiz" class="com.xxx.spring.aop.bean.annotation.aop.AspectBiz"></bean>
- <!-- aop配置 可以配置多个-->
- <aop:config>
- <!-- 切面类 -->
- <aop:aspect id="aspectTest" ref="myAspect">
- <!-- 切入点 标识切入点 aop包下类名以Biz结尾的类的所有方法-->
- <aop:pointcut expression="execution(* com.xxx.spring.aop.bean.annotation.aop.*Biz.*(..))" id="myPointcut"/>
- <!-- 通知 ,通过切入点切入切入切面类中的before方法 在执行切入点指定的方法之前执行 -->
- <aop:before method="before" pointcut-ref="myPointcut"/>
- <!-- 返回之后的通知 -->
- <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
- <!-- -->
- <aop:after-throwing method="afteThrowing" pointcut-ref="myPointcut"/>
- <!-- after(finally) advice 不管是否抛出异常,最后都会执行的方法 -->
- <aop:after method="after" pointcut-ref="myPointcut"/>
- <!-- adroun advice 环绕通知,方法的第一参数必须是ProceedingJjoinPoint类型 -->
- <!-- <aop:around method="around" pointcut-ref="myPointcut"/> -->
- <!-- 参数传递 -->
- <aop:around method="aroundInit" pointcut="execution(* com.xxx.spring.aop.bean.annotation.aop.AspectBiz.init(String,int))
- and args(bizName,times)"/>
- </aop:aspect>
- </aop:config>
- </beans>
其中,通知中也可以单独指定的pointcut,切入点中,我们单独指定了init方法,且指定了方法的参数类型,和方法参数的名字,其中方法参数名字,可以更实际的方法参数名字不同,比如说init(String
biz,int t) 都是可以匹配到的,不过为了维护方便,最好还是都一样。
- /*
- * 声明一个切面类
- * */
- public class MyAspect {
- public void before(){
- System.out.println("aspect before");
- }
- public void afterReturning(){
- System.out.println("aspect afterReturning");
- }
- public void afteThrowing(){
- System.out.println("aspect afteThrowing");
- }
- public void after(){
- System.out.println("aspect after(finally)");
- }
- public void around(ProceedingJoinPoint joinPoint){
- Object object = null;
- try{
- System.out.println("aspect around 1"); //方法执行前
- object = joinPoint.proceed(); //代表业务方法的执行
- System.out.println("aspect around 2"); //方法执行后
- }catch(Throwable e){
- e.printStackTrace();
- }
- }
- //AOP中参数的传递
- public void aroundInit(ProceedingJoinPoint joinPoint,String bizName,int times){
- System.out.println(bizName+"--"+times);
- Object object = null;
- try{
- System.out.println("aspect around 1"); //方法执行前
- object = joinPoint.proceed(); //代表业务方法的执行
- System.out.println("aspect around 1"); //方法执行后
- }catch(Throwable e){
- e.printStackTrace();
- }
- }
- }
业务类:
- public class AspectBiz {
- public void biz(){
- System.out.println("Aspect biz");
- }
- public void init(String bizName,int times){
- System.out.println("aspectBiz init:"+bizName+" "+times);
- }
- }
测试:
- @Test
- public void aspectAround(){
- ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/spring-aop-schema-advice.xml");
- AspectBiz aspectBiz = (AspectBiz) ac.getBean("aspectBiz");
- aspectBiz.init("init", 3);
- }
测试结果:
- aspect before
- init--3
- aspect around 1
- aspectBiz init:init 3
- aspect around 1
- aspect after(finally)
- aspect afterReturning
- AfterClass 标注的方法 会最后执行
可以看到,参数比顺利的传送过去
6.Advisor
Advisor就像一个小的自包含,只有一个advice切面通过一个bean标识,并且必须实现一个advice接口,同时advisor也可以很好的利用aspectJ的切入点表达式Spring通过配置文件中
<aop:advisor>元素支持advisor实际使用中,大多数情况下它会和transactional
advice配合使用,用于事务控制
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
- <bean name="sessionFactory"
- class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
- <property name="configLocation">
- <!-- 路径位于src下 -->
- <value>classpath:hibernate.cfg.xml</value>
- </property>
- </bean>
- <!-- 事物配置 -->
- <bean id="txManager"
- class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <property name="sessionFactory">
- <ref local="sessionFactory" />
- </property>
- </bean>
- <!-- advisor配置 配置事务处理的Bean,定义切面(advice)
- 由于getxxx,queryxxx,findxxx不涉及事务,可以设置read-only为true
- -->
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="get*" read-only="true" />
- <tx:method name="query*" read-only="true" />
- <tx:method name="find*" read-only="true" />
- <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
- <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
- <tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
- <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
- <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
- </tx:attributes>
- </tx:advice>
- <!-- aop配置 -->
- <aop:config>
- <!-- 给impl包下的所有的方法配置adivce -->
- <aop:pointcut expression="excution(* com.xxx.spring.aop.bean.annotation.service.impl..(..))" id="serviceMethod" />
- <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
- </aop:config>
- </beans>
关于spring的事务控制将会在另外一篇文章中介绍。
7.SpringAOP代理实现
7.1.1ProxyFactoryBean
7.1.2使用代理实现环绕通知around
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean><!-- advice在方法执行前后添加我们的操作 -->
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!--proxyInterfaces 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现类一个或多个接口,
- 那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理 -->
- <property name="proxyInterfaces" value="com.xxx.spring.aop.bean.UserDao"></property>
- <!-- 注入目标对象(注入被代理的对象) -->
- <property name="target" ref="target"></property>
- <!-- 注入代理对象所要执行的处理程序,通知配置 -->
- <property name="interceptorNames">
- <list>
- <value>advice</value>
- </list>
- </property>
- </bean>
- </beans>
注意:
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
value中可以写多个接口
如果有多个通知可以写成:
- <property name="interceptorNames">
- <list>
- <value>advice</value>
- </list>
- </property>
但是 通知是不可以省略的,同时推荐结合的写法
- import java.util.Date;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class AdviceTest implements MethodInterceptor{
- @Override
- public Object invoke(MethodInvocation method) throws Throwable {
- System.out.println("方法开始执行"+new Date());
- method.proceed();
- System.out.println("方法执行完毕"+new Date());
- return null;
- }
- }
- //委托类
- public class UserDaoImpl implements UserDao {
- @Override
- public void saveUser() {
- System.out.println("保存用户");
- }
- @Override
- public void deleteUser() {
- System.out.println("删除用户");
- }
- @Override
- public void updateUser() {
- System.out.println("更新用户");
- }
- }
接口:
- public interface UserDao {
- public abstract void saveUser();
- public abstract void deleteUser();
- public abstract void updateUser();
- }
- @Test
- public void advice(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/advice.xml");
- UserDao userDao = factory.getBean("proxy", UserDao.class);
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
测试结果:
- 方法开始执行Sun Sep 11 21:02:12 CST 2016
- 保存用户
- 方法执行完毕Sun Sep 11 21:02:12 CST 2016
- 方法开始执行Sun Sep 11 21:02:12 CST 2016
- 删除用户
- 方法执行完毕Sun Sep 11 21:02:12 CST 2016
- 方法开始执行Sun Sep 11 21:02:12 CST 2016
- 更新用户
- 方法执行完毕Sun Sep 11 21:02:12 CST 2016
7.1.3使用动态代理实现前置通知before
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 注入目标对象(注入被代理的对象) -->
- <property name="target" ref="target"></property>
- <!-- 注入代理对象需要实现的所有接口 -->
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
- <!-- 注入代理对象所要执行的处理程序 -->
- <property name="interceptorNames">
- <array>
- <value>before</value>
- </array>
- </property>
- </bean>
- </beans>
切面前置通知实现类:
- import java.lang.reflect.Method;
- import org.springframework.aop.MethodBeforeAdvice;
- public class BeforeTest implements MethodBeforeAdvice{
- @Override
- public void before(Method method, Object[] obj, Object object)
- throws Throwable {
- System.out.println("version 1.0 author tom "+method.getName()+" is execute");
- }
- }
测试:
- @Test
- public void before(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/before.xml");
- UserDao userDao = factory.getBean("proxy", UserDao.class);
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
- version 1.0 author tom saveUser is execute
- 保存用户
- version 1.0 author tom deleteUser is execute
- 删除用户
- version 1.0 author tom updateUser is execute
- 更新用户
7.1.4后置通知afterReturning
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="after" class="com.xxx.spring.aop.bean.AfterTest"></bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 注入目标对象(注入被代理的对象) -->
- <property name="target" ref="target"></property>
- <!-- 注入代理对象需要实现的所有接口 -->
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
- <!-- 注入代理对象所要执行的处理程序 -->
- <property name="interceptorNames">
- <array>
- <value>after</value>
- </array>
- </property>
- </bean>
- </beans>
切面后置通知:
- import java.lang.reflect.Method;
- import org.springframework.aop.AfterReturningAdvice;
- public class AfterTest implements AfterReturningAdvice {
- @Override
- public void afterReturning(Object arg0, Method arg1, Object[] arg2,
- Object arg3) throws Throwable {
- System.out.println(arg1.getName()+" is over!");
- }
- }
测试:
- @Test
- public void after(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/after.xml");
- UserDao userDao = factory.getBean("proxy", UserDao.class);
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
测试结果:
- 保存用户
- saveUser is over!
- 删除用户
- deleteUser is over!
- 更新用户
- updateUser is over!
7.1.5异常通知throw
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 注入目标对象(注入被代理的对象) -->
- <property name="target" ref="target"></property>
- <!-- 注入代理对象需要实现的所有接口 -->
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
- <!-- 注入代理对象所要执行的处理程序 -->
- <property name="interceptorNames">
- <list>
- <value>throws</value>
- </list>
- </property>
- </bean>
- </beans>
异常通知切面:
- package com.briup.spring.aop.bean;
- import java.lang.reflect.Method;
- import org.springframework.aop.ThrowsAdvice;
- public class ThrowsAdiviceTest implements ThrowsAdvice{
- public void afterThrowing(Method method,Object[] args,Object target,Exception ex)throws Throwable{//Throwable subclass
- System.out.println("afterThrowing 2 ...."+method.getName()+" "+ target.getClass().getName());
- }
- }
- @Test
- public void throwTest(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/throw.xml");
- UserDao userDao = factory.getBean("proxy", UserDao.class);
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
结果:
- 保存用户
- afterThrowing 2 ....saveUser com.xxx.spring.aop.bean.UserDaoImpl
7.1.6切入点配置pointcut
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.briup.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean>
- <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
- <!-- 切入点 -->
- <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
- <property name="mappedNames">
- <list>
- <value>sa*</value>
- </list>
- </property>
- </bean>
- <!-- advisor -->
- <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
- <property name="advice" ref="throws"></property>
- <property name="pointcut" ref="pointcutBean"></property>
- </bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 注入目标对象(注入被代理的对象) -->
- <property name="target" ref="target"></property>
- <!-- 注入代理对象需要实现的所有接口 -->
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
- <!-- 注入代理对象所要执行的处理程序 -->
- <property name="interceptorNames">
- <list>
- <value>defaultAdvisor</value>
- <value>throws</value>
- </list>
- </property>
- </bean>
- </beans>
如上边切入点配置:
- <!-- 切入点 -->
- <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
- <property name="mappedNames">
- <list>
- <value>sa*</value>
- </list>
- </property>
- </bean>
切面:
- <!-- advisor -->
- <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
- <property name="advice" ref="advice"></property>
- <property name="pointcut" ref="pointcutBean"></property>
- </bean>
7.1.7使用匿名的代理对象
- <?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-3.2.xsd">
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="advice" class="com.xxx.spring.aop.bean.AdviceTest"></bean>
- <bean name="throws" class="com.xxx.spring.aop.bean.ThrowsAdiviceTest"></bean>
- <!-- 切入点 -->
- <bean name="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
- <property name="mappedNames">
- <list>
- <value>sa*</value>
- </list>
- </property>
- </bean>
- <!-- advisor -->
- <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
- <property name="advice" ref="advice"></property>
- <property name="pointcut" ref="pointcutBean"></property>
- </bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <!-- 注入目标对象(注入被代理的对象),使用匿名的方式 -->
- <property name="target">
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- </property>
- <!-- 注入代理对象需要实现的所有接口 -->
- <property name="interfaces">
- <array>
- <value>com.xxx.spring.aop.bean.UserDao</value>
- </array>
- </property>
- <!-- 注入代理对象所要执行的处理程序 -->
- <property name="interceptorNames">
- <list>
- <value>defaultAdvisor</value>
- <value>throws</value>
- </list>
- </property>
- </bean>
- </beans>
7.1.8IntroductionInterceptor
8.自动代理实现
8.1.BeanNameAutoProxyCreator
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>
- <!-- 配置代理对象 -->
- <bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
- <!-- 注入需要被代理的对象名字,会代理所有以targ开始的bean -->
- <property name="beanNames">
- <list>
- <value>targ*</value>
- </list>
- </property>
- <!-- 注入advice或者advisor -->
- <property name="interceptorNames">
- <list>
- <value>before</value>
- </list>
- </property>
- </bean>
- </beans>
通过,自动代理,proxy会自动帮我们代理所有以targ开头的目标委托类
- //委托类
- public class UserDaoImpl implements UserDao {
- @Override
- public void saveUser(){
- System.out.println("保存用户");
- }
- @Override
- public void deleteUser() {
- System.out.println("删除用户");
- }
- @Override
- public void updateUser() {
- System.out.println("更新用户");
- }
- }
- @Test
- public void autoAdvisor(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/autoAdvisor.xml");
- UserDao userDao = factory.getBean("target", UserDao.class);//autoAdvisor只能通过委托类的名字来拿
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
结果:
- version 1.0 author tom saveUser is execute
- 保存用户
- version 1.0 author tom deleteUser is execute
- 删除用户
- version 1.0 author tom updateUser is execute
- 更新用户
8.2DefaultAdvisorAutoProxyCreator
- <?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-3.2.xsd">
- <!-- 配置委托类 -->
- <bean name="target" class="com.xxx.spring.aop.bean.UserDaoImpl"></bean>
- <!-- 配置代理对象将来执行的时候,所要执行的处理程序 -->
- <bean name="before" class="com.xxx.spring.aop.bean.BeforeTest"></bean>
- <!-- 配置advisor -->
- <!-- 作用:筛选要拦截的方法 -->
- <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <!-- 注入advice -->
- <property name="advice" ref="before"></property>
- <!-- 注入需要被拦截的目标对象中的方法 -->
- <property name="patterns">
- <list>
- <value>.*deleteUser</value>
- </list>
- </property>
- </bean>
- <!-- 配置代理对象,当前IoC容器中自动应用,不用显示应用advisor的bean定义 -->
- <bean name="proxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
- </beans>
- @Test
- public void autoProxyByName(){
- BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/autoProxyByName.xml");
- UserDao userDao = factory.getBean("target", UserDao.class);
- userDao.saveUser();
- userDao.deleteUser();
- userDao.updateUser();
- }
结果:
- 保存用户
- version 1.0 author tom deleteUser is execute
- 删除用户
- 更新用户
http://blog.csdn.net/abcd898989/article/details/50809321
http://blog.csdn.net/peng658890/article/details/7223046
Spring中AOP实现的更多相关文章
- Spring中AOP原理,源码学习笔记
一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...
- Spring中AOP简介与切面编程的使用
Spring中AOP简介与使用 什么是AOP? Aspect Oriented Programming(AOP),多译作 "面向切面编程",也就是说,对一段程序,从侧面插入,进行操 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- Spring 中aop切面注解实现
spring中aop的注解实现方式简单实例 上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们 ...
- Spring中AOP相关源码解析
前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...
- AOP 与 Spring中AOP使用(上)
AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续 ...
- JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解
在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...
- 浅析Spring中AOP的实现原理——动态代理
一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...
- Spring中AOP相关的API及源码解析
Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...
- Spring中AOP的模拟实现
什么是AOP? 面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 1.面向切面编程提供声明式事务管理 2.spring支持用户自定义的切面 面向 ...
随机推荐
- 【题解】 bzoj1923: [Sdoi2010]外星千足虫 (线性基/高斯消元)
bzoj1923,戳我戳我 Solution: 这个高斯消元/线性基很好看出来,主要是判断在第K 次统计结束后就可以确定唯一解的地方和\(bitset\)的骚操作 (我用的线性基)判断位置,我们可以每 ...
- 【题解】 [HNOI2015]落忆枫音 (拓扑排序+dp+容斥原理)
原题戳我 Solution: (部分复制Navi_Aswon博客) 解释博客中的两个小地方: \[\sum_{\left(S是G中y→x的一条路径的点集\right))}\prod_{2≤j≤n,(j ...
- 【CF949D】Curfew(贪心)
[CF949D]Curfew(贪心) 题面 CF 洛谷 破池姐姐翻译好强啊 题解 今天菊开讲这题,我大力猜想一波说肯定从中间有个分界线,他还说可能是假的 大力贪心就好了,从两边往中间考虑,只要这个房间 ...
- 【ZJOI2015】诸神眷顾的幻想乡 解题报告
[ZJOI2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热 ...
- SQL Server 排名函数( ROW_NUMBER、RANK、DENSE_RANK、NTILE )
排名函数是Sql Server2005新增的功能,下面简单介绍一下他们各自的用法和区别.我们新建一张Order表并添加一些初始数据方便我们查看效果. CREATE TABLE [dbo].[Order ...
- vue npm start 自动打开网页
打开config/index.js,设置: autoOpenBrowser: true,(默认是false,把false改为true即可)
- Hadoop源码阅读-HDFS-day1
HDFS声明及构造函数 @InterfaceAudience.Private @InterfaceStability.Evolving public class Hdfs extends Abstra ...
- Kubernetes之解决从k8s.gcr.io拉取镜像失败问题
前言 因谷歌网络限制问题,国内的K8ser大多数在学习Kubernetes过程中因为镜像下载失败问题间接地产生些许失落感,笔者也因此脑壳疼,故翻阅资料得到以下解决方式: 在应用yaml文件创建资源时, ...
- BAT及各大互联网公司2014前端笔试面试题--Html,Css篇(昨天有个群友表示写的简单了点,然后我无情的把他的抄了一遍)
某个群友 http://www.cnblogs.com/coco1s/ 很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习前端基础 ...
- 五行代码终极完美解决从IE6到Chrome所有浏览器的position:fixed;以及闪动问题
这个方法其实已经使用很久了,之前主要在嵌入式WebQQ等产品中用过,现在拿出来分享一下吧,是目前最简洁的方式来实现ie6的position:fixed; 失效bug,以及的其他方法的闪动问题,CSS代 ...