Spring----AOP

来源:互联网 发布:oracle删除用户及数据 编辑:程序博客网 时间:2024/05/22 01:30

一、AOP概述

1.1、什么是AOP?

      AOP:Aspect Oriented Programing,面向切面编程。
      AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
      SpringAOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类植入增强代码。

1.2、AOP底层原理

AOP的顶层实现原理就是代理机制。

1.3、Spring的AOP代理(一窥)

JDK动态代理:对实现了接口的类生成代理。
CGLib代理机制:对类生成代理。

1.4、AOP术语

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知。
  • Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期间为类动态地添加一些方法或Field。
  • Target(目标对象):代理的目标对象。
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
  • Proxy(代理):一个类被AOP织入增强后就产生一个结果代理类。
  • Aspect(切面):是切入点和通知(引介)的结合。

1.5、AOP底层实现(二探)

JDK动态代理机制

CGLib代理机制

二、Spring中的AOP

2.1、Spring的传统AOP

Spring中的通知:

  • 前置通知(org.springframework.aop.MethodBeforeAdvice):在目标方法执行前实施增强。
  • 后置通知(org.springframework.aop.AfterReturningAdvice):在目标方法执行后实施增强。
  • 环绕通知(org.aopalliance.intercept.MethodInterceptor):在目标方法执行前后实施增强。
  • 异常抛出通知(org.springframework.aop.ThrowsAdvice):在方法抛出异常后实施增强。
  • 引介通知(org.springframework.aop.IntroductionInterceptor):在目标类中添加一些新的方法和属性。

Spring中的切面类型:

  • Advisor:Spring中的传统切面,一个切点和一个通知的组合。
  • Aspect:多个切点和多个通知的组合。

Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截(不带切点的切面,对所有方法进行拦截)。
PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类的某些方法(带有切点的切面,对指定方法进行拦截)。

三、Spring的AOP开发

3.1、不带切点的切面

第一步:导入相应的jar包

  • spring-aop-3.2.0.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar

第二步:编写被代理的对象
BookDao接口:

public interface BookDao {    public void add();    public void delete();    public void update();    public void serach();}

BookDaoImpl实现类

第三步:编写增强的代码

import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class MyBeforeAdvice implements MethodBeforeAdvice {    @Override    public void before(Method method, Object[] args, Object target)            throws Throwable {        System.out.println("进行前置增强!");    }}

第四步:生成代理
Spring通过配置生产代理。
Spring生产代理基于ProxyFactoryBean类,底层自动选择使用JDK的动态代理还是CGlib的代理。
ProxyFactoryBean类属性:
target:代理的目标对象。
proxyInterfaces:代理要实现的接口。
如果多个接口可以使用以下格式赋值:
<list>
     <value></value>
     <value></value>
     <value></value>
      ………
</list>
proxyTargetClass:是否对类代理而不是接口,设置为true时,使用CGlib代理。
interceptorNames:需要织入目标的Advice。
singleton:返回代理是否为单例,默认为单例。
optimize:当设置为true时,强制使用CGlib。
在applicationContent.xml中进行配置:

<!-- 定义目标对象 -->    <bean id="bookDao" class="cn.xpu.hcp.impl.BookDaoImpl"></bean>    <!-- 定义增强 -->    <bean id="beforeAdvice" class="cn.xpu.hcp.advice.MyBeforeAdvice"></bean>    <!-- Spring支持配置生成代理 -->    <bean id="bookDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 设置目标对象 -->        <property name="target" ref="bookDao"/>        <!-- 设置实现的接口,value中写接口的全路径 -->        <property name="proxyInterfaces" value="cn.xpu.hcp.dao.BookDao"/>        <!-- 设置增强 ,使用value而不是ref-->        <property name="interceptorNames" value="beforeAdvice"></property>    </bean>

