6、spring 入门—Spring AOP API介绍

本文深入探讨Spring AOP的基本概念和技术细节,包括Pointcut、Advice、ProxyFactoryBean等核心组件的作用及配置方法,并提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@Spring AOP API:
1、这是Spring1.2历史用法,现在(V4.0)仍然支持;
2、这是SpringAOP基础,不得不了解;

3、现在的用法也是基于历史的,只是更简便了。

Pointcut:
实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
成员变量:mappedNames,匹配的方法名集合


配置文件中<list></list>表明:当前这个property是一个集合,list里面可以写<value>

pointcut的实现方法之一:
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut"> 
<property name="mappedNames">
<list> 
<value>sa*</value>
</list>
</property> 
</bean>

Before advice
.一个简单的通知类型
.只是在进入方法之前被调用,不需要MethodInvocation对象
.前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值

Throws advice
.如果连接点抛出异常,throws advice在连接点返回后被调用
.如果throws-advice的方法抛出异常,那么它将覆盖原有异常
.接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法:
.void afterThrowing([Method,args,target],ThrowableSubclass);

public void afterThrowing(Exception ex);
.public void afterThrowing(RemoteException ex);
.public void afterThrowing(Method method,Object[] args,Object target,Exception ex);
.public void afterThrowing(Method method,Object[] args,Object target,ServletException ex);

After Returning advice
.后置通知必须实现
 org.springframework.aop.AfterReturningAdvice接口
.可以访问返回(但不能进行修改)、被调用的方法、方法的参数和目标
.如果抛出异常,将会抛出拦截器链,替代返回值

Interception around advice
.Srping 的切入点模型使得切入点可以独立于advice重用,以针对不同的advice可以使用相同的切入点
public interface MethodInterceptor extends Interceptor {


    Object invoke(MethodInvocation invocation) throws Throwable;
}


public class DebugInterceptor implements MethodInterceptor {


    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]");
        Object rval = invocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

Introduction advice
.Srping把引入通知作为一种特殊的拦截通知
.需要IntroductionAdvisor和IntroductionInterceptor
.仅适用于类,不能和任何切入点一起使用
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {


    ClassFilter getClassFilter();


    void validateInterfaces() throws IllegalArgumentException;
}


public interface IntroductionInfo {


    Class[] getInterfaces();
}

Introduction advice


public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {


    private boolean locked;


    public void lock() {
        this.locked = true;
    }


    public void unlock() {
        this.locked = false;
    }


    public boolean locked() {
        return this.locked;
    }


    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
            throw new LockedException();
        }
        return super.invoke(invocation);
    }


}

Introduction advice
.一个Spring test sute的例子
.如果调用lock()方法,希望所有的setter方法抛出
    LockedException异常(如果物体不可变,AOP典型例子)
.需要一个完成繁重任务的IntroductionInterceptor,这种情况下,可以使用org.springframework.aop.support.DelegatingIntroductionInterceptor


public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}Introduction advice
.Introduction advisor比较简单,持有独立的LockMixin实例


public class LockMixinAdvisor extends DefaultIntroductionAdvisor {


    public LockMixinAdvisor() {
        super(new LockMixin(), Lockable.class);
    }
}

Advisor API in Spring
.Advisor是仅包含一个切入点表达式关联的单个通知的方面
.处理introductions,advisor可以用于任何通知
.org.springframework.aop.support.DefaultIntroductionAdvisor是最常用的advisor类,它可以与MethodInceptor,BeforeAdvice或者ThrowsAdvice一起使用
.它可以混合在Spring同一个AOP代理的advisor和advice

ProxyFactoryBean<br>
.使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的最重要的好处是通知和切入点也可以由IoC来管理
.被代理的类没有任何接口,使用CGLIB代理,否则JDK代理
.通过proxyTargetClass为true,可强制使用CGLIB 
.如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
.如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建.

传统AOP的实现中,需要在XML文档中手动引入ProxyFactoryBean.
通过getBean()方法获取该bean对应的id不是ProxyFactoryBean的实例本身,而是这个类的getObject()方法创建的对象.
getObject()方法将创建一个AOP代理的目标对象.


 <aop:aspectj-autoproxy  ></aop:aspectj>标签中与ProxyFactoryBean中有proxyTargetClass属性,当该属性设置为true时,会强制使用CGLIB动态代理,默认为false.


如果ProxyFactoryBean的proxyInterfacs属性没有被设置,但是目标类实现了一个(或者更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理.


在Spring实现AOP的方式中,不需要引入ProxyBeanFactory类了,直接使用<aop:config>标签与注解来进行配置即可.


注意:在定义Person的bean时,class为ProxyFactoryBean而不是Person类
personbean中实现的代理接口为com.mycompany.Persion
 target属性的引用值为personTarget对象,就是PersonImpl类的实例对象
 当使用person对象赋值给peisonUserbean的person属性时,实际上是使用target(PersonImpl的实例对象)去赋值

//相关代码——(上):
//package com.imooc.aop.api;
@BizLogic.java
public interface BizLogic {
String save();
}


@BizLogicImpl.java
public class BizLogicImpl implements BizLogic {
public String save() {
System.out.println("BizLogicImpl:BizLogicImpl save.");
return "BizLogicImpl save.";
// throw new RuntimeException();
}
}


@MoocAfterReturningAdvice.java
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("MoocAfterReturningAdvice:"+method.getName()+"  "+target.getClass().getName()+"  "+returnValue);
}
}


