动态代理(JDK动态代理)(CGLIB动态代理)

一.引入

  • 程序为什么需要代理?
    对象 如果嫌身上的事太多的话,可以通过代理来转移部分职责

  • 生活中都有什么代理呢?

      最明显的就是明星和经纪人的例子:
      一个明星只需要会唱歌/跳舞就好,不需要自己再去谈合作/租场地等
      而谈合作/租场地这些工作都由经纪人来完成
      这就是一个简单的代理
    
  • 什么是代理?
    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理.
    动态代理原理详解

  • 两种代理方式
    (1) JDK 原生动态代理
    (2) CGLIB 动态代理

二.JDK动态代理

  • JDK动态代理的4个条件
    目标接口,目标类,代理类,代理类里的拦截器
  • 动态代理接口:InvocationHandler
  • 动态代理类:Proxy

目标接口

//目标接口
public interface UserService {
...
	 //一个简单的添加用户的接口
	Boolean add(User user);
...
}

目标类

//目标类UserServiceImpl 继承了目标接口UserService 
public class UserServiceImpl implements UserService{
...
	//实现目标接口的方法
  	@Override
    public Boolean add(User user) {
       	...
        return null;
    }
...
}

代理类 and 代理类里的拦截器

//代理类
public class JDKProxy implements InvocationHandler {
...
	//该方法的作用是 返回创建一个代理对象
	public Object createProxyObject(Object targetObject){...}
...
	//该方法就是代理类中的	拦截器invoke
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) {...}
...
}

动态代理—>你让我办事可以,我可能把你的事变成我的事

代码案例

  • UserService 目标接口

package com.neuedu.service;

import com.neuedu.entity.User;

//目标接口
public interface UserService {
    //一个简单的添加用户的接口
    Boolean add(User user);
    Boolean select();
}

  • UserServiceImpl 目标类(实现目标接口的方法)
package com.neuedu.service;

import com.neuedu.entity.User;
import org.springframework.stereotype.Service;

//目标类---->一定要实现目标接口(规定)
//一个类通过关键字implements声明自己使用一个或者多个接口

/**
 * 这个add()方法写好就不动了--->作为目标类--->我们想要通过一个代理类,实现 调用add()方法时可以执行其他内容
 * 就是不动这个代码,但是可以让它实现其他内容
 */

@Service
public class UserServiceImpl implements UserService{

    @Override
    public Boolean add(User user) {
        System.out.println("添加用户方法");
        return null;
    }

    @Override
    public Boolean select() {
        System.out.println("查询方法");
        return null;
    }

}

  • JDKProxy 代理类+代理类中的拦截器(invoke方法)
package com.neuedu.proxy;

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

/**
 * 代理类---->必须实现InvocationHandler这个接口
 * 注意导包要导java.lang.reflect这个包的InvocationHandler接口
 */
public class JDKProxy implements InvocationHandler {

//    获取要代理的对象(目标对象)---->命名为了targetObject
    private Object targetObject;
//    写一个方法--->通过目标对象获取一个代理对象
    public Object createProxyObject(Object targetObject){
        this.targetObject=targetObject;
/**
 * newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
 * ClassLoader loader---->参数1:用于指定一个类加载器
 * Class<?>[] interfaces---->参数2:指定生成的代理长什么样子,也就是有哪些方法(因为方法不止一个,所以是数组类型的)
 * InvocationHandler h---->参数3:用来指定生成的代理对象要干什么事情
 */

//返回了一个代理对象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),
                this);
    }


//    invoke就是代理类中的拦截器

    /**
     * Object proxy---->代理对象
     * Method method---->目标方法
     * Object[] args---->目标方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("正在执行invoke");
        System.out.println("代理类执行的目标方法:"+method.getName());
        /**
         * invoke(Object obj, Object... args)
         * Object obj---->目标对象
         * Object... args---->就是上面的Object[] args
         */
//        执行目标类的方法
        method.invoke(this.targetObject,args);

        return null;
    }
}

三.CGLIB动态代理

CGLIB动态代理不需要再添加Maven依赖了—>因为spring-core中自带

  • CGLIB动态代理

      1.用CGLIB生成的 动态代理类是目标类的子类 
      2.用CGLIB生成的 代理类不需要接口
      3.用CGLIB生成的 代理类重写了父类的各个方法
      4.拦截器中的intercept方法内容正好就是代理类中的方法体
    
  • 目标类

      package com.neuedu.service;
    
      import org.springframework.stereotype.Service;
      
      /**
       * CGLIB动态代理--->目标类
       */
      
      //把目标类放入到spring容器中--->这样就不用new了
      @Service
      public class DepartService {
    
      /**
       * 一个简单的 事务提交 的模拟
       * 事务-->需要多个表同时进行 增,删,改
       */
      public void save(){
          /**
           * 自己造一个异常--->1/0是一个bug-->因为除0是不允许的
           * 造异常是为了完善代码--->在代理类的拦截方法(intercept)中-->增加异常抛出的代码
           */
          int a=1/0;
    
          System.out.println("保存部门信息");
      }
    
      public void update(){
          System.out.println("更新部门信息");
      }
    
      public void select(){
          System.out.println("数据查询");
      }
    
      }
    
  • 代理类—>必须实现MethodInterceptor接口

      public class CGLIBProxy implements MethodInterceptor {
      	//目标对象
      	private Object targetObject;
      	//创建代理对象
      	public Object createProxy(){
              //字节码增强技术--->在反射时提高效率
              Enhancer enhancer = new Enhancer();
              //设置代理类的父类是目标类
              enhancer.setSuperclass(targetObject.getClass());
              //设置回调
              enhancer.setCallback(this);
              return enhancer.create();
         }
         ...
         //实现接口方法
         //等同于invoke方法-->拦截器
         @Override
      	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy){
      		...
      	}
      }
    
  • 测试

      @Autowired
      private CGLIBProxy cglibProxy;
      @Autowired
      private DepartService departService;
    
      @Test
      public void testCGLIBProxy(){
          cglibProxy.setTargetObject(departService);
          //ds是一个代理对象
          DepartService ds= (DepartService) cglibProxy.createProxy();
          ds.save();
          ds.update();
          ds.select();
      }
    

代码实例

package com.neuedu.proxy;

import lombok.Setter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * CGLIB动态代理---->代理类
 * CGLIB代理类要实现MethodInterceptor接口
 * 注意 : MethodInterceptor接口-->导包-->org.springframework.cglib.proxy.MethodInterceptor
 */

//把代理类放入到spring容器中--->这样就不用new了
@Component
public class CGLIBProxy implements MethodInterceptor {

    //目标对象
    @Setter
    private Object targetObject;

    //创建代理对象
    public Object createProxy(){
        //字节码增强技术--->在反射时提高效率
        Enhancer enhancer = new Enhancer();
        //设置代理类的父类是目标类
        enhancer.setSuperclass(targetObject.getClass());
        //设置回调
        enhancer.setCallback(this);
        return enhancer.create();

    }

    //实现接口方法
    /**
     * 等同于invoke方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ret=null;
        if(method.getName().equals("save")||method.getName().equals("update")){
            System.out.println("开启事务...");
            try{
                ret=method.invoke(this.targetObject,objects);
                System.out.println("提交事务");
            }catch (Exception e){
                System.out.println("事务回滚");
            }finally {
                System.out.println("始终执行");
            }
        }else {
            ret=method.invoke(this.targetObject,objects);
        }
        return ret;
    }
}

在这里插入图片描述

final类
final类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值