Spring之AOP框架

AOP称为面向切面编程,其原理来自于代理模式,通过动态代理实现给程序增加新的功能。AOP是一种编程模式,它从另一种角度来思考程序结构并对面向对象编程进行补充。

AOP主要优点是:1)降低模块间的耦合度、2)使系统变得容易扩展、3)更好的代码复用。

AOP的实现技术分为两大类:一是静态织入,引入特定语法在编译期间织入方面代码,如AspectJ,二是动态代理技术,利用拦截消息的方式,在消息进行装饰以取代原有对象的行为。

Spring提供了基于动态AOP机制实现的AOP支持(即第二种方式),通过动态Proxy模式,在目标对象的方法调用前后插入相应的处理代码。

代理模式一般有三个角色:接口、代理和真实对象。其中代理与真实对象实现了同一接口,真实对象作用代理的一个属性,对外发布代理对象。当使用者调用代理的方法时,代理将转而调用真象的方法,在调用前后提供相关服务。

动态代理机制有两种方式:一是JAVA动态代理,特点是只能代理接口,采用JAVA的java.lang.reflection.Proxy来处理。二是CGLIB 代理,可代理接口和类(final method除外),采用CGLIB包来处理。

Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:

(1). Interface InvocationHandler

(2).Proxy:该类即为动态代理类

动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反射功能动态生成的。例:

public interface Target {

    public void say();

}

public class TargetImpl implements Target {

   public TargetImpl () { }

   public void say() {

       System.out.println("Hello World.");

   }

}


import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class TargetProxy implements InvocationHandler {

    private Target sub;

    public TargetProxy() {

    }

    public TargetProxy(Target obj) {

        sub = obj;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("前置代理" + method);

        method.invoke(sub, args);

        System.out.println("后置代理" + method);

        return null;

    }

    public static void main(String[] args) throws Throwable {

        Target targetImpl = new TargetImpl(); //在这里指定被代理类

        InvocationHandler ds = new TargetProxy(targetImpl); //初始化代理类

        Class cls = targetImpl.getClass();

        Target target = (Target) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds);

        target.say();

//        Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces()) ;

//        Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});

//        Target target =( Target) ct.newInstance(new Object[]{ds});

//        target.request();

    }

}

关于cglib的一个简单示例如下:

public class Target1 {

    public void say(){

      System.out.println("hello world");

     }   

}

public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {

        //设置需要创建子类的类

        enhancer.setSuperclass(clazz);

        enhancer.setCallback(this);

        //通过字节码技术动态创建子类实例

        return enhancer.create();

    }

    //实现MethodInterceptor接口方法

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        System.out.println("前置代理" + method);

        //通过代理类调用父类中的方法

        Object result = proxy.invokeSuper(obj, args);

        System.out.println("后置代理"+ method);

        return result;

    }

    public static void main(String[] args) {

        CglibProxy proxy = new CglibProxy();

        //通过生成子类的方式创建代理类

        Target1 proxyImp = (Target1) proxy.getProxy(Target1.class);

        proxyImp.say();

    }

}

以上是关于动态代理技术的两个简单示例,Spring AOP实现了以上两种方式的动态代理技术,参考代码如下:

1.实现接口的类进行AOP,参见org.springframework.aop.framework.JdkDynamicAopProxy,主要方法如下:

public Object getProxy(ClassLoader classLoader) {

    if (logger.isDebugEnabled()) {

        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());

    }

    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);

    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); //生成Proxy对象,this对应InvocationHandler对象,

}

org.springframework.aop.framework.ReflectiveMethodInvocation的主要方法如下:

public Object proceed() throws Throwable {

    //         We start with an index of -1 and increment early.

    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {

        return invokeJoinpoint();

    }

    Object interceptorOrInterceptionAdvice =

                                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {

        // Evaluate dynamic method matcher here: static part will already have

        // been evaluated and found to match.

        InterceptorAndDynamicMethodMatcher dm =

                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;

        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {

            return dm.interceptor.invoke(this);

        }

        else {

            // Dynamic matching failed.

            // Skip this interceptor and invoke the next in the chain.

            return proceed();

        }

    }

    else {

        // It's an interceptor, so we just invoke it: The pointcut will have

        // been evaluated statically before this object was constructed.

        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

    }

}

