代理模式:
- 静态代理
- 动态代理
- jdk动态代理
- cgLib动态代理
使用目的:解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面对对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问,直接访问会给使用者或者系统结构带来很多麻烦
每一个动态代理类中都必须实现Invocation接口,实现其中的invoke方法
JDK的动态代理必须需要目标对象实现接口,否则不能生成代理对象
UserServiceImpl implements UserService
静态代理
静态代理方式是为每个被代理的对象构造对应的代理类。在程序运行前,代理类的class文件已经存在了。但是一个代理类对应一个被代理对象。
public interface UserService {
void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加一名用户");
}
}
public class StaticProxy implements UserService{
private UserServiceImpl userServiceImpl;
public StaticProxy(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
@Override
public void addUser() {
System.out.println("运行方法前日志");
userServiceImpl.addUser();
System.out.println("运行方法后日志");
}
}
public class StaticProxyFactory {
public static StaticProxy newInstance(){
return new StaticProxy(new UserServiceImpl());
}
}
调用时
StaticProxy staticProxy = StaticProxyFactory.newInstance();
staticProxy.addUser();
这种方式是继承同一个接口,然后内部维护了一个实现类,也可以直接继承该实现类,方法实现用super调用。
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理模式的共有优点
缺点:
- 代理对象的一个接口只服务于一种类型的对象,如果要代理的类型很多,势必要为每一种类型的方法都进行代理
- 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
和装饰设计模式的核心区别:
装设设计模式是对被装饰者对象执行结果(map)的处理
而静态代理是在调用委托类对象方法执行前后做一些其他操作
动态代理
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。invoke方法采用回调的方式执行,只有在代理对象执行方法时,才会被invoke拦截
JDK动态代理
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。
实现步骤
- 创建被代理的类以及实现的接口
- 创建一个实现接口InvocationHandler的类,必须实现invoke方法
- 调用Proxy的newProxyInstance静态方法,创建一个代理类
- 通过代理对象调用目标方法
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("hello");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method :" + method.getName() + " is invoked!");
return method.invoke(target, args);
}
}
main{
Hello helloWorld = (Hello) Proxy.
newProxyInstance(JDKProxyTest.class.getClassLoader(),
new Class<?>[]{Hello.class},
new MyInvocationHandler(new HelloImpl()));
helloWorld.sayHello();
}
实现InvocationHandler接口步骤
- 定义含参构造方法,该参数为要代理的实例对象,目的是用于执行method.invoke()方法(也就是执行目标方法)
- 实现接口的invoke()方法,该方法用于对目标方法的增强处理,比如记录日志等。该方法的返回值就是代理对象执行目标方法的返回值
具体参数
-
proxy 动态dialing生成的代理对象
-
method 目标方法的实例
-
args 目标方法的参数
通过Proxy.newProxyInstance方法生成代理对象,具体参数有 -
loader目标类的类加载器
-
interfaces 目标类实现的接口
-
InvocationHandler 调用处理类的实现对象
参数解释,因为JDK动态代理本质是运行的时候动态的去生成了一个类继承了同一接口,然后本地维护了一个被代理对象,因此需要将生成的代理类加载到JVM中需要一个类加载器。最后基于回调,真正还是由内部维护的被代理对象执行方法,实现了一个InvocationHandler接口
注意点:JDK的动态代理只能代理实现了接口的类
cglib动态代理
cglib是针对类来实现代理的,原理是通过字节码对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,顺势织入横切逻辑。因为采用的是继承,所以不能对final修饰的类进行代理。
需要引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
例子
//代理类
public class UserServiceCglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib Before add");
Object result = methodProxy.invokeSuper(object, objects);
System.out.println("Cglib After add");
return result;
}
}
//代理工厂
public class ProxyCglibFactory {
public static UserService getInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new UserServiceCglibMethodInterceptor());
return (UserServiceImpl)enhancer.create();
}
}
//测试
public class TestCglibProxy {
public static void main(String[] args) {
UserService userService = ProxyCglibFactory.getInstance();
User user = new User();
user.setId(12);
user.setName("哈哈");
user.setAge(23);
userService.add(user);
}
}
代理对象的生成过程由Enhancer类实现,大致步骤如下
- 生成代理类Class的二进制字节码
- 通过Class.foName加载二进制字节码,生成Class对象
- 通过反射机制获取实例构造,并初始化代理类对象
特点
- cglib的动态代理是针对类来实现代理
- 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用
- 因为是通过集成实现,final类无法使用cglib
总结JDKProxy和CgLibProxy的区别
- jdk动态代理生成的代理类和委托类实现了相同的接口
- cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final修饰的类
- jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法
- jdk利用jdk的API,动态的再内存中构建代理对象
- cglib在内存中构建一个子类对象
- JDK动态代理生成的类为com.sun.proxy.$Proxy0
- cglib生成的类为class.cglib.委托类接口$ $ EnhancerByCGLIB$$552188b6
JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
//TODO
//JDK动态代理源码分析
参考
JAVA中的静态代理、动态代理以及CGLIB动态代理
JDK动态代理详解
Java的静态代理和动态代理
JAVA中的静态代理、动态代理以及CGLIB动态代理
深入理解[代理模式]原理与技术
Java三种代理模式:静态代理、动态代理和cglib代理
JDK动态代理-超详细源码分析