当前位置:首页 > java > 正文内容

Spring AOP的JDK动态代理和CGLIB代理 的原理

关中浪子3年前 (2022-02-23)java1506
买泛域名SSL证书 送5斤装现摘猕猴桃一箱、同时提供技开源商城搭建免费技术支持。
泛域名ssl证书 239元1年送1个月、单域名39元1年,Sectigo(原Comodo证书)全球可信证书,强大的兼容性,高度安全性,如有问题7天内可退、可开发票
加微信VX 18718058521 备注SSL证书
【腾讯云】2核2G4M云服务器新老同享99元/年,续费同价

一.AOP的概念

         在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    二.主要用途

        将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

    三.代理

         AOP的实现关键在于AOP框架自动创建的AOP代理。AOP代理主要分为两大类:

         1.静态代理:使用AOP框架提供的命令进行编译,从而在编译阶段就可以生成AOP代理类,因此也称为编译时增强;静态代理一Aspectj为代表。

         2.动态代理:在运行时借助于JDK动态代理,CGLIB等在内存中临时生成AOP动态代理类,因此也被称为运行时增强,Spring AOP用的就是动态代理。

         那么其实在Spring框架中用到就是JDK动态代理或者是CGLIB代理。

     四.JDK动态代理

        JDK动态代理用到了一个类Proxy,下面举个例子来说明一下,Proxy这个类是如何实现JDK的动态代理的。

          有一个需求就是,当用户名为空的时候,不能调用业务方法,当用户名不为空是可以调用业务方法。

          1.新建一个业务接口,并书写业务方法。

           

复制代码
package cn.service;public interface DoService {  
  //添加    public void  add();   
   //查询    public void selectInfo();    
   //更新    public void update();
}

 

          2.新建一个业务接口实现类

          

复制代码
package cn.service.impl;
import cn.service.DoService;
public class DoServiceImpl implements DoService{
    //用户名    
    private String userName;
    
    @Override    public void add() {
        System.out.println("添加的方法");
        
    }

    @Override    public void selectInfo() {
        System.out.println("查询的方法");
        
    }

    @Override    public void update() {
        System.out.println("更新的方法");
    }    
    public String getUserName() {
            return userName;
    }    
    public void setUserName(String userName) {
            this.userName = userName;
    }
    

}

           一般如果我们需要进行这样一个业务需求我们会相到 一个办法就是在所有的方法中进行一个判断就是判读用户名是否为空,显然这样很麻烦,所有我们用到了JDK动态代理,主函数 -->代理-->目标对象的方法。这样,以后不管是修改判断条件,还是查找等,可以直接在代理类中进行处理。

 

           

 

          3.创建代理对象工厂类

         

复制代码
package cn.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.service.impl.DoServiceImpl;
/**
 * 代理工厂类
 * @author hyj
 * */public class JDKProxyFactory implements InvocationHandler{
     //要返回的代理对象   private Object obj;   /**
    * 通过代理工厂的方式来创建代理类
    * @param obj  要代理的类的对象
    * @return    */  
     public Object createProxyIntance(Object obj){
            this.obj=obj;       
            //第一个参数是目标对象的类加载器       
            //第二个参数是目标对象实现的接口       
            //第三个参数传入一个InvocationHandler实例,该参数和回调有关系。       
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);
       
   }
   @Override    
   public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {
       Object proxyObject=null;
       DoServiceImpl ds=(DoServiceImpl)obj;       
       if(ds.getUserName()!=null){
           proxyObject= method.invoke(ds, args);
       }else{
           System.out.println("用户名为空,已拦截");
       }        
       return proxyObject;
    }    
    public Object getObj() {
            return obj;
    }    
    public void setObj(Object obj) {        
        this.obj = obj;
    }
    
   
}

            4.测试类

     

