Sping-AOP

Spring AOP        

        在Spring Framework简介中介绍了spring的整体框架,在Spring IOC中介绍了spring的核心功能,那么spring framework中,另外一个比较重要的模块就是Spring AOP。那么,什么是AOP呢?AOP是Aspect-Oriented Programming的缩写,是一种不同与面向对象(Object-Oriented Programming (OOP))的编程方式。其主要的设计目的是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码,也就是无入侵式的编程。

AOP概念

        要理解AOP,就必须要对一些关键词非常了解,下面列表是AOP中常用的一些关键词:
  • Aspect,切面,可以包含多个类,多个方法组成,由Advice,Pointcut等元素组成。在常见的事务管理中,我们通常会使用如下的配置:
    <aop:config>
        <aop:pointcut id="service" expression="execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))" />
	<aop:advisor pointcut-ref="service" advice-ref="advice" />
    </aop:config>
  • Join Point,切入点,通常指的是一个方法,也就是在调用时,匹配Pointcut表达式指定的方法。
  • Advice,增强点,通常是在执行某个方法时,根据需要来执行某种操作。比如在发送邮件前,先要记录下日志,这种就是属于前置增强。发送邮件后发送通知给管理员,这种就属于后置增强点。
  • Pointcut,切入点集合,通常是一个表达式,比如:execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))。
  • Introduction:为目标对象声明一些字段或者方法,通常是声明接口。
  • Target Object:目标对象,需要使用AOP增强的目标类
  • AOP proxy:代理对象,由spring framework动态创建
  • Wearving:根据配置信息,在编译、加载类、运行时,动态的代码组织起来。

Advice的类型

       在AOP的规定中,Advice的类型主要有:Before Advice,After Advice,Around Advice,After returning Advice, After throwing advice。如下图所示:

        通过上面的图片,我们可以看出,这些定义都是围绕着代码的执行时间点来命名的,比如Before advice就是在执行Join point前插入一段执行代码,After advice就是在执行成功后插入一段代码,After throwing advice就是在抛出异常后执行一段代码,Around advice就是把Before advice、After advice同时都执行。

AOP的实现原理

       介绍完AOP的一些基本概念后,下面我们来探讨下AOP的实现。在设计模式中,有一种设计模式叫做代理设计模式。那么什么是代理设计模式呢?在生活中,该设计模式也随处可见。比如我们经常在淘宝上海淘,通常会去某家海淘店铺购买物品,然后店主帮我们去购买,发货给我们。如下图:

       在这里面,我们就是购买者,换成java角度而言,就是调用者;海淘店主就是代理对象(Proxy);实际货品提供者就是目标对象(Target object)。通过代理模式,即可为Target object提供一些而外的功能,也就是上文所述的Advice。在这里面,可能大家都很疑惑,Advice是在代码实现层是如何动态加入进去的呢?这个其实也不难,在JDK中,对于动态代理模式提供的相应的API,可以动态的生成代理对象。如下代码所示:
<span style="font-size:18px;">/**
 * 接口PersonService,Target Object的接口定义,因为jdk只能生成接口的动态代理对象
 * */
public interface PersonService{
    public void save();

    public void add();

    public void update();
}

/**
 * 要被代理的对象,也就是Target object
 * */
public class PersonServiceImpl implements PersonService{
    private String user;

    public PersonServiceImpl(String user){
        this.user = user;
    }

    //给外部提供接口
    public String getUser(){
        return user;
    }

    public void add(){
        System.out.println("I am the PersonServiceImpl add() method");
    }
    public void update(){
    }

    public void save(){
    }
}

</span>
<span style="font-size:18px;">import java.lang.reflect.*; 
/*在这里,直接让工厂实现了InvocationHandler,所以在调用createProxy()的时候,给Proxy.netProxyInstance()的第三个参数传进去了一个this。如果要把工厂和InvocationHandler解耦,可以重新顶一个实现InvocationHandler的类*/
public class JDKProxyFactory implements InvocationHandler{
     
    //要代理的目标对象
    private Object target;

    public JDKProxyFactory(Object obj){
        target = obj;
    }
    