第五步:测试

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class AopTest {    @Autowired    @Qualifier("bookDaoProxy")//注入代理对象    private BookDao bookDao;    @Test    public void test() {        bookDao.add();        bookDao.delete();        bookDao.update();        bookDao.serach();    }}

测试结果:
这里写图片描述

3.2、带切点的切面

使用不带切点的切面对所有方法进行了增强,当我们只对特定方法实现增强时这就不适用了。我们选择带切点的切面。
PointcutAdvisor接口:
DefaultPointcutAdvisor:最常用的切面类型,他可以通过任意Pointcut和Advice组合定义切面。
RefexpMethodPointcutAdvisor:构造正则表达式切点切面。
第一步:创建被代理对象
我使用和上面一样的
第二步:编写增强的类
我选择环绕增强。

import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class MyAroundAdvice implements MethodInterceptor {    @Override    public Object invoke(MethodInvocation invocation) throws Throwable {        System.out.println("环绕前增强。。。");        Object result = invocation.proceed();//执行目标对象的方法        System.out.println("环绕后增强。。。");        return result;    }}

第三步:使用配置生成代理

<!-- 定义目标对象 -->     <bean id="bookDao" class="cn.xpu.hcp.impl.BookDaoImpl"></bean>    <!-- 定义增强  -->       <bean id="aroundAdvice" class="cn.xpu.hcp.advice.MyAroundAdvice"></bean>    <!-- 定义切点、切面 -->    <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">        <!-- 使用正则表达式,规定哪些方法执行增强 -->        <!-- .表示任意字符;*表示任意个 -->        <!--<property name="pattern" value=".*"/>--><!-- .* 表示任意方法,和不带切点切面一样了 -->        <!--<property name="pattern" value="cn\.xpu\.hcp\.impl\.BookDaoImpl\.add.*"/>--><!-- cn.xpu.hcp.impl.BookDaoImpl 中add开头的方法 -->        <property name="pattern" value=".*add.*"/>        <!-- 具体应用至哪个增强 -->        <property name="advice" ref="aroundAdvice"/>    </bean>    <!-- 定义生成代理对象 -->    <bean id="bookDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <!-- 设置目标对象 -->        <property name="target" ref="bookDao"/>        <!-- 针对类的代理,使用CGlib -->        <property name="proxyTargetClass" value="true"/>        <!-- 设置增强 -->        <property name="interceptorNames" value="myPointcutAdvisor"/>    </bean>

第四步:编写测试类
与上面一样
测试结果:
这里写图片描述
只对add方法进行了增强。

3.3、自动代理

在前面的代理中每个代理都要通过ProxyFactoryBean织入切面代理,在时间开发中非常多的Bean每个都配置ProxyFactoryBean开发量可想而知非常大。
自动创建代理其实就是基于后处理Bean。在Bean创建的过程中完成的增强,生成的Bean就是代理。
BeanNameAutoProxyCreator根据Bean名称创建代理。
DeafultAdvisorAutoProxyCreator根据Advisor本身包含信息创建代理。

3.3.1、BeanNameAutoProxyCreator:按名称生成代理

配置applicationContext.xml:

<!-- 定义目标对象 -->    <bean id="bookDao" class="cn.xpu.hcp.impl.BookDaoImpl"/>    <bean id="orderDao" class="cn.xpu.hcp.dao.OrderDao"/>    <!-- 定义增强 -->    <bean id="beforeAdvice" class="cn.xpu.hcp.advice.MyBeforeAdvice"/>    <bean id="aroundAdvice" class="cn.xpu.hcp.advice.MyAroundAdvice"/>    <!-- 自动代理:按名称的代理,基于后处理Bean,后处理Bean不需要配置ID -->    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">        <property name="beanNames" value="*Dao"/>        <property name="interceptorNames" value="aroundAdvice"/>    </bean>

根据名称,只对Dao进行增强,具体使用哪个增强可以很方便的修改。
测试:

@Autowired    @Qualifier("orderDao")    private OrderDao orderDao;    @Autowired    @Qualifier("bookDao")    private BookDao bookDao;    @Test    public void test() {        bookDao.add();        bookDao.delete();        orderDao.add();        orderDao.delete();    }

结果:
这里写图片描述

3.3.2、DefaultAdvisorAutoProxyCreator:根据切面中定义的信息生成代理

配置applicationContext.xml:

<!-- 定义目标对象 -->    <bean id="bookDao" class="cn.xpu.hcp.impl.BookDaoImpl"/>    <bean id="orderDao" class="cn.xpu.hcp.dao.OrderDao"/>    <!-- 定义增强 -->    <bean id="beforeAdvice" class="cn.xpu.hcp.advice.MyBeforeAdvice"/>    <bean id="aroundAdvice" class="cn.xpu.hcp.advice.MyAroundAdvice"/>    <!-- 定义带切点的切面 -->    <bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">        <property name="pattern" value=".*add.*"/>        <property name="advice" ref="beforeAdvice"/>    </bean>    <!-- 自动代理 -->    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">    </bean>

测试代码相同。
测试结果:
这里写图片描述

ProxyFactoryBean与自动代理的区别?
答:ProxyFactoryBean:先有被代理对象,将被代理对象传入到代理类中生成代理。
自动代理基于后处理Bean。在Bean的生成过程中就产生了代理对象,把代理对象返回。生成Bean已经是代理对象。