复制代码
package cn.test;
import cn.aop.JDKProxyFactory;
import cn.service.DoService;
import cn.service.impl.DoServiceImpl;
public class TestHappy {   
    public static void main(String[] args) {
      JDKProxyFactory jpf=new JDKProxyFactory();
      DoService ds=(DoService)jpf.createProxyIntance(new DoServiceImpl("fsf"));
      ds.selectInfo();
   }
}

     步骤:

  • 首先调用代理工厂的createProxyIntance(Object obj)创建DoServiceImpl类的代理类.

  • 在该方法内,调用Proxy.newProxyInstance()方法创建代理对象。第一个参数是目标对象的类加载器,第二个参数是目标对象实现的接口,第三个参数传入一个InvocationHandler实例,该参数和回调有关系。

  • 每当调用目标对象的方法的时候,就会回调该InvocationHandler实例的方法,也就是public Object invoke()方法,我们就可以把限制的条件放在这里,条件符合的时候,就可以调用method.invoke()方法真正的调用目标对象的方法,否则,则可以在这里过滤掉不符合条件的调用。

        五.CGLIB代理

       在这一步中,我们使用一个Enhancer类来创建代理对象,不再使用Proxy。使用Enhancer类,需要为其实例指定一个父类,也就是我们 的目标对象。这样,我们新创建出来的对象就是目标对象的子类,有目标对象的一样。除此之外,还要指定一个回调函数,这个函数就和Proxy的 invoke()类似。

总体来说,使用CGlib的方法和使用Proxy的方法差不多,只是Proxy创建出来的代理对象和目标对象都实现了同一个接口。而CGlib的方法则是直接继承了目标对象。

1.引入jar包

 

 

2.创建CGLIB实现代理的类

复制代码
package cn.aop;
import java.lang.reflect.Method;
import cn.service.impl.DoServiceImpl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * CGLIB实现代理
 * @author hyj
 * */
 public class CGLIBProxyFactory implements MethodInterceptor {
     //要放回的代理对象     
     private Object obj;     
     public Object createProxy(Object obj){         
     //把传进来的代理对象赋值给obj         
         this.obj=obj;
         Enhancer enhancer = new Enhancer();         //需要为其实例指定一个父类,也就是我们 的目标对象,那么我们新创建出来的对象就是目标对象的子类,有目标对象的一样
         enhancer.setSuperclass(this.obj.getClass());         //除此之外,还要指定一个回调函数,这个函数就和Proxy的 invoke()类似
         enhancer.setCallback(this);    
         return enhancer.create();   
         
     }
    @Override    
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
          Object proxyObject=null;
           DoServiceImpl ds=(DoServiceImpl)obj;
            if(ds.getUserName()!=null){
               proxyObject= methodProxy.invoke(ds, args);
           }else{
               System.out.println("用户名为空,已拦截");
           }            
           return proxyObject;
    }
    
}

        测试类 

   

复制代码
 @Test   
 public void CGLIBProxyTest(){
       CGLIBProxyFactory gb=new CGLIBProxyFactory();
       DoServiceImpl ds= (DoServiceImpl)gb.createProxy(new DoServiceImpl("sf"));
       ds.add();
   }
复制代码


找梯子最重要的就是稳定,这个已经上线三年了,一直稳定没有被封过,赶紧下载备用吧!

扫描二维码推送至手机访问。

版权声明:本文由码农翻生发布,如需转载请注明出处。

本文链接:https://lubojian.cn/post/119.html

分享给朋友:

相关文章

Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoo 错误解决

Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig at java.base/jdk.inte...

静态代理和动态代理的区别

静态代理静态代理,设计模式的代理模式举例通常是用这种方式实现的,对于每一个要代理的类为了添加相同的操作,需要分别去实现其接口,容易造成代理类过多public interface Subject {  ...

Spring Boot + MyBatis + MySQL 实现读写分离!不懂多看

Spring Boot + MyBatis + MySQL 实现读写分离!不懂多看

1、引言读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做。因此,一般来讲,读写分离有两种实现方式。第一种是依靠中间件(比如:MyCat),也就是说应用程序...

java poi读取excel文件(xlsx)

poi读取excel文件先maven导入jar包<dependency>     <groupId>org.apache.poi</groupId>  ...

JAVA POI读取EXCEL 最简洁写法

package com.sunland.poi;   import java.io.FileInputStream; import java.io.IOException;  ...

nohup 命令详解 >nohup.out 2>&1 &

nohup 命令详解 >nohup.out 2>&1 &

1.nohup用途:不挂断地运行命令。语法:nohup Command [ Arg … ] [ & ]  无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。  如果当前目录的 no...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。