zoukankan      html  css  js  c++  java
  • 代理模式及Spring AOP (二)

    一.Spring AOP

        1.1 Spring AOP

            底层还是用的动态代理。如果目标对象所对应的类有接口,spring就用jdk生成代理对象;

                                                   如果目标对象所对应的类没有接口,spring就用CGLIB生成代理对象。

            优点:动态代理,如果一个类被它代理,则接口或者类的所有方法都被强迫执行。而spring AOP可以按业务划分,有些方法需要事务则扩展功能,有些方法不需要则不进行扩展。

        1.2 相关概念名词

            ① 切面aspect:切面是一个类(功能类),类里有若干方法,每个方法代表一个功能。把这些功能方法切到指定的位置,切到到业务方法的前后等

                                      对应:spring中<aop:aspect id="myAspect" ref="tm"></aop:aspect>

            ② 连接点joinpoint:用代理对象调用方法,这句话的位置就是连接点,用连接点启动横切。

            ③ 通知advice:切面类中的那些方法就是通知。

           前置通知<aop:before                在执行目标方法前面

                后置通知<aop:after-returning    在执行完目标方法后执行

                异常通知<aop:after-throwing    在目标方法抛异常执行,不会执行后置通知

                最终通知<aop:after                   前置,后置或异常通知,都执行完毕后会执行最终通知

                环绕通知<aop:around               特点:可以控制目标方法的执行

            ④ 切入点pointcut:把切面中的通知,切到哪个类中的哪些方法上,切入点是指切到哪些方法上,spring给提供一些规则表达式,用于指定切在哪些方法上。

                                 对应:spring中<aop:pointcut id="myPointCut" expression="execution(* com.hdu.service..*.*(..))"/>

            ⑤ 目标对象targetObject:实际的业务类的对象

            ⑥ aop代理aopproxy:由spring框架帮程序员用jdk或cglib生成代理对象,这个生成的过程被spring封装了。spring的独有配置,用于指定生成代理对象。

                                      对应: spring中<aop:config></aop:config>

            ⑦ 织入weaving:也叫横切,把额外的功能用配置文件和注解的方式织入到业务中;取消配置文件和注解,就取消了织入,不影响原有业务流程

                             在spring中加入<aop:config节点,<aop:pointcut expression="",从spring容器取出expression属性指定的对象都是代理对象。

                             在spring中没有加入<aop:config节点,从spring容器中取出的所有对象是真实对象。

        1.3 参考手册

            1.3.1 声明一个切面

        

            1.3.2 声明一个切点

        其中<aop:pointcut/>标签写在<aop:aspect></aop:aspect>标签内,为局部切点,只起作用在对应切面。

          

        其中<aop:pointcut/>标签写在<aop:aspect></aop:aspect>标签外,为全局切点,所有切面对其有效。

          

      关于切点的expression 表达式:

      

        参考手册中的示例:

          ① the execution of any public method:所有的public方法
            execution(public * *(..))
          ② the execution of any method with a name beginning with "set": 所有以set开头的方法
            execution(* set*(..))
          ③ the execution of any method defined by the AccountService interface: com.xyz.service.AccountService类或接口中任意方法,任意参数,任意返回值
            execution(* com.xyz.service.AccountService.*(..))
          ④ the execution of any method defined in the service package: com.xyz.service包下任意类,任意方法,任意参数,任意返回值
            execution(* com.xyz.service.*.*(..))
          ⑤ the execution of any method defined in the service package or a sub-package: com.xyz.service包下以及子包下任意类.任意的方法,任意参数,任意返回值
            execution(* com.xyz.service..*.*(..)) 

      关于切点的within表达式:

         ① any join point (method execution only in Spring AOP) within the service package:  com.xyz.service的任意类

          within(com.xyz.service.*)

         ② any join point (method execution only in Spring AOP) within the service package or a sub-package: com.xyz.service及其子包下的任意类

          within(com.xyz.service..*)

         ③ any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface: com.xyz.service的AccountService类

          this(com.xyz.service.AccountService)

            1.3.3 声明通知

        通常 前置通知、后置通知、异常通知、最终通知 与 环绕通知分开使用,否则执行顺序不符合预期逻辑。

        若环绕通知叠加使用,按配置顺序先A后B,则执行时,先执行A在目标对象调用方法之前的内容,再执行B在目标对象调用方法之前的内容,调用目标对象的原有方法,执行A在目标对象调用方法之后的内容,执行B在目标对象调用方法之后的内容。

        1.3.3.1 前置通知

          

        1.3.3.2 后置通知

             

        1.3.3.3 异常通知

             

        1.3.3.4最终通知

          

        1.3.3.5环绕通知

           

    二.Spring AOP使用示例:使用xml配置文件的方式

        2.1 新增jar包,支持对AOP的处理

            

        2.2 接口及业务实现类

    1 package com.hdu.dao;
    2  
    3 public interface IUserDao {
    4     public int addrUser();
    5     public int updateUser();
    6 }
    Interface IUserDao
     1 package com.hdu.dao.impl;
     2 
     3 import com.hdu.dao.IUserDao;
     4 
     5 public class UserDaoImpl implements IUserDao {
     6 
     7     @Override
     8     public int addrUser() {
     9         System.out.println("UserDaoImpl.addUser()");
    10         return 1;
    11     }
    12 
    13     @Override
    14     public int updateUser() {
    15         System.out.println("UserDaoImpl.updateUser()");
    16         return 1;
    17     }
    18 }
    19 
    20 Class UserDaoImpl
    Class UserDaoImpl
    1 package com.hdu.service;
    2 
    3 public interface IUserService {
    4     public boolean addUser();
    5     public boolean updateUser();
    6     
    7 }
    8 
    9 Interface IUserService
    Interface IUserService
     1 package com.hdu.service.impl;
     2 
     3 import com.hdu.dao.IUserDao;
     4 import com.hdu.dao.impl.UserDaoImpl;
     5 import com.hdu.service.IUserService;
     6 
     7 public class UserServiceImpl implements IUserService {
     8     //应该用spring进行解耦,本次重点在于代理,故简化操作
     9     private IUserDao userDao = new UserDaoImpl();
    10     
    11     @Override
    12     public boolean addUser() {
    13         System.out.println("UserServiceImpl.addUser()");
    14         boolean flag = false;
    15         int rowAffect = userDao.addrUser();
    16         if(rowAffect==1) {
    17             flag = true;
    18         }
    19         return flag;
    20     }
    21 
    22     @Override
    23     public boolean updateUser() {
    24         System.out.println("UserServiceImpl.updateUser()");
    25         boolean flag = false;
    26         int rowAffect = userDao.updateUser();
    27         if(rowAffect==1) {
    28             flag = true;
    29         }
    30         return flag;
    31     }
    32 
    33 }
    34 
    35 Class UserServiceImpl
    Class UserServiceImpl

         2.3 切面类

            其中JoinPoint 可以获取目标对象,目标方法及目标方法的参数,环绕通知则使用ProceedingJoinPoint 获取。

     1 package com.hdu.aspect;
     2 
     3 import org.aspectj.lang.JoinPoint;
     4 import org.aspectj.lang.ProceedingJoinPoint;
     5 
     6 /**
     7  * 切面
     8  * @author Administrator
     9  *
    10  */
    11 public class TransactionManager {
    12     //前置通知方法
    13     public void begin(JoinPoint jp) {
    14         System.out.println("begin transaction");
    15         System.out.println("目标对象-->"+jp.getTarget());
    16         System.out.println("目标方法-->"+jp.getSignature().getName());
    17         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    18     }
    19     
    20     //后置通知方法
    21     public void commit(JoinPoint jp,Object flag) {
    22         System.out.println("commit transaction");
    23         System.out.println("目标对象-->"+jp.getTarget());
    24         System.out.println("目标方法-->"+jp.getSignature().getName());
    25         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    26         System.out.println("目标方法的返回值-->"+flag);
    27     }
    28     //异常通知方法
    29     public void rollback(JoinPoint jp,Exception ex) {
    30         System.out.println("rollback transaction");
    31         System.out.println("目标对象-->"+jp.getTarget());
    32         System.out.println("目标方法-->"+jp.getSignature().getName());
    33         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    34         System.out.println("目标方法抛异常-->"+ex.getMessage());
    35     }
    36     //最终通知
    37     public void finallyClose(JoinPoint jp) {
    38         System.out.println("finallyClose");
    39         System.out.println("目标对象-->"+jp.getTarget());
    40         System.out.println("目标方法-->"+jp.getSignature().getName());
    41         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    42     }
    43     //环绕通知  能控制目标方法的执行
    44     public Object around(ProceedingJoinPoint pjp)throws Throwable {
    45         Object retVal=null;
    46         try {
    47             System.out.println("around  begin transaction");
    48             System.out.println("目标对象-->"+pjp.getTarget());
    49             System.out.println("目标方法-->"+pjp.getSignature().getName());
    50             System.out.println("目标方法参数-->"+pjp.getArgs()[0]);
    51             //if() {
    52                 //就是直接指定目标方法
    53                 retVal = pjp.proceed();
    54             //}
    55             System.out.println("around  commit transaction");
    56         }catch(Exception e) {
    57             e.printStackTrace();
    58             System.out.println("around  rollback transaction");
    59         }finally {
    60             System.out.println("around  finallyClose");
    61         }
    62         
    63         return retVal;
    64     }
    65 }

        2.4 配置文件

    <?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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:util="http://www.springframework.org/schema/util" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        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
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/util 
            http://www.springframework.org/schema/util/spring-util.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
        <!-- 实例化业务模型对象 -->
        <bean id="userDao" class="com.hdu.dao.impl.UserDaoImpl"></bean>
        
        <bean id="userService" class="com.hdu.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"></property>
        </bean>
        
        <!-- 实例化切面的对象 -->
        <bean id="tm" class="com.hdu.aspect.TransactionManager"></bean>
        
        <!-- spring 独有的配置,创建代理对象和确定如何横切切面 -->
        <!-- <aop:config>这个标签就告知spring要做代理对象了
                原则:如果实际对象有接口就jdk动态代理生成代理对象;如果实际对象没有接口就用cglib动态代理生成代理对象
                 可以人为改动proxy-target-class="true",强制用cglib
        -->
        <aop:config proxy-target-class="false">
            <!-- <aop:aspect 这个节点标签,告知spring使用哪个切面
                    可以写多个<aop:aspect节点,可以使用多个横切
            -->
            <aop:aspect id="myAspect" ref="tm">
                <!-- 告知spring 把切面 切在哪些类的哪些方法上,切点可以写多个-->
                <aop:pointcut id="myPointCut"
                              expression="execution(* com.hdu.service..*.*(..))"/>
                <!-- 前置通知 -->
                <aop:before
                    pointcut-ref="myPointCut"
                    method="begin"/>
                <!-- 后置通知 
                    属性returning="flag" flag对应的通知方法中的参数
                -->
                <aop:after-returning
                    pointcut-ref="myPointCut"
                    method="commit"
                    returning="flag"/>
                <!-- 异常通知 
                    属性throwing="ex"  ex对应的异常通知的方法的参数
                -->
                <aop:after-throwing
                    pointcut-ref="myPointCut"
                    method="rollback"
                    throwing="ex"/>
                    
                <!-- 最终通知 -->
                <aop:after
                    pointcut-ref="myPointCut"
                    method="finallyClose"/>
                <!-- 环绕通知 -->
    <!--              <aop:around -->
    <!--                 pointcut-ref="myPointCut" -->
    <!--                 method="around"/>         -->
            </aop:aspect> 
            
        </aop:config>    
        
    </beans>

        2.5 测试

     1 package com.hdu.test;
     2 
     3 import org.junit.Test;
     4 import org.springframework.context.ApplicationContext;
     5 import org.springframework.context.support.ClassPathXmlApplicationContext;
     6 
     7 import com.hdu.dao.UserDao;
     8 import com.hdu.entity.User;
     9 import com.hdu.service.UserService;
    10 
    11 
    12 public class TestSpringAOP {
    13     @Test
    14     public void testMethod1() {
    15         //初始化spring容器 
    16         ApplicationContext context = new ClassPathXmlApplicationContext("resources/spring_aop.xml");
    17         UserService userService=context.getBean("userService",UserService.class);
    18         System.out.println(userService.getClass());
    19         userService.addUser(new User());
    20         
    21         //userDao不会被代理
    22         UserDao userDao=context.getBean("userDao",UserDao.class);
    23         System.out.print("userDao不会被代理"+userDao.getClass());
    24     }
    25 }
    Test

      结果:

    class com.sun.proxy.$Proxy6
    begin transaction
    目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9
    目标方法-->addUser
    目标方法参数-->com.hdu.entity.User@4b5d6a01
    UserServiceImpl.addUser()
    UserDaoImpl.addUser()
    commit transaction
    目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9
    目标方法-->addUser
    目标方法参数-->com.hdu.entity.User@4b5d6a01
    目标方法的返回值-->true
    finallyClose
    目标对象-->com.hdu.service.impl.UserServiceImpl@769f71a9
    目标方法-->addUser
    目标方法参数-->com.hdu.entity.User@4b5d6a01
    userDao不会被代理class com.hdu.dao.impl.UserDaoImpl
    Conclusion

        2.6调用过程

        我们获取对象,要么是对象地址,要么是代理:System.out.println(userService.getClass());

      从打印结果 class com.sun.proxy.$Proxy6 可知,目前使用的是jdk动态代理。

        其中UserService userService = (UserService) context.getBean("userService");执行时spring已经把UserService变成代理对象。

      在执行userService.addUser();业务方法时就会被invoke方法拦截。就被切点表达式expression="execution(* com.hdu.service..*.*(..))"拦截。

      切点表达式expression="execution(* com.hdu.service..*.*(..))"匹配上了实现类,就会执行切面中通知。

        <aop:aspect id="myAspect" ref="tm">

          <aop:before pointcut-ref="myPointCut" method="begin"/>

        </aop:aspect>

      就找到TransactionManager类,找到before方法,并调用。

      也就是说只要程序满足切点表达式,就会调用切面的方法。最终我们就看到它不是真实的对象,而是一个代理对象。由代理去执行就会响应执行通知的方法。

    三.Spring AOP使用示例:使用注解的方式

        3.1 切面类

      参考手册:

        

      切点可以是局部的也可以是全局的。

     1 package com.hdu.aspect;
     2 
     3 import org.aspectj.lang.JoinPoint;
     4 import org.aspectj.lang.ProceedingJoinPoint;
     5 import org.aspectj.lang.annotation.After;
     6 import org.aspectj.lang.annotation.AfterReturning;
     7 import org.aspectj.lang.annotation.AfterThrowing;
     8 import org.aspectj.lang.annotation.Around;
     9 import org.aspectj.lang.annotation.Aspect;
    10 import org.aspectj.lang.annotation.Before;
    11 import org.aspectj.lang.annotation.Pointcut;
    12 import org.springframework.stereotype.Component;
    13 
    14 /**
    15  * 切面
    16  * @author Administrator
    17  *
    18  */
    19 @Component("tm")
    20 @Aspect
    21 public class TransactionManager {
    22     //the pointcut expression
    23     @Pointcut("execution(* com.hdu.service..*.*(..))")
    24     private void myPointCut() {}// the pointcut signature
    25     
    26     //前置通知方法
    27     //@Before("myPointCut()")
    28     public void begin(JoinPoint jp) {
    29         System.out.println("begin transaction");
    30         System.out.println("目标对象-->"+jp.getTarget());
    31         System.out.println("目标方法-->"+jp.getSignature().getName());
    32         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    33     }
    34     
    35     //后置通知方法
    36     //@AfterReturning(pointcut="myPointCut()",returning="flag")
    37     public void commit(JoinPoint jp,Object flag) {
    38         System.out.println("commit transaction");
    39         System.out.println("目标对象-->"+jp.getTarget());
    40         System.out.println("目标方法-->"+jp.getSignature().getName());
    41         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    42         System.out.println("目标方法的返回值-->"+flag);
    43     }
    44     //异常通知方法
    45     //@AfterThrowing(pointcut="myPointCut()",throwing="ex")
    46     public void rollback(JoinPoint jp,Exception ex) {
    47         System.out.println("rollback transaction");
    48         System.out.println("目标对象-->"+jp.getTarget());
    49         System.out.println("目标方法-->"+jp.getSignature().getName());
    50         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    51         System.out.println("目标方法抛异常-->"+ex.getMessage());
    52     }
    53     //最终通知
    54     //@After("myPointCut()")
    55     public void finallyClose(JoinPoint jp) {
    56         System.out.println("finallyClose");
    57         System.out.println("目标对象-->"+jp.getTarget());
    58         System.out.println("目标方法-->"+jp.getSignature().getName());
    59         System.out.println("目标方法参数-->"+jp.getArgs()[0]);
    60     }
    61 
    62     //环绕通知  能控制目标方法的执行
    63     @Around("myPointCut()")
    64     public Object around(ProceedingJoinPoint pjp)throws Throwable {
    65         Object retVal=null;
    66         try {
    67             System.out.println("around  begin transaction");
    68             System.out.println("目标对象-->"+pjp.getTarget());
    69             System.out.println("目标方法-->"+pjp.getSignature().getName());
    70             System.out.println("目标方法参数-->"+pjp.getArgs()[0]);
    71             //if() {
    72                 //就是直接指定目标方法
    73                 retVal = pjp.proceed();
    74                 
    75             //}
    76             System.out.println("around  commit transaction");
    77         }catch(Exception e) {
    78             e.printStackTrace();
    79             System.out.println("around  rollback transaction");
    80         }finally {
    81             System.out.println("around  finallyClose");
    82         }
    83         
    84         return retVal;
    85     }
    86 }

        3.2 配置清单

    <?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"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:util="http://www.springframework.org/schema/util" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        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
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/util 
            http://www.springframework.org/schema/util/spring-util.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
        <!-- 
            @Controller @Service @Repository @Component @Resource    @Autowired @Qualifier
         -->
        <!-- 实例化业务模型对象 -->
        <context:component-scan base-package="com.hdu.dao.impl"></context:component-scan>
        <context:component-scan base-package="com.hdu.service.impl"></context:component-scan>
        
        
        <!-- 实例化切面的对象 -->
        <context:component-scan base-package="com.hdu.aspect"></context:component-scan>
        
        <!-- spring 独有的配置,创建代理对象和确定如何横切切面
            @Aspect @Pointcut @Before @AfterReturning @AfterThrowing @After @Around
         -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    </beans>

        3.3 测试

     1 package com.hdu.test;
     2 
     3 import org.junit.Test;
     4 import org.springframework.context.ApplicationContext;
     5 import org.springframework.context.support.ClassPathXmlApplicationContext;
     6 
     7 import com.hdu.entity.User;
     8 import com.hdu.service.UserService;
     9 
    10 
    11 public class TestSpringAOP {
    12     @Test
    13     public void testMethod1() {
    14         //初始化spring容器 
    15         ApplicationContext context= new ClassPathXmlApplicationContext("resources/spring_aop.xml");
    16         UserService userService=context.getBean("userService",UserService.class);
    17         System.out.println(userService.getClass());
    18         userService.addUser(new User());//连接点 启动横切
    19     }
    20     
    21 
    22 }
    Test

         结果

    class com.sun.proxy.$Proxy13
    around  begin transaction
    目标对象-->com.hdu.service.impl.UserServiceImpl@217ed35e
    目标方法-->addUser
    目标方法参数-->com.hdu.entity.User@f5958c9
    UserServiceImpl.addUser()
    UserDaoImpl.addUser()
    around  commit transaction
    around  finallyClose
    Conclusion

      

      

  • 相关阅读:
    java线程的理解
    linux常用命令
    排序算法-(2)-选择排序
    git查看某个文件修改历史
    Information:java: javacTask: 源发行版 1.8 需要目标发行版 1.8
    排序算法-(1)-插入排序
    去重脚本
    771.宝石与石头
    json 的应用
    xml 文件处理
  • 原文地址:https://www.cnblogs.com/kuotian/p/8863257.html
Copyright © 2011-2022 走看看