1. 代理模式
1.1 何为代理?
代理是通过代理对象访问目标对象,这样可以在目标对象的逻辑进行增强,或者称之为改变。
代理分为静态代理和动态代理两种。
1.2 为什么要代理?
原因一:你可能没有目标类的源码,也就是java文件,这样你如何修改逻辑?
原因二:若是直接在目标类上修改某方法逻辑,而项目中有多处都需要调用该类的该方法,会导致所有调用处的逻辑都发生改变,但假如我只想在某一处调用的地方添加自己的增强逻辑呢?
2. 静态代理
2.1 介绍
静态代理和动态代理都是实现代理的两种方式,区别在于:使用静态代理是需要编写代理类的,也就是最终你可以在你项目编译的文件中看到class文件,而动态代理是直接动态生成字节码文件(类加载器加载class文件到JVM中就变成了字节码文件)。
实现静态代理又包含两种方式,分别是继承和聚合。
2.2 继承
继承方式的核心思想是:使用代理类继承目标类,重写目标类的方法,并在重写的方法中调用目标类的方法,再添加代理的逻辑。
目标类:
package cn.it.show.inherit;
/**
* 目标类
*/
public class Service {
public void test(){
System.out.println("---目标类逻辑---");
}
}
代理类:
package cn.it.show.inherit;
public class ProxyService extends Service{
@Override
public void test() {
super.test();
System.out.println("---代理类逻辑---");
}
}
运行主类:
package cn.it.show;
public class TestMain {
public static void main(String[] args) {
Service Service = new ProxyService();
Service.test();
}
}
2.3 聚合
通过聚合实现代理,一般有一个前提,就是代理类和目标类都实现同一个接口。我们看下面一个代码样例。
公用接口
package cn.it.show.aggregation;
public interface Service {
/**
* 接口方法
*/
public void test();
}
目标类
package cn.it.show.aggregation;
public class TargetService implements Service{
public void test() {
System.out.println("---目标类逻辑---");
}
}
代理类
package cn.it.show.aggregation;
public class ProxyService implements Service {
// target为目标对象
private Service target;
public ProxyService(Service target) {
this.target = target;
}
public void test() {
// 调用目标对象的逻辑
target.test();
// 编写代理逻辑
System.out.println("---代理类逻辑---");
}
}
运行主类
package cn.it.aggregation;
import cn.it.show.juhe.ProxyService;
import cn.it.show.juhe.Service;
import cn.it.show.juhe.TargetService;
public class TestMain {
public static void main(String[] args) {
Service service = new ProxyService(new TargetService());
service.test();
}
}
聚合的核心思想是:代理类和目标类都实现同一个接口,创建代理对象时,通过构造方法或者set方法将目标对象聚合进去。
3. 动态代理
3.1 介绍
动态代理即动态的生成字节码文件到JVM中,并不会看到class文件。动态代理又分为JDK动态代理和Cglib动态代理,两种的区别在于:JDK动态代理都是对接口进行代理,而Cglib一般是对类进行代理。
值得一提的是,动态代理技术在开源框架中经常被使用到。例如Mybatis、还有Spring源码中也在很多地方使用了动态代理,像@Configuration注解,还有SpringCloud的Feign组件等等。
3.2 JDK动态代理代码
因为这个代理类和方法由JDK自带的,类名:Proxy
JdkService
package cn.it.show.jdk;
public interface JdkService {
/**
* 接口方法
*/
public void test();
}
MyInvocationHandler
package cn.it.show.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----代理逻辑-----");
return null;
}
}
Test类
package cn.it.show;
import cn.it.show.jdk.JdkService;
import cn.it.show.jdk.MyInvocationHandler;
import java.lang.reflect.Proxy;
public class TestMain {
public static void main(String[] args) {
// 作用:为JdkService接口动态生成一个代理类,并加载到JVM,返回一个JdkService类型的代理对象
JdkService o = (JdkService) Proxy.newProxyInstance(JdkService.class.getClassLoader(),
new Class[]{JdkService.class},
new MyInvocationHandler());
// 实际上会执行MyInvocationHandler的invoke方法
o.test();
}
}
4. CGLIB动态代理
为什么要使用CGLIB代理
特点:cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类或实现java接口,它广泛的被许多AOP的框架使用,为他们提供方法的拦截。
如果一个类并没有实现任何的接口,则无法使用上面所说的JDK动态代理,这时需要使用CGLIB代理,本质上是对原有类的继承,子类重新相应的方法。
使用cglib动态代理
-
直接引入Spring-Core核心包就可以,已经包含cglib功能
-
在spring-core中依赖的jar包中就包含了cglib
代码
真实角色
package com.it.proxy;
/**
* 真实对象:被代理的对象
*/
public class SuperStar {
/**
* 唱歌
* @param song
*/
public void sing(String song) {
System.out.println("小明唱:" + song);
}
/**
* 演戏
*/
public void act(double money) {
System.out.println("拍戏出场费:" + money);
}
}
Fans类
package com.it.proxy;
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 Fans {
public static void main(String[] args) {
System.out.println("真实对象:");
//真实对象
SuperStar s1 = new SuperStar();
//唱歌的方法不变
s1.sing("常回家看看");
//对演戏的方法进行增强
s1.act(800);
System.out.println("代理对象:");
/**
* 创建代理对象
* 参数1:被代理的类类型
* 参数2:回调函数
*/
SuperStar s2 = (SuperStar) Enhancer.create(SuperStar.class, new MethodInterceptor() {
/**
* 每个被代理的方法都会执行一次
* @param proxy 生成的代理对象
* @param method 被代理的方法对象
* @param args 方法的参数
* @param methodProxy 生成代理方法
* @return 方法运行的返回值
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//获取方法名字
String methodName = method.getName();
//判断是不是act方法,对原有的功能进行增强
if ("act".equals(methodName)) {
//获取出场费价格
double money = (double) args[0];
if (money < 1000) {
System.out.println("没有档期");
return null;
}
}
//调用原来的方法
return method.invoke(s1, args);
}
});
//调用代理对象方法
s2.sing("绿光");
s2.act(1700);
}
}