聊聊aop和它的动态代理

什么是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通过继承⽗类创建的代理类

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值