2.从基类派生出来的类进行AOP,参见org.springframework.aop.framework .Cglib2AopProxy

public Object getProxy(ClassLoader classLoader) {

    if (logger.isDebugEnabled()) {

        logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.getTargetSource());

    }

    try {

        Class rootClass = this.advised.getTargetClass();

        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class proxySuperClass = rootClass;

        if (AopUtils.isCglibProxyClass(rootClass)) {

            proxySuperClass = rootClass.getSuperclass();

            Class[] additionalInterfaces = rootClass.getInterfaces();

            for (Class additionalInterface : additionalInterfaces) {

                this.advised.addInterface(additionalInterface);

            }

        }

        // Validate the class, writing log messages as necessary.

        validateClassIfNecessary(proxySuperClass);

        // Configure CGLIB Enhancer...

        Enhancer enhancer = createEnhancer();

        if (classLoader != null) {

            enhancer.setClassLoader(classLoader);

            if (classLoader instanceof SmartClassLoader &&

                     ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {

                enhancer.setUseCache(false);

            }

        }

        enhancer.setSuperclass(proxySuperClass);

        enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));

        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));

        enhancer.setInterceptDuringConstruction(false);

        Callback[] callbacks = getCallbacks(rootClass);

        enhancer.setCallbacks(callbacks);

        enhancer.setCallbackFilter(new ProxyCallbackFilter(

                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

        Class[] types = new Class[callbacks.length];

        for (int x = 0; x < types.length; x++) {

            types[x] = callbacks[x].getClass();

        }

        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.

        Object proxy;

        if (this.constructorArgs != null) {

            proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);

        }

        else {

            proxy = enhancer.create();

        }

        return proxy;

    }

    catch (CodeGenerationException ex) {

        throw new AopConfigException("Could not generate CGLIB subclass of class [" +

                                this.advised.getTargetClass() + "]: " +

                "Common causes of this problem include using a final class or a non-visible class",

                ex);

    }

    catch (IllegalArgumentException ex) {

        throw new AopConfigException("Could not generate CGLIB subclass of class [" +

                                this.advised.getTargetClass() + "]: " +

                                "Common causes of this problem include using a final class or a non-visible class",

                                ex);

    }

    catch (Exception ex) {

        // TargetSource.getTarget() failed

        throw new AopConfigException("Unexpected AOP exception", ex);

    }

}

Spring AOP框架是Spring的一个重要组成部分,但是Spring IoC容器并不依赖于AOP,所以我们可以在应用中不采用AOP。

AOP中有几个比较重要的概念:

1、join point(连接点):程序运行过程中的某个阶段点,它定义在哪里加入你的逻辑功能,对于Spring AOP,Jointpoint指的就是Method。

2、point cut(切入点):一系列连接点的集合,它指明通知(Advice)将在何时被触发。本质上是一个捕获连接点的结构。

3、advice(通知):在某个连接点所采用的处理逻辑,是point cut的执行代码,是执行“方面”的具体逻辑。

4、aspect(方面):对象操作过程中的截面,实际就是Advice和Pointcut的组合。它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。

5、introduce(引入):用来给一个类型声明额外的方法或属性,为对象引入附加的方法或属性,从而达到修改对象结构的目的。

6、织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时、类加载时或运行时完成。

7、目标对象(Target Object): 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。

8、AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约。

通过切入点匹配连接点的概念是AOP的关键。切入点使得通知可以独立对应到面向对象的层次结构中。

Spring AOP 通知类型

Spring中提供了以下几种Advice:

1、BeforeAdvice:前置通知需实现MethodBeforeAdvice。BeforeAdvice可以修改目标的参数,也可以通过抛出异常来阻止目标运行。
2、AfterreturningAdvice:实现AfterreturningAdvice,我们无法修改方法的返回值,但是可以通过抛出异常阻止方法运行。
3、AroundAdvice:Spring 通过实现MethodInterceptor(aopalliance)来实现包围通知,最大特点是可以修改返回值,当然它在方法前后都加入了自己的逻辑代码,因此功能异常强大。通过MethodInvocation.proceed()来调用目标方法(甚至可以不调用)。
4、ThrowsAdvice:通过实现若干afterThrowing()来实现。
5、IntroductionInterceptor:Spring 的默认实现为DelegatingIntroductionInterceptor

