什么是代理?
代理是一种设计模式,提供了对目标对象(委托对象)另外的访问方式,即通过代理对象来访问目标对象(委托对象)。这样做可以在目标对象(委托对象)实现的基础上,增加额外的功能操作,扩展目标对象(委托对象)的功能。
好处就是,可以不破坏目标对象(委托对象)的任何代码,通过代理的方式来进行扩展。
听起来是不是很厉害的样子?下面我们一一来看看。
代理模式又分三种:
1、静态代理。2、动态代理(JDK 动态代理)。3、Cglib 代理。
静态代理
需要定义接口,目标对象(委托对象)和代理对象实现相同的接口
/**
* 接口
*/
public interface UserDao {
public void saveUser();
}
/**
* 实现接口(委托对象),目标对象(委托对象)
*/
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存一个用户-------------");
}
}
/**
* 静态代理对象
*/
public class UserProxy implements UserDao {
/**
* 需要代理的目标对象
*/
private UserDaoImpl userDaoImpl;
public UserProxy(UserDaoImpl userDaoImpl){
this.userDaoImpl = userDaoImpl;
}
@Override
public void saveUser() {
System.out.println("静态代理开始");
userDaoImpl.saveUser();
System.out.println("静态代理结束");
}
}
public class Test {
public static void main(String[] args) {
/**
* 创建目标对象
*/
UserDaoImpl userDao = new UserDaoImpl();
/**
* 代理对象
*/
UserProxy proxy = new UserProxy(userDao);
/**
* 调用代理对象的方法
*/
proxy.saveUser();
}
}
很明显,我们没有更改目标对象(委托对象)的代码,调用了它的功能,并且还能进行扩展;那么有没有缺点呢?答案是有的。
试想一个,如果我们有很多的代理类的话,那么每个代理类都需要实现这个接口,如果这个接口修改了方法,或者添加了方法,那么我们所有的目标对象(委托对象)和代理对象都需要修改,这样的体验肯定不好。
动态代理(JDK 动态代理)
代理对象不需要实现接口,代理对象的生成,是利用 JDK 的 api,动态的在内存中构建代理对象(需要我们指定创建目标对象实现的接口的类型)
* 接口
*/
public interface UserDao {
public void saveUser();
}
/**
* 实现接口,目标对象(委托对象)
*/
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存一个用户-------------");
}
}
/**
* JDK 动态代理
*/
public class JDKUserProxy implements InvocationHandler {
/**
* 被代理类的实例
*/
private Object target;
/**
* 将被代理者的实例传进动态代理类的构造函数中
*/
public JDKUserProxy(Object target){
this.target = target;
}
/**
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理开始");
/**
* 调用目标对象的方法
*/
Object obj = method.invoke(target,args);
System.out.println("JDK动态代理结束");
return obj;
}
}
public class JDKProxyTest {
public static void main(String[] args) {
/**
* 目标对象
*/
UserDao userDao = new UserDaoImpl();
/**
* 要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
*/
JDKUserProxy jdkUserProxy = new JDKUserProxy(userDao);
/**
* 给目标对象创建代理对象
* 参数1:指定当前目标对象使用类加载器,获取加载器的方法是固定的
* 参数2:目标对象实现的接口的类型,使用泛型方式确认类型
* 参数3:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
*/
UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),jdkUserProxy);
proxy.saveUser();
}
}
JDK 动态代理代理对象不需要实现接口,但是目标对象(委托对象)必须实现接口,否则不能使用动态代理。
Cglib 代理
导入 cglib 的 jar 包,然后在内存中构建一个目标的子类对象
public void saveUser(){
System.out.println("保存用户-------------");
}
}
/**
* Cglib 子类工厂代理,为目标对象构建一个子类对象
*/
public class CglibUserProxy implements MethodInterceptor {
/**
* 指定目标对象
*/
private Object target;
public CglibUserProxy(Object target){
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib 动态代理开始");
/**
* 执行目标对象的方法
*/
Object obj = method.invoke(target,objects);
System.out.println("Cglib 动态代理结束");
return obj;
}
}
public class CglibTest {
public static void main(String[] args) {
/**
* 目标对象
*/
User user = new User();
/**
* 代理对象
*/
CglibUserProxy cglibUserProxy = new CglibUserProxy(user);
User proxy = (User) cglibUserProxy.getProxyInstance();
proxy.saveUser();
}
}
目标类不能是 final 修饰的,方法不能被 static/final 修饰。
JDK 动态代理和 Cglib 动态代理的区别:
1、JDK 动态代理主要针对实现接口的类生成代理,不能正对类实现代理。
2、Cglib 动态代理主要针对类实现代理,针对指定的类生成一个子类,然后覆盖其中的方法。