spring(四)AOP前言——动态代理和静态代理

springAOP的底层就是动态代理,在学习AOP之前还是需要将动态代理和静态代理进行一定的学习

代理模式:

  • 静态代理
  • 动态代理
静态代理

可以通过创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在

静态代理的好处

1.可以使得真实角色更加纯粹 . 将一些公共的事情交给代理类

2.实现了业务的分工,可以在不改变真实角色的情况下增加额外的操作

3.公共业务发生扩展时变得更加集中和方便 .

缺点

1.类多了 , 多了代理类 , 工作量变大了 . 开发效率降低

静态代理,在代码编译时就确定了被代理的类是哪一个。

静态代理比较简单,代理类和被代理类实现了同一接口,在代理类的构造函数中定义一个被代理类的对象即可。

代码演示:

被代理类实现的接口

public interface IUserInfoDao {
    //保存的方法
    public abstract void save(UserInfo userInfo);
    //更新的方法
    public abstract void update(UserInfo userInfo);
    //查询 返回值是一个UserInfo对象
    UserInfo queryByLoginName(String uname);
    void demo();
}

被代理类

public class UserInfoDao implements IUserInfoDao {
    @Override
    public void save(UserInfo userInfo) {
        System.out.println("UserInfoDao save method invoke");
    }

    @Override
    public void update(UserInfo userInfo) {
        System.out.println("UserInfoDao update method invoke");
    }

    @Override
    public UserInfo queryByLoginName(String uname) {
        return new UserInfo(2, "root2", "123456789", "James2", false);
    }
    @Override
    public void demo(){
        System.out.println("demo");
    }
}

在上面前提下,当我们需要在实现类增加业务处理的时候(不修改源代码),创建一个实现相同接口的代理类,代理类中实现被代理类的原先功能。并增加一些功能

public class UserInfoDaoProxy implements IUserInfoDao {
    private UserInfoDao userInfoDao=new UserInfoDao();
    @Override
    public void save(UserInfo userInfo) {
        System.out.println("增加的功能");
        userInfoDao.save(userInfo);
    }

    @Override
    public void update(UserInfo userInfo) {
        System.out.println("增加的功能");
        userInfoDao.save(userInfo);
    }

    @Override
    public UserInfo queryByLoginName(String uname) {
        System.out.println("增加的功能");
        return userInfoDao.queryByLoginName(uname);
    }

    @Override
    public void demo() {
        System.out.println("s");
    }
}

那么我们在service层调用的时候,就可以调用我们写的静态代理类。

private IUserInfoDao userInfoDao=new UserInfoDaoProxy();

可以实现不改变源代码的情况下,进行业务的增强

动态代理

在这里插入图片描述

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

  • 基于接口的动态代理----JDK动态代理

  • ​ 基于类的动态代理–cglib

在java的动态代理中,有两个重要的类和接口

InvocationHandler接口:调用处理程序

其中InvocationHandler是动态代理类都必须要实现的接口,通过代理对象调用一个方法的时候,该方法就会被转发由InvocationHandler这个接口的invoke方法来调用。

Object invoke(Object proxy, 方法 method, Object[] args);

参数:

proxy 调用该方法的代理实例

method 所述方法对应于调用代理实例上的接口方法

args 包含的方法调用传递代理实例的参数值

Proxy类:代理

Proxy类,动态创建一个代理对象的类,它提供了许多方法,用的最多的是newProxyInstance()方法,得到一个动态的代理对象。每个代理实例都有一个关联的调用处理程序对象,实现了InvocationHandler接口,调用处理程序的invoke方法

public Object getProxy(){
   return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                 rent.getClass().getInterfaces(),this);
}
JDK代理

被代理类必须实现接口,没有接口的类,不能够被代理

