代理模式:Proxy Pattern,也叫委托模式,为其他对象提供一种代理来控制这个对象的访问,在某些情况下一个对象不适合或不能直接引用另一个对象,而代理对象可以在客户类和目标对象直接起到中介的对象
Provide a surrogate or placerholder for another object to control access to it:为其他对象提供一种代理以控制对这个对象的访问
代理模式的作用:
- 功能增强:其中目标对象实现真正的功能,但是代理对象可以对目标对象的功能做进一步的扩充
- 控制访问:在开发中存在 a 类需要调用 c 类的方法,完成某一个功能,但是 c 禁止 a 调用。这时,可以在 a 和 c 之间创建一个 b 类代理,a 类访问 b 类,b 类访问 c 类。例如:登录的时候需要进行短信验证,这个时候代理就是中国移动的子公司来完成短信的发送功能
代理模式的核心要素:代理类 = 目标类 + 额外功能 + 目标类实现相同的接口
静态代理
静态代理:是在程序运行前就已经存在代理类的字节码文件,静态代理通常是对原有业务逻辑的扩充,通过让代理类持有真实对象,在代理类的源代码中调用被代理类方法来添加我们需要的业务逻辑
缺点:
- 如果有多个类需要代理,那么就需要创建多个代理类分别代理目标对象,工作量较大,不利于维护
- 当接口的方法增加或修改的时候,很多类都需要修改。因为,目标类和代理类都实现了相同的接口
静态代理的使用步骤:
- 创建接口
- 创建目标类
- 创建代理类
- 测试 ```java // 创建接口 public interface Animal{ void eat(); }
// 创建目标类 class Cat implements Animal{ @Override public void eat() { System.out.println(“Cat eat fish!”); } }
// 创建代理类 class CatProxy implements Animal{ private Animal animal;
public CatProxy(Animal animal) {
this.animal = animal;
}
@Override
public void eat() {
// 对目标类进行增强
System.out.println("eat start!");
// 调用目标方法
animal.eat();
}
}
// 测试 public static void main(String[] args) { // 被增强的类 Cat cat = new Cat(); // 创建代理类 Animal catProxy = new CatProxy(cat); // 调用被增强的方法 catProxy.eat(); }
<a name="XxHsV"></a>
#### jdk 动态代理
jdk 动态代理的本质:在运行时动态生成接口的实现类(底层使用了反射)<br />1、Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):生成代理类的实例
- loader:提供一个类加载器,用于加载代理对象
- interfaces:代理对象需要实现的接口
- h:代理类中的方法执行时,会调用 h.invoke() 方法执行
2、InvocationHandler.invoke(Object proxy, Method method, Object[] args):每一个代理类的调用程序都必须实现 InvocationHandler 接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现 InvocationHandler 接口类的 invoke 方法来调用
- proxy:代理类的实例
- method:需要调用的某个对象真实的方法的 Method 对象
- args:传递的方法参数
```java
public class DynamicProxyDemo1 {
interface Inter1 {
void play();
}
interface Inter2 {
String talk(int i, String str);
}
public static void main(String[] args) {
// 当前线程的应用类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// 接口数组
Class<?>[] inters = new Class[]{Inter1.class, Inter2.class};
// 动态创建一个实现了inter1接口和inter2接口的实例
Object o = Proxy.newProxyInstance(loader, inters, (proxy, method, values) -> {
String name = method.getName();
System.out.println("当前执行的方法:" + name);
System.out.println("传入的参数:" + Arrays.toString(values));
System.out.println(method.getGenericReturnType().getTypeName());
if ("java.lang.String".equalsIgnoreCase(method.getGenericReturnType().getTypeName())){
return "世界";
}
return null;
});
((Inter1) o).play();
String talk = ((Inter2) o).talk(12, "世界");
System.out.println(talk);
// 打印jdk动态代理生成的对象
// org.example.structure.proxy.$Proxy0
System.out.println(o.getClass().getName());
}
}
上述程序的执行结果为:
很明显,Java 的 Proxy.newProxyInstance 可以根据指定的接口直接在运行时动态生成实例,但单纯的这一特性并没有什么大用,但是结合上面的静态代理模式,就可以在运行时自动生成静态代理类,然后使用静态代理类进行对目标类进行增强。
public class DynamicProxyDemo {
public static void main(String[] args) {
// 1.创建目标对象
Traffic car = new Car();
// 2.获取动态代理对象
Traffic proxy = (Traffic) new JdkProxy(car).getProxy();
// 3.使用代理对象执行相关方法
proxy.run();
}
interface Traffic {
void run();
}
static class Car implements Traffic {
@Override
public void run() {
System.out.println("汽车在公路上行驶!");
}
}
static class JdkProxy implements InvocationHandler {
// 目标对象
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
/**
* 用于书写额外功能
*
* @param proxy 代表代理对象
* @param method 接口方法
* @param args 原始方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void after() {
System.out.println("前置业务操作...");
}
private void before() {
System.out.println("前置业务操作...");
}
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
}
}
cglib 动态代理
cglib 动态代理,基于继承,即在运行时动态生成当前类的子类,然后对当前类进行增强(采用asm字节码技术实现)
导入 cglib 的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
简单使用:
public class CglibProxyDemo {
public static void main(String[] args) {
// 先创建目标对象
User user = new User();
// 创建增强类对象
Enhancer enhancer = new Enhancer();
// 设置需要增强的类型
enhancer.setSuperclass(User.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
/**
* 支持两种方式访问原对象的方法:
* 1.反射方式:method.invoke()
* 2.快速访问原始类的代理类:methodProxy.invoke()【推荐】
* @param o 代表Cglib生成的动态代理类
* @param method 代理类中被拦截的方法
* @param objects 拦截方法的参数
* @param methodProxy 快速访问目标类的原始方法,即代理类父类的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置方法");
// Object invoke = methodProxy.invoke(user, objects);
Object invoke = method.invoke(user, objects);
System.out.println("后置方法");
System.out.println(o.getClass().getName());
System.out.println(methodProxy.getClass().getName());
return invoke;
}
});
// 创建增强类
User o = (User) enhancer.create();
String s = o.get("世界", 2);
System.out.println(s);
}
static class User {
public String get(String s, Integer integer) {
System.out.println("目标方法执行!");
return s + integer;
}
}
}
输出:
使用Cglib实现动态代理:
public class CglibProxyDemo1 {
public static void main(String[] args) {
// 1.创建目标对象
User user = new User();
// 2.创建增强器
CglibProxy<User> cglibProxy = new CglibProxy<>(user);
// 3.获取代理对象
User proxy = cglibProxy.getProxy();
String say = proxy.say(10, "世界");
System.out.println(say);
}
static class CglibProxy<T> implements MethodInterceptor {
private final T t;
public CglibProxy(T t) {
this.t = t;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 前置增强
before(method, objects);
// 目标方法执行
Object ret = methodProxy.invoke(t, objects);
// 后置增强
after(ret, method, objects);
return ret;
}
// 获得代理对象
@SuppressWarnings("unchecked")
public T getProxy() {
// 1.创建Enhancer
Enhancer enhancer = new Enhancer();
// 2.设置目标类的类型
enhancer.setSuperclass(t.getClass());
// 3.设置增强回调
enhancer.setCallback(this);
return (T) enhancer.create();
}
private void after(Object ret, Method method, Object[] objects) {
System.out.println("方法的返回值:" + ret);
}
private void before(Method method, Object[] objects) {
System.out.println("执行的方法:" + method.getName());
System.out.println("传入的参数:" + Arrays.toString(objects));
}
}
static class User {
public String say(Integer a, String b) {
System.out.println("执行目标方法!");
return "返回值";
}
}
}
输出:
Spring 动态代理
Spring 中代理的选择原则:
- 当 Bean 有实现接口时,选用 JDK 动态代理
- 当 Bean 没有实现接口时,Spring 会选用 CGLIB 动态代理
- 强制设置使用 CGLIB 动态代理:
mybatis 动态代理
https://weread.qq.com/web/reader/8aa3282071f94a868aa8f52kd8232f00235d82c8d161fb2