Spring AOP机制提供两类方式实现类代理。一种是单个代理,一种是自动代理。自动代理又提供XML和注解两种配置方式

     单个代理通过ProxyFactoryBean来实现,该方法只能为单个类配置代理。

     自动代理通过BeanNameAutoProxyCreator或者 DefaultAdvisorAutoProxyCreator实现,它会自动为所有的增强所匹配的bean创建相应的代理。

      以上两种方法可以只选用其中的一个来简化配置。

Spring AOP编程式框架

Spring AOP框架中的编程过程:

1.声明目标对象接口,

2.实现目录对象

3.实现增强/通知/

4.实现切入点

5.使用代理机制

代码示例如下:

//目标对象接口. 

public interface Target { 

    public String play(int arg); 

}

//目标对象实现. 

public class TargetImpl implements Target { 

    public String play(int arg) { 

         System.out.println("play method...."); 

        return "[Target:]" + arg; 

    } 

//前置增强 

public class MyBeforeAdvice implements MethodBeforeAdvice { 

    public void before(Method method, Object[] args, Object target)  throws Throwable { 

         System.out.println(method.getName()); 

         System.out.println("before method!"); 

    } 

//后置增强 

public class MyAfterAdvice implements AfterReturningAdvice { 

    public void afterReturning(Object returnValue, Method method, 

            Object[] args, Object target) throws Throwable { 

         System.out.println(returnValue + ":after method");  

    } 

public class Main { 

    public static void main(String[] args) { 

         Target target = new TargetImpl();   //目标对象 

         Advice beforeAdvice = new MyBeforeAdvice(); //增强 

         Pointcut pointcut = new MyPointcut(); //切入点 

         DefaultPointcutAdvisor dda = new DefaultPointcutAdvisor(); //切面 

         dda.setAdvice(beforeAdvice);  //加入增强

         dda.setPointcut(pointcut);   //加入切入点

         

         //1.  基本编程方式

         AdvisedSupport advisedSupport = new AdvisedSupport();

         advisedSupport.addAdvisor(dda);    //加入切面

         advisedSupport.addAdvice(new MyAfterAdvice());  //加入增强

         advisedSupport.addInterface(Target.class); 

         advisedSupport.setTarget(target);   //加入目标代码

         AopProxy aopProxy = new DefaultAopProxyFactory().createAopProxy(advisedSupport); 

         Target proxy = (Target)aopProxy.getProxy(); 

         System.out.println(proxy.play(200)); 

        

         //2.  提供便利的编程方式.   

         ProxyFactory proxyFactory = new ProxyFactory(); 

         proxyFactory.addAdvisor(dda);  //加入切面

         proxyFactory.addInterface(Target.class); 

         proxyFactory.setTarget(target);  //加入目标代码

         Target proxy2 = (Target)proxyFactory.getProxy(); 

         System.out.println(proxy2.play(201)); 

          

         //3.  提供便利的编程方式.   

         ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

         proxyFactoryBean.addAdvisor(dda);  //加入切面

         proxyFactoryBean.addInterface(Target.class); 

         proxyFactoryBean.setTarget(target);  //加入目标代码

         Target proxy3 = (Target)proxyFactoryBean.getObject(); 

         System.out.println(proxy3.play(232)); 

    } 

}

Spring AOP配置总结

 1. XML配置方式

     BeanNameAutoProxyCreator:  BeanNameAutoProxyCreator将为名字匹配字符串或者通配符的bean自动创建AOP代理。通常接受两个参数。第一个是beanNames属性,该属性用来设置哪些bean需要自动生成代理。另一个属性是interceptorNames,该属性指定了事务拦截器,当自动创建事务代理时,系统会根据这些事务拦截器的属性来生成对应的事务代理。

<bean id=" WelcomeInterceptor " class=" AutoProxySample.WelcomeInterceptor "/>

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

  <!-- 指定对满足哪些bean name的bean自动生成业务代理 -->

  <property name="beanNames">

    <!-- 下面是所有需要创建自动代理的bean-->

    <list>

      <value> personABean </value>

      <value> personBBean </value>

    </list>

    <!-- 此处可增加其他需要创建自动代理的bean-->