public class ObjectJDKProxy {
    //构造方法传入的参数是真实的对象
    private Object object;
    public ObjectJDKProxy(Object object){
        this.object=object;
    }
    //获取动态代理
    public Object getProxyObj(){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
            /**
             *
             * @param proxy 代理类
             * @param method 执行的方法
             * @param args 方法的参数
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("ObjectJDKProxy代理的"+method.getName()+"方法执行。参数为:--->"+args);
                return method.invoke(object,args);
            }
        });
    }
}

调用:UserInfoService

//Service层
public class UserInfoService {
     //JDK
    private IUserInfoDao userInfoDao= (IUserInfoDao) new ObjectJDKProxy(new UserInfoDao()).getProxyObj();
    
    //注册
    public void register(UserInfo userInfo){
        UserInfo dbui = userInfoDao.queryByLoginName(userInfo.getUname());
        //如果传回来的不是null,说明存在该用户名
        if (dbui!=null){
            throw new RuntimeException("用户名已经存在");
        }
        //保存
        userInfoDao.save(userInfo);

    }
    //登录 传入的参数是接受的UserInfo 返回值是数据库返回的UserInfo
    public UserInfo login(UserInfo userInfo){
        UserInfo dbui = userInfoDao.queryByLoginName(userInfo.getUname());
        //说明用户名不存在
        if (dbui==null){
            throw new RuntimeException("用户名或密码错误(Dao返回的是null--用户名不存在)");
        }
        //dbui是数据库返回的UserInfo userInfo是登录的UserInfo
        if (!dbui.getUpass().equals(userInfo.getUpass())){
            throw new RuntimeException("用户名或密码错误(两个密码不一致)");
        }
        return dbui;
    }
    //更新
    public void update(UserInfo userInfo){
        userInfoDao.update(userInfo);
    }
    public void demo(){
        System.out.println("demo");
    }
}

测试类:

注意事项:

  • 使用JDK动态代理,在使用强制类型转换的时候,接受类型必须使用接口,不能是被代理类
  • 没有实现接口的使用JDK代理
代码测试注意事项
public class testJDK {
    public static void main(String[] args) {
        //没有实现接口的使用代理
        Object proxyObj = new ObjectJDKProxy(new UserInfoService()).getProxyObj();
        UserInfoService userInfoService= (UserInfoService) proxyObj;
        System.out.println(proxyObj.getClass().getName());
    }
}

在这里插入图片描述

报错:类型转换异常:代理类无法转换为UserInfoService类

数据类型转换

public class testJDK {
    public static void main(String[] args) {
        Object proxy=  new ObjectJDKProxy(new UserInfoDao()).getProxyObj();
        System.out.println(proxy.getClass().getName());//com.sun.proxy.$Proxy0
        //使用接口接收
        IUserInfoDao userInfoDao= (IUserInfoDao) new ObjectJDKProxy(new UserInfoDao()).getProxyObj();
        System.out.println(userInfoDao.getClass().getName());//com.sun.proxy.$Proxy0
        //使用被代理类接收
        UserInfoDao userInfoDao2= (UserInfoDao) new ObjectJDKProxy(new UserInfoDao()).getProxyObj();
        System.out.println(userInfoDao2.getClass().getName());
    }
}

在这里插入图片描述
同样也是类型转换异常

CGLib代理
public class ObjectCGLibProxy {
    private Object object;
    //目标类   真实的对象 被代理的对象
    public ObjectCGLibProxy(Object object){
        this.object=object;
    }
    public Object getObject(){
        Enhancer enhancer = new Enhancer();
        //父类的class文件
        enhancer.setSuperclass(object.getClass());
        //回调方法 代理类的方法调用的时候,就会执行
        //参数为Callback(接口)实现类  其中interface MethodInterceptor extends Callback
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            //Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy
            //Object obj 是原始类
            public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable {
                System.out.println("ObjectCGLibProxy代理的"+method.getName()+"方法执行。参数为:--->"+args);
                return  proxy.invokeSuper(obj, args);
                //return proxy.invoke(object,args);
            }
        });
        return enhancer.create();
    }
}

CGLib代理则不存在上面的注意事项。数据类型可以使用被代理类,没有实现接口的也可以被动态代理

public class testCGLib {
    public static void main(String[] args) {
        Object proxy = new ObjectCGLibProxy(new UserInfoDao()).getObject();
        System.out.println(proxy.getClass().getName());com.gx.dao.UserInfoDao$$EnhancerByCGLIB$$4ffc8a53
        UserInfoDao userInfoDao= (UserInfoDao) proxy;
        System.out.println(userInfoDao.getClass().getName());//com.gx.dao.UserInfoDao$$EnhancerByCGLIB$$4ffc8a53
        userInfoDao.save(new UserInfo());
/*        Method[] methods = userInfoDao.getClass().getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }*/
        //无接口的类
        Object proxyObj = new ObjectCGLibProxy(new UserInfoService()).getObject();
        UserInfoService userInfoService= (UserInfoService) proxyObj;
        System.out.println(userInfoService.getClass().getName());//com.gx.service.UserInfoService$$EnhancerByCGLIB$$738f0c95
        userInfoService.demo();
        //userInfoService.update(new UserInfo());
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值