动态代理

注:以下操作是AOP的内部技术,如果只要学习AOP怎样使用,以下内容不看也罢

动态代理有JDK动态代理和cglib动态代理
首先我们已经写好了两个Dao,一个是实现了接口的,而另一个没有

/*
*UserDao.java
*/
//接口
public interface UserDao {
    public void save();
}
/*
*UserDaoimpl.java
*/
//实现接口
public class UserDaoimpl implements UserDao {

    @Override
    public void save() {
        // TODO Auto-generated method stub
        System.out.println("User----save");
    }
}

/*
*StudentDao .java
*/
//不实现接口,直接定义方法
public class StudentDao {
    public void save() {
        System.out.println("save");
    }
}

接下来分别用两种代理方式来增强被代理类

JDK动态代理

废话不说,先上代码

/*
*JDKProxy .java
*/
public class JDKProxy {
    // 创建代理,返回被代理类对象
    public UserDao creatProxy(UserDao userDao) {
        UserDao proxyInstance = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(), new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // TODO Auto-generated method stub
                        System.out.println("enter proxy method!");
                        if ("save".equals(method.getName())) {
                            System.out.println("check_rights");
                            return method.invoke(userDao, args);
                        }
                        System.out.println("record");
                        return method.invoke(userDao, args);
                        //method.invoke(userDao, args);返回被代理类的代理对象
                    }
                });
        //返回代理对象
        return proxyInstance;
    }
}

方法: newProxyInstance(arg1,arg2,arg3)
arg1 是类加载器,arg2是类继承的接口(所以被代理类必须要有接口), 两者都可以反>射获取.
arg3是一个匿名内部类,继承接口InvocationHandler重写invoke()方法,
这个被代理类的所有方法在执行钱都会先调用这个invoke(),称之为“回调”,
于是乎可以在invoke里面写一些调用某个方法前后的操作, 有点像过滤器.

这里演示下效果

/*
*MainClass.java
*/
    @Test
    public void test() {
        UserDao userDao=new UserDaoimpl();
        
        JDKProxy proxy = new JDKProxy();
        UserDao userDaoProxy = proxy.creatProxy(userDao);
        userDaoProxy.save();
    }

在这里提一下:
UserDao userDao=new UserDaoimpl();
UserDao userDaoProxy = proxy.creatProxy(userDao);
这两个都是UserDao(被代理类)的实例化,但是前者“userDao”是普通的实例化对象,但是后者“userDaoProxy ”是将“userDao”传入后经过上述(invoke)操作后返回的代理对象,不是同一对象

运行:


动态代理_第1张图片
image.png
小结:
  • JDK动态代理就是将被代理对象传入后,在代理类内部增强,返回一个被代理类实例化的代理对象,然后直接操作代理对象即可。
  • 优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
  • 缺点:因为代理对象,需要实现与目标对象一样的接口,会导致代理类十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。

cglib动态代理

对了,忘记说了,为什么上一个动态代理叫JDK动态代理呢?因为那是JDK自带的动态代理(废话!)
而cglib动态代理则是第三方提供的动态代理,所以需要导入cglib的jar包
在这里我们Spring框架的Spring-Croe的jar包自带了cglib。
依然先上代码

/*
*CglibProxy.java
*/
//导入Spring自带的cglib
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

    public StudentDao createCglibProxy(StudentDao userDao) {

        // 1.创建核心类
        Enhancer enhancer = new Enhancer();
        // 2.设置父类 继承方式 创建一个子类 自动继承 StudentDao
        enhancer.setSuperclass(userDao.getClass());
        // 3.设置回调
        enhancer.setCallback(this);
        // 4.创建代理对象 就是把子类 给你
        StudentDao obj = (StudentDao) enhancer.create();
        return obj;
    };
/*
 * 第三步回调时,依然可以写匿名内部类实现接口MethodInterceptor重写intercept()方法,
 * 但是这里还有第二种写法,将这个外部类实现MethodInterceptor,
 * 由外部类来重写intercept()方法,
 * 都是一样的,JDK代理也能这样写
 * */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        if ("save".equals(method.getName())) {
            System.out.println("权限-----");
            return methodProxy.invokeSuper(o, objects);
        }

        return methodProxy.invokeSuper(o, objects);

    }
}

说明详见注释,注释写的很清楚了,不再赘述
这里讲下原理:
和JDK代理不同,cglib代理可以不用被代理类实现接口,因为它采用的是继承被代理类的方式,返回代理类(子类),因为多态,所以子类(代理类)可以调用父类(被代理类)的方法,同时还能够增强。

演示:

    @Test
    public void test2() {
        StudentDao studentDao = new StudentDao();
        
        CglibProxy cglibProxy = new CglibProxy();
        StudentDao studentProxy = cglibProxy.createCglibProxy(studentDao);
        studentProxy.save();
    }

运行:


动态代理_第2张图片
小结:

代理类的创建和使用两种代理方式是基本一样的,只是实现原理和方式不一样
但是,后者又将前者的缺点弥补了。
以上就是两种动态代理方式。

你可能感兴趣的:(动态代理)