• 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"/>   

  • 相关阅读:
    Java反射机制源码分析及知识点总结
    Dubbo admin 在Windows下的安装和服务发现
    Redis知识点总结
    Error:(xx) java: -source 1.5 中不支持
    Java中的线程间通信
    linux主机名显示bogon问题
    Linux(CentOS)上安装Apache Hadoop
    Java虚拟机(JVM)及其体系结构
    在微服务领域中处理分布式事务
    Redis持久化
  • 原文地址:https://www.cnblogs.com/jevo/p/2966993.html
Copyright © 2020-2023  润新知