  </property>

  <!-- 下面定义BeanNameAutoProxyCreator所需的拦截器-->

  <property name="interceptorNames">

    <list>

      <value> WelcomeInterceptor </value>

      <!-- 此处可增加其他新的Interceptor -->

    </list>

  </property>

</bean>

<!--由BeanNameAutoProxyCreator生成自动代理-->

<bean id="personABean" class="AutoProxySample.PersonA">

</bean>

<bean id="personBBean" class="AutoProxySample.PersonB">  <!-- 与PersonA实现相同接口的业务实现类 -->

DefaultAdvisorAutoProxyCreator:DefaultAdvisorAutoProxyCreator这个类功能更为强大,实现了BeanProcessor接口,它将描述ApplicationCentext读取到的所有Bean的信息,寻找所有的Advistor,并将这些Advisor应用到所有符合切入点的Bean中。(一个Advisor是一个切入点和一个通知的组成)

<bean id="WelcomeAdvice" class="AutoProxySample.WelcomeAdvice"></bean>

<!-- 自动代理所有的advisor -->

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

</bean>

<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

     <property name="pattern">

       <value>.*say.+</value>  <!-- 业务实现方法名匹配 -->

     </property>

     <property name="advice">

       <ref bean="WelcomeAdvice"/> <!-- 实现前置通知接口的类-->

     </property>

</bean>

  

<bean id="personABean" class="AutoProxySample.PersonA">       <!-- 业务实现类 -->

</bean>

<bean id="personBBean" class="AutoProxySample.PersonB">  <!-- 与PersonA实现相同接口的业务实现类 -->

</bean>

相关的Java代码如下:

package AutoProxySample;

public interface Person {

  public void say();

  public void hear();

}

package AutoProxySample;

public class PersonA implements Person {

    public String say() {

        System.out.println(this.getName()+" said: Hello Word!");

        return null;

    }

   

    public String hear(String s) {

        System.out.println(this.getName()+" hear: "+s);

        return null;

    }

}

package AutoProxySample;

public class PersonB implements Person {

    public String say() {

        System.out.println(this.getName()+" said: Hello Word!");

        return null;

    }

    public String hear(String s) {

        System.out.println(this.getName()+" hear: "+s);

        return null;

    }

}

package AutoProxySample;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

//前置通知

public class WelcomeAdvice implements MethodBeforeAdvice {

    public void before(Method method, Object[] args, Object obj)

            throws Throwable {

        System.out.println("Hello welcome to bye ");

    }

}

2。注解方式配置

 针对一个接口和接口的实现类,这里使用AutoProxySample.Person和AutoProxySample.PersonA两个类,使用Spring注解方式对PersonA进行方法拦截。

定义一个切面,对PersonA进行方法拦截,如下:

package AutoProxySample;


@Aspect
public class AspectAdvice {

 /**
  * 指定切入点匹配表达式,注意它是以方法的形式进行声明的。

  */
    @Pointcut("execution(*AutoProxySample.PersonA.*(..))")
    public void anyMethod() {
    }

  /**
     * 前置通知
     */
    @Before("anyMethod() && args(name)")
    public void doBefore(String name) {
       System.out.println(name); 

       System.out.println("前置通知"); 
    }

    /**
     * 后置通知
     */
    @AfterReturning("anyMethod()")
    public void doAfter() {
       System.out.println("前置通知"); 
    }

    /**
     * 环绕通知
     */
    @Around(“anyMethod”)
    public void doAround(ProceedingJoinPoint pjp) throws Throwable {
       System.out.println("进入环绕通知"); 

       Object object = pjp.proceed();//执行该方法

       System.out.println("退出方法");

        return object; 

    }

    /**
     * 异常通知
     */
    @AfterThrowing(“anyMethod”)

    public void doThrow(JoinPoint jp, Throwable e) {
        System.out.println("例外通知");
    }

}

然后在Spring的配置文件中配置该Bean,需要打开AOP命名空间

<aop:aspectj-autoproxy/> 

<bean id="personABean" class="AutoProxySample.PersonA"/>      

<bean id="myProxy" class="AutoProxySample.AspectAdvice"/>   

原文地址:https://www.cnblogs.com/jevo/p/2966993.html