@MoocBeforeAdvice.java
public class MoocBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("MoocBeforeAdvice:"+method.getName()+"  "+target.getClass().getName());
}
}

//相关代码——(中上):
//package com.imooc.aop.api;
@MoocMethodInterceptor.java
public class MoocMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("MoocMethodInterceptor 1:"+invocation.getMethod().getName()+"  "+invocation.getStaticPart().getClass().getName());
Object obj=invocation.proceed();
System.out.println("MoocMethodInterceptor 2:"+obj);
return obj;
}
}


@MoocThrowsAdvice.java
public class MoocThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex) throws Throwable{
System.out.println("MoocThrowsAdvice afterThrowing 1");
}
public void afterThrowing(Method method,Object[] args,Object target,Exception ex) throws Throwable{
System.out.println("MoocThrowsAdvice afterThrowing 2:"+method.getName()+"  "+target.getClass().getName());
}
}


//相关代码——(中下):
@spring-aop-api.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans aop...>


<bean id="moocBeforeAdvice" class="com.imooc.aop.api.MoocBeforeAdvice"></bean>
<bean id="moocAfterReturningAdvice" class="com.imooc.aop.api.MoocAfterReturningAdvice"></bean>
<bean id="moocMethodInterceptor" class="com.imooc.aop.api.MoocMethodInterceptor"></bean>
<bean id="moocThrowsAdvice" class="com.imooc.aop.api.MoocThrowsAdvice"></bean>
<!-- <bean id="bizLogicImplTarget" class="com.imooc.aop.api.BizLogicImpl"></bean> -->

<!-- <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>匹配sa开头的方法,如果匹配不到,则pointcutBean切入失败,则moocBeforeAdvice也会失效。
</list>
</property>
</bean>

<bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="moocBeforeAdvice" ></property>
<property name="pointcut" ref="pointcutBean" ></property>
</bean>

//相关代码——(下上):
@spring-aop-api.xml
注意:这个getBean("bizLogicImpl")返回的对象并不是ProxyFactoryBean,而是property name="target"的ref bean。
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="bizLogicImplTarget"></ref>
</property>
<property name="interceptorNames">
<list>
注意顺序,如果独立,则按照顺序输出;如果不独立,有关联,则根据关联的先后顺序分别执行。
重点注意:如果 around 加上去,则 after 一定在 afterrunning 后面,如果 around 不加上去,根据 after 和 afterrunning 的配置先后顺序来决定输出顺序。
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>defaultAdvisor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean> -->

//输出:
MoocMethodInterceptor 1:save  java.lang.reflect.Method
MoocBeforeAdvice:save  com.imooc.aop.api.BizLogicImpl
BizLogicImpl:BizLogicImpl save.
MoocMethodInterceptor 2:BizLogicImpl save.
MoocAfterReturningAdvice:save  com.imooc.aop.api.BizLogicImpl  BizLogicImpl save.


//输出(new RuntimeException()):
MoocMethodInterceptor 1:save  java.lang.reflect.Method
MoocBeforeAdvice:save  com.imooc.aop.api.BizLogicImpl
BizLogicImpl:BizLogicImpl save.
MoocThrowsAdvice afterThrowing 2:save  com.imooc.aop.api.BizLogicImpl


//输出(匹配不到 sa* 少了 Before Advice):
MoocMethodInterceptor 1:save  java.lang.reflect.Method
BizLogicImpl:BizLogicImpl save.
MoocMethodInterceptor 2:BizLogicImpl save.
MoocAfterReturningAdvice:save  com.imooc.aop.api.BizLogicImpl  BizLogicImpl save.

//输出(方法一):
MoocMethodInterceptor 1:save  java.lang.reflect.Method
MoocBeforeAdvice:save  com.imooc.aop.api.BizLogicImpl
BizLogicImpl:BizLogicImpl save.
MoocMethodInterceptor 2:BizLogicImpl save.
MoocAfterReturningAdvice:save  com.imooc.aop.api.BizLogicImpl  BizLogicImpl save.


//输出(方法二):
MoocMethodInterceptor 1:save  java.lang.reflect.Method
MoocBeforeAdvice:save  com.imooc.aop.api.BizLogicImpl
BizLogicImpl:BizLogicImpl save.
MoocMethodInterceptor 2:BizLogicImpl save.
MoocAfterReturningAdvice:save  com.imooc.aop.api.BizLogicImpl  BizLogicImpl save.

