代理模式
1. 抛出问题
在Java的开发中有分层的思想,分为Controller层,Service层还有DAO层。每个层面都有自己的工作,分别解决不同的问题,Controller层负责业务流程的控制,指挥Service层的接口来完成业务的处理,在Service层之上;Service层是一个中间层,用于实现业务的设计和逻辑实现,在DAO层之上;DAO层主要做数据持久层,一般用于对数据库的具体操作。
在三个层次中实际上Service层要处理的任务是最多的,所以Service层也是我们处理业务的核心逻辑层,那么Service层中包含了那些代码呢?
可以大致分为核心功能和额外功能两类:
- 核心功能
- 业务运算
- 对DAO层的调用
- 额外功能
- 不属于业务
- 可有可无
- 代码量很小
- 如:事务,日志,性能监控…
核心功能是Service层中必须要处理的,那么额外功能在Service层中实现是否合适呢?
- 从Service层的调度者(Controller层)的角度:应该在Service层中实现额外要实现的功能
- 从软件设计者的角度:Service不应该编写额外功能
现实生活中的举例:
假如一个人有很多房子,先要出租出去,这个人也就是房东就需要完成两个事情:1.带租客看房交钱入住,2.宣传自己出租房屋的广告。但是从房东的角度来讲,自己的核心任务就是带租客看房交钱入住,而宣传自己出租房屋的广告是要额外做的事情,所以就有了中介。中介要做的任务就是宣传房东的租房广告,也就是房东要实现的额外任务。
从上面的例子可以看出,中介代理(Proxy)了房东完成额外的任务,那么,从软件设计的角度,Service层如果有了这样的代理来完成额外的任务,Service层就可以专心处理核心业务,利于Service层的代码维护,于是我们引入了代理模式。
补充:建议大家在学习过程中不要在意静态代理还是动态代理,只要记住他们的本质都是代理模式而已。
2. 静态代理
2.1 概念
通过代理类,为原始类(目标类)增加额外的功能。好处是有利与原始类代码的维护。
2.2 名词解释
-
原始类(目标类)
指定是业务类,也就是被代理类
-
原始方法(目标方法)
原始类中的方法,就是原始方法。
-
代理类
实现附加功能的类(如事务,日志,性能监控…)
2.3 静态代理开发的核心要素
原始类的特征抽象出一个接口,实际实现由其实现类来完成。代理类需要与原始类实现相同的接口,同时需要在代理方法中调用目标方法。
代理类 = 原始类 + 额外功能 + 实现与原始类相同的接口
2.4 编码
UserService
接口:
public interface UserService {
boolean login();
void register();
}
实现类UserServiceImpl
:
public class UserServiceImpl implements UserService {
@Override
public boolean login() {
System.out.println("UserServiceImpl.login");
return false;
}
@Override
public void register() {
System.out.println("UserServiceImpl.register");
}
}
代理类UserServiceProxy
:
public class UserServiceProxy implements UserService {
private UserService userService = new UserServiceImpl();
@Override
public boolean login() {
System.out.println("----额外功能----");
return userService.login();
}
@Override
public void register() {
System.out.println("----额外功能----");
userService.register();
}
}
2.5 静态代理存在的问题
-
静态类文件多,不利于项目的管理
有多少个类需要被代理,就需要创建多少个代理类
-
代码维护性差
代理类很多,导致额外功能维护性差
3. 动态代理
动态代理基于JVM的动态字节码技术,在程序运行时,动态的去生成代理类、代理类的对象,这样就可以不需要进行硬编码编写多个类来实现代理功能。
3.1 JDK动态代理
JDK动态代理的原理和静态代理类似,都是需要与原始类实现相同的接口,这样可以约束代理类的行为。但是JDK动态代理与上面提到的静态代理不同,JDK动态代理的代理类对象是在程序运行时根据代理类动态进行创建。
所以怎么进行创建呢,创建的过程中需要什么“原料”吗?
- JVM中的类都是由类加载器来进行加载的,所以需要提供一个类加载器。
- 需要获取原始类实现的接口信息,也就是需要提供接口。
- 动态执行增强逻辑的方法。
JDK为我们提供了相关的API:java.lang.reflect
包下的Proxy类。
Proxy中有一个静态方法:
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
方法参数解析:
ClassLoader loader
:需要提供的类加载器。Class<?>[] interfaces
:原始类所实现的接口。InvocationHandler h
:是一个InvocationHandler
接口的实例,需要通过实现InvocationHandler
,从而实现增强逻辑的方法。
比较复杂的是实现InvocationHandler
接口:
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
};
方法参数说明:
Object proxy
:创建的代理类对象的引用。- 可以使用反射获取代理对象的信息(也就是
proxy.getClass().getName()
)。 - 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象。
- 可以使用反射获取代理对象的信息(也就是
Method method
:原始方法,可以调用Method中的invoke()
方法来执行。Object[] args
:原始方法的参数。- 返回值:是原始方法的返回值。
代码:
在invoke方法中我们可以实现很多额外的功能。
public class TestProxy {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---前置处理---");
Object ret = method.invoke(userService, args);
System.out.println("---后置处理---");
return proxy;
}
};
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
boolean ret = userServiceProxy.login("Tom", "123456");
System.out.println("ret = " + ret);
userServiceProxy.register(new User("Jerry", "123123"));
}
}
3.2 CGlib动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法⼀致,同时在代理类中提供新的实现(额外功能+原始方法)
CGlib实现动态代理的相关API在org.springframework.cglib.proxy
包下,需要使用包中的Enhancer类。
代码:
package com.jc.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TestCGlib {
public static void main(String[] args) {
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCGlib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---CGlib 前置处理---");
Object ret = method.invoke(userService, objects);
System.out.println("---CGlib 后置处理---");
return ret;
}
};
enhancer.setCallback(methodInterceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("Tom", "123456");
userServiceProxy.register(new User("Jerry", "123123"));
}
}
参数解释:
Object o
:代理类对象。Method method
:原始方法对象。Object[] objects
:原始方法中的参数。MethodProxy methodProxy
:代理方法对象。
3.3 动态代理总结
- JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类(JDK自带API)
- Cglib动态代理 Enhancer 通过继承原始类创建的代理类 (Spring提供的API)