zoukankan      html  css  js  c++  java
  • 09 Spring框架 AOP (二) 高级用法

    上一篇文章我们主要讲了一点关于AOP编程,它的动态考虑程序的运行过程,和Spring中AOP的应用,前置通知,后置通知,环绕通知和异常通知,这些都是Spring中AOP最简单的用法,也是最常用的东西,本节我们 要讲:对上节课的补充和AOP的高级使用,本文将以以下的顺序来进行分析:

    • 无接口的CGLIB动态代理
    • 有接口的CGLIB动态代理
    • 方法名匹配切入点顾问
    • 正则表达式方法切入点顾问
    • 自动代理生成器

    (一)无接口的CGLIB动态代理

    上一节我们示范的通知(前置,后置...),都有实现接口,我们在动态代理中也曾经强调过jdk的动态代理只能实现有接口的主业务方法,因为动态代理生成的代理类都会实现Proxy从而会放弃继承实现类的父类,所以jdk的动态代理只能够实现有接口的方法类。
    我们这里就不对CGLIB多做介绍,后面我会为CGLIB再进行探讨。
    言归正传,无接口的通知怎么实现,其实和上一节我们讲的一样,我们按同样的方式配置就好,Spring会自动识别我们实现的方法是否实现了接口,从而动态的来选择实现方法,当然,如果实现的是有接口的方法,Spring在默认情况下回优先使用jdk的动态代理(CGLIB已经停止维护好长时间)。

    (二)有接口的CGLIB动态代理

    既然Spring对那些实现接口的目标类会优先使用jdk的动态代理来实现AOP,那么我们怎么样才能让Spring使用CGLIB来代现有接口的目标类,
    我们就以上一节的后置通知为例(代码就不再展示,在08中有):
    我们只需要在配置文件中增加一些配置即可:

    //这个是后置通知的原配置文件
    	<bean id="service" class="com.test.afterMethodAdvice.SomeServiceImp"/>
    	
    	<bean id="myAdvice" class="com.test.afterMethodAdvice.myAfterMethodAdvice"/>
    	
    	<bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="targetName" value="service"/>
    		<!-- <property name="target" value="service"/> -->
    		<property name="interceptorNames" value="myAdvice"/>
    		<!---我们只需在这里加上一些配置即可实现->
    	</bean>
    

    加上这样的配置:

    <property name="proxyTargetClass" value="true"/>
    <!--proxyTargetClass是org.springframework.aop.framework.ProxyFactoryBean的一个属性,表明是否代理目标实现类,用来控制是否使用CGLIB-->
    
    <!--同样是ProxyFactoryBean的一个属性,表明是否优化-->
    <property name="optimize" value="true"/>
    

    (三)方法名匹配切入点顾问

    这个名字听上去不知道是什么对吧?在讲这个之前我们先说一下什么是顾问:
    顾问:
    顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器

    在前面我们AOP的实现中,我们只能够通过通知,对一个类的所有方法进行加强,但是不能够选择性的对一个目标对象的个别方法进行加强,所以Spring提供了顾问这个概念,你可以将通知当做顾问的一个组成部分。

    下面我们就来看看怎么实现顾问,首先是方法名匹配切入点顾问(下面的实例是在后置通知的基础上进行的增强),指定对应的方法名(简单方法名)来实现加强:

    //目标接口
    public interface SomeServices {
    	void doFirst();
    	void doSecond();
    	void doThird();
    }
    

    //目标实现类
    public class SomeServiceImp implements SomeServices{
    	
    	@Override
    	public void doFirst() {
    		System.out.println("print first");
    	}
    	
    	@Override
    	public void doSecond() {
    		System.out.println("print second");
    		
    	}
    
    	@Override
    	public void doThird() {
    		System.out.println("print third");
    		
    	}
    
    }
    
    

    //通知类
    public class myAfterMethodAdvice implements AfterReturningAdvice {
    
    	//returnValue:业务方法的返回值
    	//method:业务方法属性类
    	//args:方法参数
    	//target:目标类
    	@Override
    	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    		System.out.println("执行业务后方法");
    	}
    }
    

    配置文件:

    <!--注册目标类  -->
    	<bean id="service" class="com.test.NameMatchedAdvisor.SomeServiceImp"/>
    <!-- 注册后置通知 -->
    	<bean id="myAdvice" class="com.test.NameMatchedAdvisor.myAfterMethodAdvice"/>
    	
    	<!-- 注册顾问 -->
    	<bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    		<!-- 将通知放到顾问中 -->
    		<property name="advice" ref="myAdvice"/>
    		<!-- 选定加强的方法 -->
    		<!-- <property name="mappedName" value="doFirst"/> -->
    		<!-- 加强多个方法 -->
    		<!-- <property name="mappedNames" value="doFirst,doSecond"/> -->
    		<!-- 使用通配符,来指定加强含有ir的方法 -->
    		<property name="mappedNames" value="*ir*"></property>
    	</bean>
    	
    	
    	<bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="targetName" value="service"/>
    		<!-- <property name="target" value="service"/> -->
    		<property name="interceptorNames" value="myAdvisor"/><!--这里要放入顾问-->
    	</bean>
    

    //测试类
    public class test {
    
    	@Test
    	public void Test01() {
    		String source = "com/test/NameMatchedAdvisor/applicationContext.xml";
    		ApplicationContext ac = new ClassPathXmlApplicationContext(source);
    		SomeServices service = (SomeServices)ac.getBean("ProxyService");
    		service.doFirst();
    		service.doSecond();
    		service.doThird();
    	}
    }
    
    

    控制台输出:

    print first
    执行业务后方法
    print second
    print third
    执行业务后方法
    //我们会发现只有第一个和第三个方法执行了增强(后置通知)
    

    (四)正则表达式方法切入点顾问

    正则匹配切入点顾问,和命名匹配切入点对象相同,只是这里不是用方法名指定,而是用正则表达式指定,并且指定的是全限定方法名(带包名)配置文件上有一点小差异,其他不变。
    这里我们只给出配置文件:

    	<!--注册目标类  -->
    	<bean id="service" class="com.test.RegexMatchedAdvisor.SomeServiceImp"/>
    	
    	<!-- 注册后置通知 -->
    	<bean id="myAdvice" class="com.test.RegexMatchedAdvisor.myAfterMethodAdvice"/>
    	
    	<!-- 注册顾问 -->
    	<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    		<property name="advice" ref="myAdvice"></property>
    		<property name="pattern" value=".*doFirst"></property><!-- 这里方法的全限定方法名 -->
    	</bean>
    	
    	
    	<bean id="ProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="targetName" value="service"/>
    		<!-- <property name="target" value="service"/> -->
    		<property name="interceptorNames" value="myAdvisor"/>
    	</bean>
    

    (五)自动代理生成器

    上面我们只配置了一个目标类,当我们需要创建多个目标类的时候,那么我们岂不是要配置多个代理工厂bean,这样势必会使得我们的代码臃肿,不利于我们项目的维护,所以Spring提供了自动代理生成器,它会根据需要自动为我们实现代理bean的功能,但是使用这个自动生成代理需要必须是顾问,即不能是通知!

    我们来看看怎么配置:

    
    	<!--注册目标类  -->
    	<bean id="service" class="com.test.AutoProxyCreator.SomeServiceImp"/>
    	
    	<!-- 注册后置通知 -->
    	<bean id="myAdvice" class="com.test.AutoProxyCreator.myAfterMethodAdvice"/>
    	<!-- 注册顾问 -->
    	<bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    		<!-- 将通知放到顾问中 -->
    		<property name="advice" ref="myAdvice"/>
    		<!-- 选定加强的方法 -->
    		<!-- <property name="mappedName" value="doFirst"/> -->
    		<!-- 加强多个方法 -->
    		<!-- <property name="mappedNames" value="doFirst,doSecond"/> -->
    		<!-- 使用通配符,来指定加强含有ir的方法 -->
    		<property name="mappedNames" value="*ir*"></property>
    	</bean>
    	
    	<!---下面的配置进行自动代理-->
    	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    	
    

    我们在测试的时候也不需要获取代理类的bean了,可以直接:

    SomeServices service = (SomeServices)ac.getBean("service");
    

    直接获取我们目标实现类注册的bean就好了!

    这个自动代理我们如果看它的源码的话,我们会发现它实现了BeanPostProcessor接口,熟悉不?对!就是我们的bean前处理和bean后处理,Spring通过它实现了对我们原来目标类的代理(本人才疏学浅就不带大家看源码了)。

    但是还有一个问题,刚才我们说了,这个自动代理生成器默认的将所有的bean全代理了固然方便,但是并不是所有的对象都需要代理啊,我怎么选择性的进行代理嘞?
    和配置普通bean一样,我们同样可以配置它的属性来完成对目标类的选择性代理!

    
    	<!--注册目标类  -->
    	<bean id="service" class="com.test.BeanNameAutoProxyCreator.SomeServiceImp"/>
    	<bean id="service2" class="com.test.BeanNameAutoProxyCreator.SomeServiceImp"/>
    	
    	<!-- 注册后置通知 -->
    	<bean id="myAdvice" class="com.test.BeanNameAutoProxyCreator.myAfterMethodAdvice"/>
    	<!-- 注册顾问 -->
    	<bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    		<!-- 将通知放到顾问中 -->
    		<property name="advice" ref="myAdvice"/>
    		<!-- 选定加强的方法 -->
    		<!-- <property name="mappedName" value="doFirst"/> -->
    		<!-- 加强多个方法 -->
    		<!-- <property name="mappedNames" value="doFirst,doSecond"/> -->
    		<!-- 使用通配符,来指定加强含有ir的方法 -->
    		<property name="mappedNames" value="doFirst"></property>
    	</bean>
    	
    	<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    		<property name="beanNames" value="service"></property>
    		<property name="interceptorNames" value="myAdvisor"></property>
    	</bean>
    

    注意:和默认自动代理的区别:

    class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    

    这样就可以选择性的代理目标对象了!

    至此我们的Spring对AOP的实现就讲完了(关于AspectJ对AOP的实现我们下一节讲),博主整理的这些配置供以后自己查阅,也供大家参考,如有不准确或者错误,不吝赐教!

  • 相关阅读:

    k
    通过类名调用类方法
    类Area的getArea方法是一个重载方法
    构造cry
    两个lader对象共享bottom
    向一个方法的基本数据类型参数传值
    Circle
    常量的用法
    显示本机时间
  • 原文地址:https://www.cnblogs.com/MindMrWang/p/8144037.html
Copyright © 2011-2022 走看看