//相关代码——(下下):
@spring-aop-api.xml
<bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.imooc.aop.api.BizLogic</value>
</property>
<property name="target">
<!-- <bean class="com.imooc.aop.api.BizLogicImpl" ></bean> 方法一(推荐):经过代理-->
<!-- <ref bean="bizLogicImplTarget"></ref> 方法二:通过id获取,没有经过代理,获取原始的BizLogicImpl-->
</property>
<property name="interceptorNames">
<list>
<value>moocAfterReturningAdvice</value>
<!-- <value>moocMethodInterceptor</value> -->
<value>mooc*</value><!-- 通配符的方式,注意:只能适用于拥有拦截器接口,不适用于Advice -->
<value>moocBeforeAdvice</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
</beans>


@TestAOPAPI.java
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPAPI extends UnitTestBase{
public TestAOPAPI(){
super("classpath:spring-aop-api.xml");
}
@Test
public void testSave(){
BizLogic logic=super.getBean("bizLogicImpl");
logic.save();
}
}


@UnitTestBase.java
//省略代码...

注意:

person这个bean,当调用get方法的时候不是调用getPerson,而是ProxyFactoryBean的getObject方法,getObject方法返回的是personImpl这个类的对象。

要想获得BizLogic接口的实现类BizLogicImplTaget,不直接通过该实现类的bean id 利用getBean方法去获得,而是通过代理工厂的getObject方法获得。spring不用自己创建代理工厂类,而是为proxyBean用XML写一个bean,id为BizLogicImpl,target为BizLogicImplTarget。这样通过getBean方法调用id为BizLogicImpl的bean,实际上就是调用了BizLogicImplTarget这个实现了BizLogic接口的实现类。
所以proxyBean相当于以前写的factory类,BizLogicImpl的bean相当于以前写的factory类里的getObject方法。

MethodInterceptor就是around advice

pointCutBean的属性mappedNames是一个list,list里的值都是名称已sa开头的方法名(比如例子的save()方法)。表示把所有名称以sa开头的方法都作为切入点放到mappedNames这个list中。
指向spring框架的defaultpointcutAdvisor的id为defaultAdvisor 的bean中有名为pointCut 的属性ref 的是pointCutBean这个bean。

通过ProxyBean的类型为list的interceptorNames属性为target的BizLogicImpl添加before、afterRunning 等通知。

如果没有为proxyBean定义proxyInterfaces的class,则有proxyBean自己去发现是否有实现接口。而有定义proxyInterfaces的class,表示target指向的实现类实现了proxyInterfaces的class所代表的接口。

为target指定类的时候不用ref 而是在target这个property里定义一个bean 改bean指向target要指定的类,这样阻止直接访问到被指定的那个类。


Proxying classes
.前面的例子中如果没有Person接口,这种情况下Spring会使用CGLIB代理,而不是JDK动态代理
.如果想,可以强制在任何情况下使用CGLIB,即使有接口
.CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方调用到原来的目标
.子类使用来实现Decorator模式,织入通知

在Spring的AOP当中:
若目标实体类是没有实现接口的,那么这种情况下Spring会使用CGLIB来进行代理,当有实现某个接口,那么便会采用JDK动态代理.


也可以在<aop:aspectj-autoproxy  proxy-target-class="true">标签中把proxy-target-class属性设置为true,来强制在任何情况下使用CGLIB的方式,即使目标类有实现某个接口.


CGLIB的代理对用户是透明的,需要注意的是:


-final方法不能被通知,因为它们不能被覆盖


-不用把CGLIB的jar包添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR包中(即不用手动引入CGLIB包,通过CGLIB实现动态代理的代码实现,Spring都处理好了,即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”)

使用global advisors
.用*做通配符,匹配所有拦截器加入通知链

简化的proxy定义
.使用父子bean定义,以及内部bean定义,可能会带来更清洁和更简洁的代理定义(抽象属性标记父子bean定义为抽象的这样它不能被实例化)

使用ProxyFactory


.使用Spring AOP而不必依赖于Spring IoC
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
.大多数情况下最佳时间是用IoC容器创建AOP代理
.虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现

使用“auto-proxy”
.Spring也允许使用“自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的“bean post processor”功能基础上的(在加载bean的时候就可以修改)
.BeanNameAutoProxyCreator


<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="jdk*,onlyJdk"/>
    <property name="interceptorNames">
        <list>
            <value>myInterceptor</value>
        </list>
    </property>
</bean>

使用“auto-proxy”
.DefaultAdvisorAutoProxyCreator,当前IoC容器中自动应用,不用显示声明应用advisor的bean定义


<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>


<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
    <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>


<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>


<bean id="businessObject1" class="com.mycompany.BusinessObject1">
    <!-- Properties omitted -->
</bean>


<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>

Spring AOP API小结
.Pointcut
.Advice
.ProxyFactoryBean


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值