Spring笔记系列_jp(四)

第四天
1  advisor  增强器
   作用:筛选指定要拦截(要代理)的方法
        默认情况下advice会拦截目标对象中的所有方法,advisor增强器可以进一步进行方法的筛选,筛选的方法就是我们的adivce需要拦截的方法。
   指定为advisor的接口为:
    public interface PointcutAdvisor {
        Pointcut getPointcut();
        Advice getAdvice();
    }
    spring中已经给我们提供了实现类RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了
    
    xml配置文件:
    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="beforeAdvice"
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    <!-- 配置advisor 增强器-->
    <!-- 作用:筛选要拦截(要代理)的方法 -->
    <bean name="advisor"
    class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice 表示增强器要在哪一个advice起作用-->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法(连接点) -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao"
     class="com.briup.aop.dao.AccountDaoImpl"/>
    <!-- 配置目标对象 -->
    <bean name="target"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <!-- 配置代理对象 -->
    <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
    <bean name="proxy"
    class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
        
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
        
        <!-- 注入advice/advisor  可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>advisor</value>
            </list>
        </property>
    </bean>
    


2  AutoProxy 自动代理:DefaultAdvisorAutoProxyCreator类的使用
   使用原因:在配置文件中我们往往需要给很多个目标对象设置代理对象,那么上面例子的方式就需要每个目标对象的代理对象都需要配置一套类似的标签
   自动代理:可以用很少的配置为xml文件中的目标对象自动的生成对应的代理对象

   xml配置文件:
    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="beforeAdvice"
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    <!-- 配置advisor -->
    <!-- 作用:筛选要拦截的方法 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法 -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
                <value>.*deposit</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
    
    <!-- 配置目标对象 -->
    <bean name="target"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target2"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target3"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    
    <!-- 配置代理对象 -->
    <!-- 这里使用自动代理的方式 autoproxy -->
    <!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
    <bean name="proxy"
    class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    </bean>
    <!--
        使用自动代理的时候需要注意的方面:
        1.当前的配置里面一定要有一个advisor的配置
        2.不需要向自动代理类中注入任何信息
        3.不管目标对象是否实现了一个或多接口,自动代理的方式
          都能够为它产生代理对象(CGLib的方式).
        4.从spring容器中拿代理对象的时候,需要通过目标对象的
          名字来拿。
    5.spring如何确定配置文件中哪个bean是作为目标对象:
      通过advisor中筛选的方法,如果这个bean中含有advisor中所配置的方法,则这个bean将来称为我们的目标对象进行代理
     -->


3  AutoProxyByName 通过名字进行自动代理:BeanNameAutoProxyCreator类的使用
    使用原因:虽然自动代理可以很方便的给xml文件中的目标对象设置对应的代理对象,但是并不是xml文件中的所有对象都是我们的目标对象,我们更想希望可以进一步筛选出某几个对象为我们的目标对象
    名字进行自动代理:解决了上面的问题,给我们提供了筛选目标对象的配置方式

    xml配置文件:
    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="beforeAdvice"
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    
    <!-- 配置advisor -->
    <!-- 作用:筛选要拦截的方法 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法 -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
                <value>.*deposit</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao"
     class="com.briup.aop.dao.AccountDaoImpl"/>
    
    <!-- 配置目标对象 -->
    <bean name="target"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target2"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target3"
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    
    
    <!-- 配置代理对象 -->
    <!-- 这里使用自动代理的方式 autoproxybyname -->
    <bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 注入需要被代理的对象名字 -->
           <property name="beanNames">
               <list>
                   <value>target</value>
                   <value>target2</value>
                   <value>dao</value>
                   <value>service*</value>
               </list>
           </property>
           
           <!-- 注入advice或者advisor -->
           <property name="interceptorNames">
               <list>
                   <value>advisor</value>
               </list>
           </property>
    </bean>
    <!--
        使用自动代理的时候需要注意的方面:
        1.当前的配置里面有没有advisor的配置都没关系
        2.需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
        3.不管目标对象是否实现了一个或多接口,自动代理的方式
          都能够为它产生代理对象.
        4.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。
     -->

