设计模式——动态代理
设计模式——动态代理
java 设计模式
简介
动态代理是相对就静态代理而言
静态代理是指:代理类通过对被代理类的引用,在代理类中重构被代理对象中需要代理的方法(在调用被代理类的引用对象相应方法的前后,添加我们需要的操作),静态代理对原有代码不会产生任务改变,不会自动生成新的字节码,只需要新建代理类就行,而动态代理会在运行时动态生成代码,取消了对被代理类的扩展限制,遵循开闭原则。
静态代理结构(代码)如下:
class Student {
public void study() {
System.out.println("I am studying");
}
}
class StudentStudyProxy {
private Student student;
public StudentStudyProxy(Student student) {
this.student = student;
}
public void study() {
before();
student.study();
after();
}
private void before() {
System.out.println("before study");
}
private void after() {
System.out.println("after study");
}
}
动态代理
是指可以在代码中通过调用的方式,为任何一个我们想要代理的类,生成代理,而不需要更改原有的代码
动态代理作为spring中极其常见的一种设计模式,可以说贯穿着整个spring 生态,spring 代表作 aop,今天就一起来学习一下,java 的动态代理
动态代理的分类
- ——基于JDK的实现,主要用到 java.lang.reflect 包里及其之包相关类
- ——基于CGLib的实现,主要用到 net.sf.cglib.proxy 包里及其之包相关类
JDK动态代理
JDK代理需要被代理类必须要先实现一个接口(前提),代理类需要实现java.lang.reflect.InvocationHandler 接口。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
被代理类必须需要先实现一个接口(前提)
public interface Study {
/**
* 学习
*/
public void study();
}
被代理类
public class Student implements Study{
/**
* 具体实现
*/
@Override
public void study() {
}
}
创建通用的代理类
//泛型T必须是被代理类所实现的接口的类型
public class ObjectProxy implements InvocationHandler {
Object object;
/**
* 通用的jdk动态代理,只需要传入需要被代理类的类类型,这个类必须实现了T
*
* @param clazz 需要被代理的类 类型
* @return
*/
public <T> T getInstance(Class<? extends T> clazz) throws IllegalAccessException, InstantiationException {
this.object = clazz.newInstance();
//获取被对象的 类 类型
//获取需要被代理对象的所有接口
Class<?>[] interfaces = clazz.getInterfaces();
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(object, args);
after();
return result;
}
private void before() {
System.out.println("before");
}
private void after() {
System.out.println("after");
}
}
代理类的使用
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Study studentProxy = new ObjectProxy().getInstance(Student.class);
studentProxy.study();
}
原理分析:
JDK动态代理采用字节码重组,重新生成对象来替代原始对象,以达到动态代理的作用。JDK动态代理生成对象的步骤如下:
1.获取被代理对象的引用,并且获取它的所有接口(通过反射获取);
2.JDK动态代理类重新生成一个新的类,在新的类里面实现被代理类实现的所有接口;
3.动态生成java代码,新加的 after 和 before业务添加到新生成的类中;
4.编译新生成的java代码,生成class文件;
5.重新将class文件加载到JVM中运行。
CGLib动态代理
CGLib需要先引入cglib.jar,通过实现方法拦截器去实现动态代理(net.sf.cglib.proxy.MethodInterceptor)
maven pom引入cglib
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
需要被代理的类
class Student {
public void study() {
System.out.println("I am studying!!");
}
}
创建被代理类
class ObjectProxy implements MethodInterceptor {
/**
* 根据传入类型,返回同样的类型,因为代理类实际上是创建的一个被代理类的子类
* @param clazz
* @param <T>
* @return
*/
public <T> T getInstance(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
private void before() {
System.out.println("before");
}
private void after() {
System.out.println("after");
}
}
代理类的使用
public static void main(String[] args) {
Student studentProxy = new ObjectProxy().getInstance(Student.class);
studentProxy.study();
}
通过上面,我们看到,cglib代理方式,代理类本身不需要实现任何接口,而是cglib新生成一个类,去继承代理类,methodProxy.invokeSuper()去调用父类中的业务逻辑,并返回结果,前后可以加上我们的业务代码。
JDK动态代理和CGLib代理对比
1.JDK动态代理新生成的类实现了被代理对象所实现的接口,CGLib代理是继承了被代理对象;
2.JDK动态代理和CGLib代理都是在运行期间生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib底层更加复杂;
3.JDK动态代理方法是通过反射机制调用的,而CGLib代理是通过FastClass机制直接调用方法,CGLib理论上执行效率更高。
Spring AOP中的动态代理
在spring aop中提供了两种动态代理方式,那如何进行选择呢?
默认选择使用规则:
1.当Bean有实现接口时,Spring就会使用JDK动态代理;
2.当Bean没有实现接口时,Spring就会选择CGLib代理;
Spring可以通过配置强制使用CGLib代理方式。