Spring之AOP框架

本文内容纲要: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"/>   

本文内容总结:Spring之AOP框架

原文链接:https://www.cnblogs.com/jevo/archive/2013/03/21/2966993.html

以上是 Spring之AOP框架 的全部内容, 来源链接: utcz.com/z/296665.html

回到顶部