代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。
这样做的好处:可以在目标对象的功能基础上,增加额外的功能,即 扩展目标对象的功能。
1、静态代理:代理对象 需要与 目标对象 实现一样的接口;
接口 IUserDao.java:
public interface IUserDao {
void save();
}
目标对象 UserDao.java:实现一个 save 方法;
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("----- 保存数据 -----");
}
}
代理对象 UserDaoProxy.java:在目标对象的 save 功能之上,增加事务的功能;
public class UserDaoProxy implements IUserDao {
// 接收保存目标对象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target = target;
}
/**
* 代理对象在目标对象的功能基础之上,增加对事物的处理功能
*/
@Override
public void save() {
System.out.println("开始事务...");
target.save(); // 执行目标对象的方法
System.out.println("提交事务...");
}
}
测试程序 Demo.java:
public class Demo {
@Test
public void test(){
// 目标对象
UserDao userDao = new UserDao();
// 代理对象:将目标对象传入代理对象
UserDaoProxy proxy = new UserDaoProxy(userDao);
proxy.save(); // 执行代理对象的方法,然后通过代理对象去执行目标对象的方法
}
}
输出结果:
静态代理总结:可以做到在不修改目标对象功能的前提下,对目标对象的功能进行扩展;
静态代理缺点:
1)、因为代理对象 需要与目标对象实现一样的接口,所如果接口和目标对象比较多的话,代理对象也会有很多;
2)、一旦接口增加方法,目标对象和代理对象都要维护,比较麻烦;
解决静态代理缺点的方法:使用代理工厂,通过一个代理工厂类,可以产生很多的代理对象。
使用代理工厂的方法叫做动态代理。
2、动态代理:
1)、代理对象,不需要实现接口;
2)、代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象(需要我们指定创建代理对象实现的接口的类型);
3)、动态代理,也叫做 JDK 代理,或者叫 接口代理;
JDK 中生成代理对象的 API:
|---- java.lang.reflect.Proxy
static Object | newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) |
参数:
loader:目标对象使用的类加载器;
interfaces:目标对象实现的接口类型;
h:事件处理器,执行目标对象的方法时会触发事件处理器,并把当前执行的方法作为事件处理器的参数传入;
接口 IUserDao.java 、目标对象 UserDao.java 和上面一样,代理工厂 ProxyFactory.java 如下:
public class ProxyFactory {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象 生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标对象使用的类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口类型
new InvocationHandler() { // 事件处理器,执行目标对象的方法时会触发事件处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务...");
// 执行目标对象的方法:method是当前执行的目标对象的方法
Object object = method.invoke(target, args);
System.out.println("提交事务...");
return object;
}
});
}
}
测试程序 Demo.java:
public class Demo {
@Test
public void test(){
// 目标对象
UserDao userDao = new UserDao();
// 给目标对象 创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(userDao).getProxyInstance();
// 执行方法
proxy.save();
}
}
动态代理总结:代理对象不需要实现接口,但是目标对象必须要实现接口,否则不能使用动态代理。
问题:如果有一个目标对象,需要扩展功能,但是该目标对象没有实现接口,那么应该怎样扩展?
可以使用 Cglib 代理。
3、Cglib 代理:
也叫做 子类代理,即在内存中构建一个子类对象 从而实现对目标对象功能的扩展。
Cglib 是一个强大的高性能的代码生成包,他可以在运行期扩展 java 类与实现 java 接口。它广泛的被许多 AOP 的框架使用,例如 spring AOP 和 dynaop,为他们提供方法的拦截(interception)。
Cglib 包的底层是通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成新的类。不鼓励直接使用 ASM,因为他要求必须对 JVM 内部接口包括 class 文件的格式和指令集都很熟悉。
cglib 子类代理:
1)、需要引入 cglib jar 包,但是 spring 高版本的核心包中已经包含了 cglib 功能,所以直接引入 spring-core-4.3.7.RELEASE.jar 接口;
2)、使用 cglib 代理需要实现 MethodInterceptor 接口;
3)、代理的类不能为 final,否则报错;
4)、目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;
目标对象 UserDao.java:
public class UserDao{
public void save() {
System.out.println("----- 保存数据 -----");
}
}
Cglib 子类代理工厂 ProxyFactory.java:
/**
* Cglib 子类代理工厂
* 使用 cglib 代理需要实现 MethodInterceptor 接口
*/
public class ProxyFactory implements MethodInterceptor {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance(){
// 1、工具类
Enhancer enhancer = new Enhancer();
// 2、设置父类
enhancer.setSuperclass(target.getClass());
// 3、设置回调函数
enhancer.setCallback(this);
// 4、创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务...");
// 执行目标对象的方法
Object object = method.invoke(target, objects);
System.out.println("提交事务...");
return object;
}
}
测试程序 Demo.java:
public class Demo {
@Test
public void test(){
// 目标对象
UserDao userDao = new UserDao();
// 给目标对象 创建代理对象:userDao 是父对象,proxy 是子对象;
UserDao proxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();
// 执行代理对象的方法
proxy.save();
}
}