欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

聊聊aop和它的动态代理

程序员文章站 2022-04-15 23:21:27
aop: 面向切面编程 以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建PointCut(切入点) 切入点:即确定添加额外功能的具体位置。 我们一般通过切入点函数来实现对切入点的表示 ,切入点函数分为四种 :* execution:最强大的切入点函数 可以表示 :⽅法切⼊点表达式、 类切⼊点表达式 、包切⼊点表达 常见用法 execution(* com.aptech.jb.epet.dao.hibimpl.*.......

aop: 面向切面编程 

    以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建 

PointCut(切入点)

    切入点:即确定添加额外功能的具体位置。

    我们一般通过切入点函数来实现对切入点的表示 ,切入点函数分为四种 :

*  execution:最强大的切入点函数 可以表示 :⽅法切⼊点表达式、 类切⼊点表达式 、包切⼊点表达
       常见用法   execution(* com.aptech.jb.epet.dao.hibimpl.*.*(..))   
       com.aptech.jb.epet.dao.hibimpl 包下所有的类的所有方法。。
       第一个*代表所有的返回值类型
       第二个*代表所有的类
       第三个*代表类所有方法 最后一个..代表所有的参数。
(大致了解即可  execution啥都能做)
*  args    : 主要⽤于函数(⽅法) 参数的匹配
*  within :  主要⽤于进⾏类、包切⼊点表达式的匹配
*  @annotation: 为具有特殊注解的⽅法加⼊额外功能

Advice (通知)  

常见的通知接口:

* MethodBeforeAdvice  前置通知   
* AfterReturningAdvice  后置通知
* ThrowsAdvice        异常通知
* Methodinterceptor   环绕通知  

如何理解切面  ?

   切面=切入点+额外功能 

   即  切面 =pointcut+ advice 

Spring如何配置切面?

    配置spring的切面存在可以通过  aspect 或者 adviser 来整合配置切面 

applicatioonContext.xml(adviser配置)

<!-- 注入通知类对象 -->
<bean id="before" class="domo07aop.server.Before"/>
<aop:config>
    <aop:pointcut id="pc" expression="execution(* domo07aop.server.serverimpl.*(..))"/>     切入点
    <aop:advisor advice-ref="before" pointcut-ref="pc"/>     通知器  
</aop:config>

applicatioonContext.xml(aspect 配置)

<aop:config>
    <aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
    <aop:aspect ref="sleepHelperAspect">
        <aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/>
        <aop:after method="afterSleep" pointcut-ref="sleepPointcut"/>
    </aop:aspect>
</aop:config>

aspect 和adviser区别

<aop:advisor>大多用于事务管理。      
<aop:aspect>大多用于日志、缓存。
<aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的通知时,通知必须实现Advice接口。   

关于动态代理

    aop的底层实现 : aop是基于动态字节码技术和反射,通过动态代理实现了spring的aop功能

    动态代理分为 : JDK动态代理   cglib动态代理

AOP的动态代理   (代码实现)

    动态代理实现需要: 目标对象    (JDK动态代理实现需要相应的接口 ,cglib动态代理通过继承实现代理)

*目标对象的接口

package 动态代理;
/**
* 人的接口类  (程序员 )
* @author Administrator
*
*/
public interface Person {
    void say();
    
    void eat();
    
    void learn();
    
    void deBug();
}

*目标对象类

public class PersonImpl  implements Person{
    
    @Override
    public void say() {
        System.out.println("你好,我是初级程序员,请多指教!");
        
    }
    @Override
    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("我有点不想吃饭了,减肥");
    }
    @Override
    public void learn() {
        // TODO Auto-generated method stub
        System.out.println("奥里给  学无止境");
    }
    @Override
    public void deBug() {
        // TODO Auto-generated method stub
        System.out.println("基操勿6");
        
    }
}

jdk动态代理:

(jdk代理基于接口实现,动态代理类对象通过方法创建)

package 动态代理;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;


import org.junit.Test;


/**
* 基于jdk的动态代理  
* @author Administrator
*
*/
public class JdkProxy {
    @Test
    public void testJdkProxy()
    {
        // 目标对象   (被代理对象 )  
        Person per=new PersonImpl();
        
        // 对代理类对象进行增强  通过内部类的方式 简化开发    invocation 类似 spring 中的环绕通知
        InvocationHandler invocation=new InvocationHandler()
        {


            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(new Date());
                Object ret=method.invoke(per, args);
                
                return ret;
            }
        };
        
        // 代理类和被代理类实现了相同的接口 ,通过接口来接收代理类对象     
        // 类加载器的作用  : 1 : 把字节码文件加载到JVM虚拟机中  
        //    2:在虚拟机中,为字节码创建对象  
        // ClassLoader:动态代理通过动态字节码文件技术把 字节码文件直接
        //写入了JVM虚拟机,但是JVM中对象的创建仍然需要类加载器,所以在创
        //建动态代理对象时,我们需要传入一个类加载器帮助代理对象初始化
        // interfaces: 传入 被代理对象的接口  使代理对象和被代理对象实现相同的接口
        // invocationHandler: 对被代理对象的方法实现增强的方法  
        Person perproxy=(Person)Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), 
       per.getClass().getInterfaces(),invocation);
        
        per.eat();
        perproxy.eat();
    }
    
    
    
}

cglib动态代理

(cglib实现动态代理基于继承,代理类继承被代理类 )(cglib如果不用spring手写实现 需要导入jar cglib-nodep-3.1.jar 

package 动态代理;
import java.lang.reflect.Method;
import org.junit.Test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 基于  Cglib的动态代理
* @author Administrator
*
*/
public class CglibProxy {
    
    @Test
    public void test1()
    {
        Person per=new PersonImpl();
        Enhancer enhance=new Enhancer();
        
        //  内部类  
        MethodInterceptor methodinter=new  MethodInterceptor()
        {
            @Override
                public Object intercept(Object  arg0, Method arg1, Object[] arg2, MethodProxy  arg3)
                throws Throwable {
                System.out.println("还活着   Log.......");
                Object ret=arg1.invoke(per,  arg2);
                return ret;
            }
            
        };
        //类加载器  classLoader 
        enhance.setClassLoader(CglibProxy.class.getClassLoader());
        //  被修饰类的字节码 (jdk传的是接口)
        enhance.setSuperclass(per.getClass());
        // 增强的方法
        enhance.setCallback(methodinter);
        
        PersonImpl  perProxy=(PersonImpl)enhance.create();
        
        perProxy.eat();
        
        
        
        
    }
    
    
    
}

JDK 动态代理 和 cglib 动态代理 的异同

1: JDK通过Proxy.newInstance(classLoader,interfaces,InvocationHandler) 实现生产代理对象; 而 cglib通过 net.sf.cglib.proxy.Enhancer.create() 创建代理对象,在创建代理对象前,也需要设置
enhance.setClassLoader(), enhance.setSuperclass(),enhance.setCallback()方法设置属性;  两个方法除了一个传的是接口,一个传的是类的字节码,别的没有很多差别。

2: Proxy.newProxyInstance() 通过接⼝创建代理的实现类; Cglib动态代理 Enhancer通过继承⽗类创建的代理类

 

本文地址:https://blog.csdn.net/attentiony/article/details/107369939