4  aop:config标签
   使用aop的专用标签来完成相关的配置.
   其中主要表现是使用AspectJ的expression的操作:
   execution(modifiers-pattern ret-type-pattern declaring-type-pattern name-pattern(param-pattern) throws-pattern)除了返回类型模式,名字模式和参数模式以外,所有的部分都是可选的。 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是 *,它代表了匹配任意的返回类型。 一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 * 通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:() 匹配了一个不接受任何参数的方法, 而 (..) 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 (*) 匹配了一个接受一个任何类型的参数的方法。 模式 (*,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型
   
   注意在使用之前需要在xml文件的beans标签中加入新的schame文件:并在Eclipse中进行关联配置
   <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:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    
   下面给出一些常见切入点表达式的例子。  

 
   1)任意包下的任意类中的公共方法的执行:  
      execution(public * *(..))
   2)任何一个以“set”开始的方法的执行:  
      execution(* set*(..))
   3)AccountService 接口的任意方法的执行:    
      execution(* com.briup.service.AccountService.*(..))
   4)定义在service包里的任意方法的执行:  
      execution(* com.briup.service.*.*(..))
   5)定义在service包或者子包里的任意方法的执行:  
      execution(* com.briup.service..*.*(..))
   6)在service包里的任意连接点(在Spring AOP中只是方法执行)
      within(com.xyz.service.*)
   7)在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) :  
      within(com.xyz.service..*)



   注意:  1.从spring容器中拿代理对象的时候也是要用目标对象的名字来拿。
      2.没有实现任何接口的目标对象也能产生代理对象。
      3.proxy-target-class="true".放在<aop:config>标签中。
      默认是false:目标类有接口则使用JDK动态代理。没有实现接口就用CGLib的方式。
      如果为true:目标类无论有无实现接口都要使用CGLib类来产生动态代理对象

        <!-- 配置aop的代理 -->
    <aop:config>
        <!-- 定义一个切入点 并给切入点起名为myPointCut -->
        <!-- 切入点是一组连接点的集合 -->
        <aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>

        <!-- 定义哪一个advice在哪一个切入点上面起作用 -->
        <aop:advisor advice-ref="beforeAdvice" pointcut-ref="myPointCut" />
    </aop:config>
    <!--
        expression="execution(public * com.briup.aop.service.*.*(..))"
        这个引号""里面就是用表达式的方式来定义切入点,只要是符合我们这个表达式要求的
        方法就是我们的连接点,连接点的集合就是我们要定义的切入点。
        表达式中从左到右的*号:
            第一个* 表示方法的返回类型不限。
            第二个* 表示包中的任意一个类
            第三个* 表示类中的任意一个方法
            
        同时方法的参数也没有限制.
     -->