    //生产一个代理对象
    public Object createProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    
    //当客户端调用目标对象进行工作的时候,就会被这个代理对象拦截到    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        //将目标对象向下转型
        PersonServiceImpl bean = (PersonServiceImpl)target;
        //这里进行拦截
        if(bean.getUser() != null && !"".equals(bean.getUser())){
            //调用目标对象的方法
            Object result = method.invoke(target, args);
            return result;
        }else{
            System.out.println("sorry, you have no limitation to access this resource!!!");
            return null;
        }
    }
}</span>
       在代码中,我们发现创建一个代理对象并不难,主要步骤如下:
     1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
     2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
     3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
     4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
     但是凡事都有弊端,java提供生成代理对象的API其实是不支持为类生成代理对象,只能给接口生成代理对象。所以在这里面,Spring AOP借助于Aspectj代理框架来完成该任务,实现类的动态代理对象的生成。有了代理模式,动态代理对象的生成,那么面向切面大功告成了。
      

AOP的应用

      在spring framework中,AOP通常和IOC模块一同使用,借助AOP来实现声明式的事务。下面我们通过配置一个声明式的事务,通过观察日志来了解AOP的工作原理。首先我们在IOC中声明如下几个bean:
<span style="font-size:18px;">        <!-- 对数据源进行事务管理 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		p:dataSource-ref="dataSource" />

	<!-- 配置事务传播特性 -->
	<tx:advice id="advice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="del*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="find*" propagation="REQUIRED" />
			<tx:method name="get*" propagation="REQUIRED" />
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="apply*" propagation="REQUIRED" />
			<tx:method name="list*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>
        <!--配置一个AOP切面,同时指定advice、pointcut,那么一个最简单的AOP就配置完毕。-->
	<aop:config>
		<aop:pointcut id="service"
			expression="execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))" />
		<aop:advisor pointcut-ref="service" advice-ref="advice" />
	</aop:config></span>
        单元测试类,这里面使用spring + junit的方式来运行。
<span style="font-size:18px;">package com.skg.luohong.biz.ou.system;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import com.skg.luohong.biz.ou.system.service.IDaysUserService;

/**
 *
 * @author 骆宏
 * @date 2015-08-29 10:27:09
 * @author 846705189@qq.com
 * @author 15013336884
 * @blog http://blog.csdn.net/u010469003
 * */
@ContextConfiguration({"classpath:conf/spring-mybatis.xml", 
	"classpath:conf/spring.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
public class DaysUserTest{
    
	@Autowired
	private IDaysUserService service;
	
	@Test
	public void testCrud(){
	    System.out.println(service.countAll());
		System.out.println(service.findAll());
	}
}
</span>
        日志输出,下面为IOC容器初始化bean的过程,下面的日志为IOC创建Pointcut, Advice,TransactionManager bean的过程:
<span style="font-size:18px;">2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean 'sqlSessionFactory'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.mybatis.spring.mapper.MapperScannerConfigurer#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating shared instance of singleton bean 'transactionManager'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean 'transactionManager'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Eagerly caching bean 'transactionManager' to allow for resolving potential circular references
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'dataSource'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Invoking afterPropertiesSet() on bean with name 'transactionManager'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean 'transactionManager'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating shared instance of singleton bean 'advice'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean 'advice'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Eagerly caching bean 'advice' to allow for resolving potential circular references
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'transactionManager'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean '(inner bean)'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [save*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [del*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [update*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [add*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [countAll*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [find*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [get*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [insert*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [apply*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [list*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean '(inner bean)'
2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Invoking afterPropertiesSet() on bean with name 'advice'</span>
       下面的日志展示了AOP对事物的拦截:
<span style="font-size:18px;">2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'transactionManager'
2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Creating new transaction with name [testCrud]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [com.mysql.jdbc.JDBC4Connection@3ff1b8db] for JDBC transaction
2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ff1b8db] to manual commit
2015-08-29 10:39:01 [org.springframework.test.context.transaction.TransactionalTestExecutionListener]-[DEBUG] No method-level @Rollback override: using default rollback [true] for test context [TestContext@7475b962 testClass = DaysUserTest, testInstance = com.skg.luohong.biz.ou.system.DaysUserTest@5cde0ca9, testMethod = testCrud@DaysUserTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@76115ae0 testClass = DaysUserTest, locations = '{classpath:conf/spring-mybatis.xml, classpath:conf/spring.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
2015-08-29 10:39:01 [org.springframework.test.context.transaction.TransactionalTestExecutionListener]-[INFO] Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@19dde4d9]; rollback [true]</span>

总结

        AOP是一种不同于OOP的编程风格,在事务管理,日志管理等需要横跨多个类的地方,有着非常广泛的用途。AOP本身的概念其实并不难,如果对代理设计模式掌握的较好,那么AOP就更加不在话下。最近这段时间,工作较为充实,抽空把Spring AOP的知识整理了一番,受益匪浅。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值