5  在一个切面类中定个多个方法,根据xml文件的配置每个方法都可以织入到切入点的不同位置,并且advice是在aop的标签中进行配置,不需要再写对应的advice类了
   例如:
    //这个类相当于我们之前的切面类
    //只不过这个切面类中有很多方法都可以织入到切入点上面
    //我们可以控制把这里的任何一个方法织入到任何一个切入点上面
    public class XmlHandler {
        
        public void beforeTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" before...");
        }
        
        
        public void afterTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" after...");
        }
        
        public void afterReturningTest(JoinPoint p){
            
            System.out.println(p.getSignature().getName()+" afterReturning");
            
        }
        
        //在和aroundAdvice结合的时候,这个方法一定要加上这个ProceedingJoinPoint类型的参数
        public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
            //JoinPoint对象不能调用连接点所表示的方法
            //ProceedingJoinPoint能调用连接点所表示的方法 pjp.proceed()
            System.out.println(pjp.getSignature().getName()+" is start..");
            //调用到连接点方法
            Object obj = pjp.proceed();
            System.out.println(pjp.getSignature().getName()+" is end..");
            return obj;
        }
        
        public void throwingTest(JoinPoint p,Exception ex){
            System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
            
        }
    }
    
        xml文件配置:
    <!-- 配置dao层对象 -->
    <bean id="dao"
        class="com.briup.aop.dao.AccountDaoImpl"/>
     <!-- 配置目标对象 -->
    <bean name="target"
    class="com.briup.aop.service.AccountServiceImpl">
            <property name="accountDao" ref="dao"></property>
    </bean>
    <!-- 配置切面类 里面包含了很多不同的切面 -->
    <!-- 我们需要把这个切面类中的不同切面织入到织入点中的不同方法中 -->
    <bean name="handler" class="com.briup.aop.xml.XmlHandler"></bean>
    
    <!-- 配置aop的代理 -->
    <aop:config>
        <!-- 定义切入点名为myPointCut -->
        <aop:pointcut expression="execution(public * com.briup.aop.service.*.*(..))" id="myPointCut"/>
        
        <!-- 定义切面类 以及需要使用的advice -->
        <aop:aspect id="aspect" ref="handler">
            <!-- 表示beforeAdvice会把切面类handler中的beforeTest方法织入到名字叫myPointCut的切入点上面 -->
            <aop:before method="beforeTest" pointcut-ref="myPointCut"/>

            <!-- after表示不管方法是否正常结束都会起作用 -->
            <aop:after method="afterTest" pointcut-ref="myPointCut"/>

            <!-- after-returning表示方法正常结束才会起作用(抛异常时候不起作用) -->
            <aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>

            <aop:around method="aroundTest" pointcut-ref="myPointCut"/>

            <!-- throwing="ex"表示throwingTest方法中接收异常对象的名字一定要是ex -->
            <aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>

        </aop:aspect>
    </aop:config>
    
    注意:<aop:config proxy-target-class="true"> 如果这样配置则是强制使用CGLIB方式进行代理


6  使用注解配置AOP:其实就是在上面的类XmlHandler中加入上注解然后去掉xml中的aop标签配置,这里把类改名为AnnotationHandler,
   例子:
    @Component
    @Aspect
    public class AnnotationHandler {
        /*
         * 在一个方法上面加上注解来定义切入点
         * 这个切入点的名字就是这个方法的名字
         * 这个方法本身不需要有什么作用
         * 这个方法的意义就是:给这个 @Pointcut注解一个可以书写的地方
         * 因为注解只能写在方法、属性、类的上面,并且方法名作为切入点的名字
         * */
        @Pointcut("execution(public * com.briup.aop.service..*.*(..))")
        public void myPointCut(){}
        
        //注:这里面的所有方法的JoinPoint类型对象都可以去掉不写
        @Before("myPointCut()")
        public void beforeTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" before...");
        }
        
        
        /*
         * @After和@AfterReturning
         *
         * @After标注的方法会在切入点上的方法结束后被调用(不管是不是正常的结束).
         * @AfterReturning标注的方法只会在切入点上的方法正常结束后才被调用.
         * */
        @After("myPointCut()")
        public void afterTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" after...");
        }
        @AfterReturning("myPointCut()")
        public void afterReturningTest(JoinPoint p){
            
            System.out.println(p.getSignature().getName()+" afterReturning");
            
        }
        
        @Around("myPointCut()")
        public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
            System.out.println(pjp.getSignature().getName()+" is start..");
            //调用连接点的方法去执行
            Object obj = pjp.proceed();
            System.out.println(pjp.getSignature().getName()+" is end..");
            return obj;
        }
        
        
        
        //在切入点中的方法执行期间抛出异常的时候,会调用这个 @AfterThrowing注解所标注的方法
        @AfterThrowing(value="myPointCut()",throwing="ex")
        public void throwingTest(JoinPoint p,Exception ex){
            System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
            
        }
        
        
    }
    
    xml配置:注意给例子中使用的其他的类上面也使用注解
    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="com.briup.aop"/>

    注意:<aop:aspectj-autoproxy proxy-target-class="true"/>这样配置则是强制使用CGLIB进行代理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值